KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > Ostermiller > util > Base64


1 /*
2  * Base64 encoding and decoding.
3  * Copyright (C) 2001-2004 Stephen Ostermiller
4  * http://ostermiller.org/contact.pl?regarding=Java+Utilities
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * See COPYING.TXT for details.
17  */

18 package com.Ostermiller.util;
19
20 import java.io.*;
21 import gnu.getopt.*;
22 import java.text.MessageFormat JavaDoc;
23 import java.util.ResourceBundle JavaDoc;
24 import java.util.Locale JavaDoc;
25
26 /**
27  * Implements Base64 encoding and decoding as defined by RFC 2045: "Multipurpose Internet
28  * Mail Extensions (MIME) Part One: Format of Internet Message Bodies" page 23.
29  * More information about this class is available from <a target="_top" HREF=
30  * "http://ostermiller.org/utils/Base64.html">ostermiller.org</a>.
31  *
32  * <blockquote>
33  * <p>The Base64 Content-Transfer-Encoding is designed to represent
34  * arbitrary sequences of octets in a form that need not be humanly
35  * readable. The encoding and decoding algorithms are simple, but the
36  * encoded data are consistently only about 33 percent larger than the
37  * unencoded data. This encoding is virtually identical to the one used
38  * in Privacy Enhanced Mail (PEM) applications, as defined in RFC 1421.</p>
39  *
40  * <p>A 65-character subset of US-ASCII is used, enabling 6 bits to be
41  * represented per printable character. (The extra 65th character, "=",
42  * is used to signify a special processing function.)</p>
43  *
44  * <p>NOTE: This subset has the important property that it is represented
45  * identically in all versions of ISO 646, including US-ASCII, and all
46  * characters in the subset are also represented identically in all
47  * versions of EBCDIC. Other popular encodings, such as the encoding
48  * used by the uuencode utility, Macintosh binhex 4.0 [RFC-1741], and
49  * the base85 encoding specified as part of Level 2 PostScript, do no
50  * share these properties, and thus do not fulfill the portability
51  * requirements a binary transport encoding for mail must meet.</p>
52  *
53  * <p>The encoding process represents 24-bit groups of input bits as output
54  * strings of 4 encoded characters. Proceeding from left to right, a
55  * 24-bit input group is formed by concatenating 3 8bit input groups.
56  * These 24 bits are then treated as 4 concatenated 6-bit groups, each
57  * of which is translated into a single digit in the base64 alphabet.
58  * When encoding a bit stream via the base64 encoding, the bit stream
59  * must be presumed to be ordered with the most-significant-bit first.
60  * That is, the first bit in the stream will be the high-order bit in
61  * the first 8bit byte, and the eighth bit will be the low-order bit in
62  * the first 8bit byte, and so on.</p>
63  *
64  * <p>Each 6-bit group is used as an index into an array of 64 printable
65  * characters. The character referenced by the index is placed in the
66  * output string. These characters, identified in Table 1, below, are
67  * selected so as to be universally representable, and the set excludes
68  * characters with particular significance to SMTP (e.g., ".", CR, LF)
69  * and to the multipart boundary delimiters defined in RFC 2046 (e.g.,
70  * "-").</p>
71  * <pre>
72  * Table 1: The Base64 Alphabet
73  *
74  * Value Encoding Value Encoding Value Encoding Value Encoding
75  * 0 A 17 R 34 i 51 z
76  * 1 B 18 S 35 j 52 0
77  * 2 C 19 T 36 k 53 1
78  * 3 D 20 U 37 l 54 2
79  * 4 E 21 V 38 m 55 3
80  * 5 F 22 W 39 n 56 4
81  * 6 G 23 X 40 o 57 5
82  * 7 H 24 Y 41 p 58 6
83  * 8 I 25 Z 42 q 59 7
84  * 9 J 26 a 43 r 60 8
85  * 10 K 27 b 44 s 61 9
86  * 11 L 28 c 45 t 62 +
87  * 12 M 29 d 46 u 63 /
88  * 13 N 30 e 47 v
89  * 14 O 31 f 48 w (pad) =
90  * 15 P 32 g 49 x
91  * 16 Q 33 h 50 y
92  * </pre>
93  * <p>The encoded output stream must be represented in lines of no more
94  * than 76 characters each. All line breaks or other characters no
95  * found in Table 1 must be ignored by decoding software. In base64
96  * data, characters other than those in Table 1, line breaks, and other
97  * white space probably indicate a transmission error, about which a
98  * warning message or even a message rejection might be appropriate
99  * under some circumstances.</p>
100  *
101  * <p>Special processing is performed if fewer than 24 bits are available
102  * at the end of the data being encoded. A full encoding quantum is
103  * always completed at the end of a body. When fewer than 24 input bits
104  * are available in an input group, zero bits are added (on the right)
105  * to form an integral number of 6-bit groups. Padding at the end of
106  * the data is performed using the "=" character. Since all base64
107  * input is an integral number of octets, only the following cases can
108  * arise: (1) the final quantum of encoding input is an integral
109  * multiple of 24 bits; here, the final unit of encoded output will be
110  * an integral multiple of 4 characters with no "=" padding, (2) the
111  * final quantum of encoding input is exactly 8 bits; here, the final
112  * unit of encoded output will be two characters followed by two "="
113  * padding characters, or (3) the final quantum of encoding input is
114  * exactly 16 bits; here, the final unit of encoded output will be three
115  * characters followed by one "=" padding character.</p>
116  *
117  * <p>Because it is used only for padding at the end of the data, the
118  * occurrence of any "=" characters may be taken as evidence that the
119  * end of the data has been reached (without truncation in transit). No
120  * such assurance is possible, however, when the number of octets
121  * transmitted was a multiple of three and no "=" characters are
122  * present.</p>
123  *
124  * <p>Any characters outside of the base64 alphabet are to be ignored in
125  * base64-encoded data.</p>
126  *
127  * <p>Care must be taken to use the proper octets for line breaks if base64
128  * encoding is applied directly to text material that has not been
129  * converted to canonical form. In particular, text line breaks must be
130  * converted into CRLF sequences prior to base64 encoding. The
131  * important thing to note is that this may be done directly by the
132  * encoder rather than in a prior canonicalization step in some
133  * implementations.</p>
134  *
135  * <p>NOTE: There is no need to worry about quoting potential boundary
136  * delimiters within base64-encoded bodies within multipart entities
137  * because no hyphen characters are used in the base64 encoding.</p>
138  * </blockquote>
139  *
140  * @author Stephen Ostermiller http://ostermiller.org/contact.pl?regarding=Java+Utilities
141  * @since ostermillerutils 1.00.00
142  */

143 public class Base64 {
144
145     /**
146      * Symbol that represents the end of an input stream
147      *
148      * @since ostermillerutils 1.00.00
149      */

150     private static final int END_OF_INPUT = -1;
151
152     /**
153      * A character that is not a valid base 64 character.
154      *
155      * @since ostermillerutils 1.00.00
156      */

157     private static final int NON_BASE_64 = -1;
158
159     /**
160      * A character that is not a valid base 64 character.
161      *
162      * @since ostermillerutils 1.00.00
163      */

164     private static final int NON_BASE_64_WHITESPACE = -2;
165
166     /**
167      * A character that is not a valid base 64 character.
168      *
169      * @since ostermillerutils 1.00.00
170      */

171     private static final int NON_BASE_64_PADDING = -3;
172
173     /**
174      * This class need not be instantiated, all methods are static.
175      *
176      * @since ostermillerutils 1.00.00
177      */

178     private Base64(){
179     }
180
181     /**
182      * Table of the sixty-four characters that are used as
183      * the Base64 alphabet: [A-Za-z0-9+/]
184      *
185      * @since ostermillerutils 1.00.00
186      */

187     protected static final byte[] base64Chars = {
188         'A','B','C','D','E','F','G','H',
189         'I','J','K','L','M','N','O','P',
190         'Q','R','S','T','U','V','W','X',
191         'Y','Z','a','b','c','d','e','f',
192         'g','h','i','j','k','l','m','n',
193         'o','p','q','r','s','t','u','v',
194         'w','x','y','z','0','1','2','3',
195         '4','5','6','7','8','9','+','/',
196     };
197
198     /**
199      * Reverse lookup table for the Base64 alphabet.
200      * reversebase64Chars[byte] gives n for the nth Base64
201      * character or negative if a character is not a Base64 character.
202      *
203      * @since ostermillerutils 1.00.00
204      */

205     protected static final byte[] reverseBase64Chars = new byte[0x100];
206     static {
207         // Fill in NON_BASE_64 for all characters to start with
208
for (int i=0; i<reverseBase64Chars.length; i++){
209             reverseBase64Chars[i] = NON_BASE_64;
210         }
211         // For characters that are base64Chars, adjust
212
// the reverse lookup table.
213
for (byte i=0; i < base64Chars.length; i++){
214             reverseBase64Chars[base64Chars[i]] = i;
215         }
216         reverseBase64Chars[' '] = NON_BASE_64_WHITESPACE;
217         reverseBase64Chars['\n'] = NON_BASE_64_WHITESPACE;
218         reverseBase64Chars['\r'] = NON_BASE_64_WHITESPACE;
219         reverseBase64Chars['\t'] = NON_BASE_64_WHITESPACE;
220         reverseBase64Chars['\f'] = NON_BASE_64_WHITESPACE;
221         reverseBase64Chars['='] = NON_BASE_64_PADDING;
222     }
223
224     /**
225      * Version number of this program
226      *
227      * @since ostermillerutils 1.00.00
228      */

229     public static final String JavaDoc version = "1.2";
230
231     /**
232      * Locale specific strings displayed to the user.
233      *
234      * @since ostermillerutils 1.00.00
235      */

236     protected static ResourceBundle JavaDoc labels = ResourceBundle.getBundle("com.Ostermiller.util.Base64", Locale.getDefault());
237
238     private static final int ACTION_GUESS = 0;
239     private static final int ACTION_ENCODE = 1;
240     private static final int ACTION_DECODE = 2;
241
242     private static final int ARGUMENT_GUESS = 0;
243     private static final int ARGUMENT_STRING = 1;
244     private static final int ARGUMENT_FILE = 2;
245
246     /**
247      * Converts the line ending on files, or standard input.
248      * Run with --help argument for more information.
249      *
250      * @param args Command line arguments.
251      *
252      * @since ostermillerutils 1.00.00
253      */

254     public static void main(String JavaDoc[] args){
255         // create the command line options that we are looking for
256
LongOpt[] longopts = {
257             new LongOpt(labels.getString("help.option"), LongOpt.NO_ARGUMENT, null, 1),
258             new LongOpt(labels.getString("version.option"), LongOpt.NO_ARGUMENT, null, 2),
259             new LongOpt(labels.getString("about.option"), LongOpt.NO_ARGUMENT, null, 3),
260             new LongOpt(labels.getString("encode.option"), LongOpt.NO_ARGUMENT, null, 'e'),
261             new LongOpt(labels.getString("lines.option"), LongOpt.NO_ARGUMENT, null, 'l'),
262             new LongOpt(labels.getString("nolines.option"), LongOpt.NO_ARGUMENT, null, 6),
263             new LongOpt(labels.getString("decode.option"), LongOpt.NO_ARGUMENT, null, 'd'),
264             new LongOpt(labels.getString("decodeall.option"), LongOpt.NO_ARGUMENT, null, 'a'),
265             new LongOpt(labels.getString("decodegood.option"), LongOpt.NO_ARGUMENT, null, 5),
266             new LongOpt(labels.getString("guess.option"), LongOpt.NO_ARGUMENT, null, 'g'),
267             new LongOpt(labels.getString("ext.option"), LongOpt.OPTIONAL_ARGUMENT, null, 'x'),
268             new LongOpt(labels.getString("force.option"), LongOpt.NO_ARGUMENT, null, 'f'),
269             new LongOpt(labels.getString("quiet.option"), LongOpt.NO_ARGUMENT, null, 'q'),
270             new LongOpt(labels.getString("reallyquiet.option"), LongOpt.NO_ARGUMENT, null, 'Q'),
271             new LongOpt(labels.getString("verbose.option"), LongOpt.NO_ARGUMENT, null, 'v'),
272             new LongOpt(labels.getString("noforce.option"), LongOpt.NO_ARGUMENT, null, 4),
273             new LongOpt(labels.getString("file.option"), LongOpt.NO_ARGUMENT, null, 7),
274             new LongOpt(labels.getString("string.option"), LongOpt.NO_ARGUMENT, null, 8),
275             new LongOpt(labels.getString("newline.option"), LongOpt.NO_ARGUMENT, null, 'n'),
276             new LongOpt(labels.getString("nonewline.option"), LongOpt.NO_ARGUMENT, null, 9),
277         };
278         String JavaDoc oneLetterOptions = "eldagx::fqQvVn";
279         Getopt opts = new Getopt(labels.getString("base64"), args, oneLetterOptions, longopts);
280         int action = ACTION_GUESS;
281         String JavaDoc extension = "base64";
282         boolean force = false;
283         boolean printMessages = true;
284         boolean printErrors = true;
285         boolean forceDecode = false;
286         boolean lineBreaks = true;
287         int argumentType = ARGUMENT_GUESS;
288         boolean decodeEndLine = false;
289         int c;
290         while ((c = opts.getopt()) != -1){
291             switch(c){
292                     case 1:{
293                     // print out the help message
294
String JavaDoc[] helpFlags = new String JavaDoc[]{
295                         "--" + labels.getString("help.option"),
296                         "--" + labels.getString("version.option"),
297                         "--" + labels.getString("about.option"),
298                         "-g --" + labels.getString("guess.option"),
299                         "-e --" + labels.getString("encode.option"),
300                         "-l --" + labels.getString("lines.option"),
301                         "--" + labels.getString("nolines.option"),
302                         "-d --" + labels.getString("decode.option"),
303                         "-a --" + labels.getString("decodeall.option"),
304                         "--" + labels.getString("decodegood.option"),
305                         "-x --" + labels.getString("ext.option") + " <" + labels.getString("ext.option") + ">",
306                         "-f --" + labels.getString("force.option"),
307                         "--" + labels.getString("noforce.option"),
308                         "-v --" + labels.getString("verbose.option"),
309                         "-q --" + labels.getString("quiet.option"),
310                         "-Q --" + labels.getString("reallyquiet.option"),
311                         "--" + labels.getString("file.option"),
312                         "--" + labels.getString("string.option"),
313                         "-n --" + labels.getString("newline.option"),
314                         "--" + labels.getString("nonewline.option"),
315                     };
316                     int maxLength = 0;
317                     for (int i=0; i<helpFlags.length; i++){
318                         maxLength = Math.max(maxLength, helpFlags[i].length());
319                     }
320                     maxLength += 2;
321                     System.out.println(
322                         labels.getString("base64") + " [-" + StringHelper.replace(oneLetterOptions, ":", "") + "] <" + labels.getString("files") + ">\n" +
323                         labels.getString("purpose.message") + "\n" +
324                         " " + labels.getString("stdin.message") + "\n" +
325                         " " + StringHelper.postpad(helpFlags[0] ,maxLength, ' ') + labels.getString("help.message") + "\n" +
326                         " " + StringHelper.postpad(helpFlags[1] ,maxLength, ' ') + labels.getString("version.message") + "\n" +
327                         " " + StringHelper.postpad(helpFlags[2] ,maxLength, ' ') + labels.getString("about.message") + "\n" +
328                         " " + StringHelper.postpad(helpFlags[3] ,maxLength, ' ') + labels.getString("g.message") + " (" + labels.getString("default") + ")\n" +
329                         " " + StringHelper.postpad(helpFlags[4] ,maxLength, ' ') + labels.getString("e.message") + "\n" +
330                         " " + StringHelper.postpad(helpFlags[5] ,maxLength, ' ') + labels.getString("l.message") + " (" + labels.getString("default") + ")\n" +
331                         " " + StringHelper.postpad(helpFlags[6] ,maxLength, ' ') + labels.getString("nolines.message") + "\n" +
332                         " " + StringHelper.postpad(helpFlags[7] ,maxLength, ' ') + labels.getString("d.message") + "\n" +
333                         " " + StringHelper.postpad(helpFlags[8] ,maxLength, ' ') + labels.getString("a.message") + "\n" +
334                         " " + StringHelper.postpad(helpFlags[9] ,maxLength, ' ') + labels.getString("decodegood.message") + " (" + labels.getString("default") + ")\n" +
335                         " " + StringHelper.postpad(helpFlags[10] ,maxLength, ' ') + labels.getString("x.message") + "\n" +
336                         " " + StringHelper.postpad(helpFlags[11] ,maxLength, ' ') + labels.getString("f.message") + "\n" +
337                         " " + StringHelper.postpad(helpFlags[12] ,maxLength, ' ') + labels.getString("noforce.message") + " (" + labels.getString("default") + ")\n" +
338                         " " + StringHelper.postpad(helpFlags[13] ,maxLength, ' ') + labels.getString("v.message") + " (" + labels.getString("default") + ")\n" +
339                         " " + StringHelper.postpad(helpFlags[14] ,maxLength, ' ') + labels.getString("q.message") + "\n" +
340                         " " + StringHelper.postpad(helpFlags[15] ,maxLength, ' ') + labels.getString("Q.message") + "\n" +
341                         " " + StringHelper.postpad(helpFlags[16] ,maxLength, ' ') + labels.getString("file.message") + "\n" +
342                         " " + StringHelper.postpad(helpFlags[17] ,maxLength, ' ') + labels.getString("string.message") + "\n" +
343                         " " + StringHelper.postpad(helpFlags[18] ,maxLength, ' ') + labels.getString("newline.message") + "\n" +
344                         " " + StringHelper.postpad(helpFlags[19] ,maxLength, ' ') + labels.getString("nonewline.message") + "\n"
345                     );
346                     System.exit(0);
347                 } break;
348                 case 2:{
349                     // print out the version message
350
System.out.println(MessageFormat.format(labels.getString("version"), (Object JavaDoc[])new String JavaDoc[] {version}));
351                     System.exit(0);
352                 } break;
353                 case 3:{
354                     System.out.println(
355                         labels.getString("base64") + " -- " + labels.getString("purpose.message") + "\n" +
356                         MessageFormat.format(labels.getString("copyright"), (Object JavaDoc[])new String JavaDoc[] {"2001-2002", "Stephen Ostermiller (http://ostermiller.org/contact.pl?regarding=Java+Utilities)"}) + "\n\n" +
357                         labels.getString("license")
358                     );
359                     System.exit(0);
360                 } break;
361                 case 'd':{
362                     action = ACTION_DECODE;
363                 } break;
364                 case 'a':{
365                     forceDecode = true;
366                 } break;
367                 case 5:{
368                     forceDecode = false;
369                 } break;
370                 case 'e':{
371                     action = ACTION_ENCODE;
372                 } break;
373                 case 'l':{
374                     lineBreaks = true;
375                 } break;
376                 case 6:{
377                     lineBreaks = false;
378                 } break;
379                 case 'g':{
380                     action = ACTION_GUESS;
381                 } break;
382                 case 'x':{
383                     extension = opts.getOptarg();
384                     if (extension == null) extension = "";
385                 } break;
386                 case 'f':{
387                     force = true;
388                 } break;
389                 case 4:{
390                     force = false;
391                 } break;
392                 case 'v':{
393                     printMessages = true;
394                     printErrors = true;
395                 } break;
396                 case 'q':{
397                     printMessages = false;
398                     printErrors = true;
399                 } break;
400                 case 'Q':{
401                     printMessages = false;
402                     printErrors = false;
403                 } break;
404                 case 7: {
405                     argumentType = ARGUMENT_FILE;
406                 } break;
407                 case 8: {
408                     argumentType = ARGUMENT_STRING;
409                 } break;
410                 case 'n': {
411                     decodeEndLine = true;
412                 } break;
413                 case 9: {
414                     decodeEndLine = false;
415                 } break;
416                 default:{
417                     System.err.println(labels.getString("unknownarg"));
418                     System.exit(1);
419                 }
420             }
421         }
422
423         int exitCond = 0;
424         boolean done = false;
425         for (int i=opts.getOptind(); i<args.length; i++){
426             done = true;
427             File source = new File(args[i]);
428             if (argumentType == ARGUMENT_STRING || (argumentType == ARGUMENT_GUESS && !source.exists())){
429                 try {
430                     int fileAction = action;
431                     if (fileAction == ACTION_GUESS){
432                         if (isBase64(args[i])){
433                             fileAction = ACTION_DECODE;
434                         } else {
435                             fileAction = ACTION_ENCODE;
436                         }
437                     }
438                     if (fileAction == ACTION_ENCODE){
439                         if (printMessages){
440                             System.out.println(labels.getString("encodingarg"));
441                         }
442                         encode(new ByteArrayInputStream(args[i].getBytes()), System.out, lineBreaks);
443                     } else {
444                         if (printMessages){
445                             System.out.println(labels.getString("decodingarg"));
446                         }
447                         decode(new ByteArrayInputStream(args[i].getBytes()), System.out, !forceDecode);
448                         if (decodeEndLine) System.out.println();
449                     }
450                 } catch (Base64DecodingException x){
451                     if(printErrors){
452                         System.err.println(args[i] + ": " + x.getMessage() + " " + labels.getString("unexpectedcharforce"));
453                     }
454                     exitCond = 1;
455                 } catch (IOException x){
456                     if(printErrors){
457                         System.err.println(args[i] + ": " + x.getMessage());
458                     }
459                     exitCond = 1;
460                 }
461             } else if (!source.exists()){
462                 if(printErrors){
463                     System.err.println(MessageFormat.format(labels.getString("doesnotexist"), (Object JavaDoc[])new String JavaDoc[] {args[i]}));
464                 }
465                 exitCond = 1;
466             } else if (!source.canRead()){
467                 if(printErrors){
468                     System.err.println(MessageFormat.format(labels.getString("cantread"), (Object JavaDoc[])new String JavaDoc[] {args[i]}));
469                 }
470                 exitCond = 1;
471             } else {
472                 try {
473                     int fileAction = action;
474                     if (fileAction == ACTION_GUESS){
475                         if (isBase64(source)){
476                             fileAction = ACTION_DECODE;
477                         } else {
478                             fileAction = ACTION_ENCODE;
479                         }
480                     }
481                     String JavaDoc outName = args[i];
482                     if (extension.length() > 0){
483                         if (fileAction == ACTION_ENCODE){
484                             outName = args[i] + "." + extension;
485                         } else {
486                             if (args[i].endsWith("." + extension)){
487                                 outName = args[i].substring(0, args[i].length() - (extension.length() + 1));
488                             }
489                         }
490                     }
491                     File outFile = new File(outName);
492                     if (!force && outFile.exists()){
493                         if(printErrors){
494                             System.err.println(MessageFormat.format(labels.getString("overwrite"), (Object JavaDoc[])new String JavaDoc[] {outName}));
495                         }
496                         exitCond = 1;
497                     } else if (!(outFile.exists() || outFile.createNewFile()) || !outFile.canWrite()){
498                         if(printErrors){
499                             System.err.println(MessageFormat.format(labels.getString("cantwrite"), (Object JavaDoc[])new String JavaDoc[] {outName}));
500                         }
501                         exitCond = 1;
502                     } else {
503                         if (fileAction == ACTION_ENCODE){
504                             if (printMessages){
505                                 System.out.println(MessageFormat.format(labels.getString("encoding"), (Object JavaDoc[])new String JavaDoc[] {args[i], outName}));
506                             }
507                             encode(source, outFile, lineBreaks);
508                         } else {
509                             if (printMessages){
510                                 System.out.println(MessageFormat.format(labels.getString("decoding"), (Object JavaDoc[])new String JavaDoc[] {args[i], outName}));
511                             }
512                             decode(source, outFile, !forceDecode);
513                         }
514                     }
515                 } catch (Base64DecodingException x){
516                     if(printErrors){
517                         System.err.println(args[i] + ": " + x.getMessage() + " " + labels.getString("unexpectedcharforce"));
518                     }
519                     exitCond = 1;
520                 } catch (IOException x){
521                     if(printErrors){
522                         System.err.println(args[i] + ": " + x.getMessage());
523                     }
524                     exitCond = 1;
525                 }
526             }
527         }
528         if (!done){
529             try {
530                 if (action == ACTION_GUESS){
531                     if(printErrors){
532                         System.err.println(labels.getString("cantguess"));
533                     }
534                     exitCond = 1;
535                 } else if (action == ACTION_ENCODE){
536                     encode(
537                         new BufferedInputStream(System.in),
538                         new BufferedOutputStream(System.out),
539                         lineBreaks
540                     );
541                 } else {
542                     decode(
543                         new BufferedInputStream(System.in),
544                         new BufferedOutputStream(System.out),
545                         !forceDecode
546                     );
547                     if (decodeEndLine) System.out.println();
548                 }
549             } catch (Base64DecodingException x){
550                 if(printErrors){
551                     System.err.println(x.getMessage() + " " + labels.getString("unexpectedcharforce"));
552                 }
553                 exitCond = 1;
554             } catch (IOException x){
555                 if(printErrors){
556                     System.err.println(x.getMessage());
557                 }
558                 exitCond = 1;
559             }
560         }
561         System.exit(exitCond);
562     }
563
564     /**
565      * Encode a String in Base64.
566      * The String is converted to and from bytes according to the platform's
567      * default character encoding.
568      * No line breaks or other white space are inserted into the encoded data.
569      *
570      * @param string The data to encode.
571      * @return An encoded String.
572      *
573      * @since ostermillerutils 1.00.00
574      */

575     public static String JavaDoc encode(String JavaDoc string){
576         return new String JavaDoc(encode(string.getBytes()));
577     }
578
579     /**
580      * Encode a String in Base64.
581      * No line breaks or other white space are inserted into the encoded data.
582      *
583      * @param string The data to encode.
584      * @param enc Character encoding to use when converting to and from bytes.
585      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
586      * @return An encoded String.
587      *
588      * @since ostermillerutils 1.00.00
589      */

590     public static String JavaDoc encode(String JavaDoc string, String JavaDoc enc) throws UnsupportedEncodingException {
591         return new String JavaDoc(encode(string.getBytes(enc)), enc);
592     }
593
594     /**
595      * Encode bytes in Base64.
596      * No line breaks or other white space are inserted into the encoded data.
597      *
598      * @param bytes The data to encode.
599      * @return String with Base64 encoded data.
600      *
601      * @since ostermillerutils 1.04.00
602      */

603     public static String JavaDoc encodeToString(byte[] bytes){
604         return encodeToString(bytes, false);
605     }
606
607     /**
608      * Encode bytes in Base64.
609      *
610      * @param bytes The data to encode.
611      * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
612      * @return String with Base64 encoded data.
613      *
614      * @since ostermillerutils 1.04.00
615      */

616     public static String JavaDoc encodeToString(byte[] bytes, boolean lineBreaks){
617         try {
618             return new String JavaDoc(encode(bytes, lineBreaks), "ASCII");
619         } catch (UnsupportedEncodingException iex){
620             // ASCII should be supported
621
throw new RuntimeException JavaDoc(iex);
622         }
623     }
624
625     /**
626      * Encode bytes in Base64.
627      * No line breaks or other white space are inserted into the encoded data.
628      *
629      * @param bytes The data to encode.
630      * @return Encoded bytes.
631      *
632      * @since ostermillerutils 1.00.00
633      */

634     public static byte[] encode(byte[] bytes){
635         return encode(bytes, false);
636     }
637
638     /**
639      * Encode bytes in Base64.
640      *
641      * @param bytes The data to encode.
642      * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
643      * @return Encoded bytes.
644      *
645      * @since ostermillerutils 1.04.00
646      */

647     public static byte[] encode(byte[] bytes, boolean lineBreaks){
648         ByteArrayInputStream in = new ByteArrayInputStream(bytes);
649         // calculate the length of the resulting output.
650
// in general it will be 4/3 the size of the input
651
// but the input length must be divisible by three.
652
// If it isn't the next largest size that is divisible
653
// by three is used.
654
int mod;
655         int length = bytes.length;
656         if ((mod = length % 3) != 0){
657             length += 3 - mod;
658         }
659         length = length * 4 / 3;
660         ByteArrayOutputStream out = new ByteArrayOutputStream(length);
661         try {
662             encode(in, out, lineBreaks);
663         } catch (IOException x){
664             // This can't happen.
665
// The input and output streams were constructed
666
// on memory structures that don't actually use IO.
667
throw new RuntimeException JavaDoc(x);
668         }
669         return out.toByteArray();
670     }
671
672     /**
673      * Encode this file in Base64.
674      * Line breaks will be inserted every 76 characters.
675      *
676      * @param fIn File to be encoded (will be overwritten).
677      *
678      * @since ostermillerutils 1.00.00
679      */

680     public static void encode(File fIn) throws IOException {
681         encode(fIn, fIn, true);
682     }
683
684     /**
685      * Encode this file in Base64.
686      *
687      * @param fIn File to be encoded (will be overwritten).
688      * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
689      * @throws IOException if an input or output error occurs.
690      *
691      * @since ostermillerutils 1.00.00
692      */

693     public static void encode(File fIn, boolean lineBreaks) throws IOException {
694         encode(fIn, fIn, lineBreaks);
695     }
696
697     /**
698      * Encode this file in Base64.
699      * Line breaks will be inserted every 76 characters.
700      *
701      * @param fIn File to be encoded.
702      * @param fOut File to which the results should be written (may be the same as fIn).
703      * @throws IOException if an input or output error occurs.
704      *
705      * @since ostermillerutils 1.00.00
706      */

707     public static void encode(File fIn, File fOut) throws IOException {
708         encode(fIn, fOut, true);
709     }
710
711     /**
712      * Encode this file in Base64.
713      *
714      * @param fIn File to be encoded.
715      * @param fOut File to which the results should be written (may be the same as fIn).
716      * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
717      * @throws IOException if an input or output error occurs.
718      *
719      * @since ostermillerutils 1.00.00
720      */

721     public static void encode(File fIn, File fOut, boolean lineBreaks) throws IOException {
722         File temp = null;
723         InputStream in = null;
724         OutputStream out = null;
725         try {
726             in = new BufferedInputStream(new FileInputStream(fIn));
727             temp = File.createTempFile("Base64", null, null);
728             out = new BufferedOutputStream(new FileOutputStream(temp));
729             encode(in, out, lineBreaks);
730             in.close();
731             in = null;
732             out.flush();
733             out.close();
734             out = null;
735             FileHelper.move(temp, fOut, true);
736         } finally {
737             if (in != null){
738                 in.close();
739                 in = null;
740             }
741             if (out != null){
742                 out.flush();
743                 out.close();
744                 out = null;
745             }
746         }
747     }
748
749     /**
750      * Encode data from the InputStream to the OutputStream in Base64.
751      * Line breaks are inserted every 76 characters in the output.
752      *
753      * @param in Stream from which to read data that needs to be encoded.
754      * @param out Stream to which to write encoded data.
755      * @throws IOException if there is a problem reading or writing.
756      *
757      * @since ostermillerutils 1.00.00
758      */

759     public static void encode(InputStream in, OutputStream out) throws IOException {
760         encode(in, out, true);
761     }
762
763     /**
764      * Encode data from the InputStream to the OutputStream in Base64.
765      *
766      * @param in Stream from which to read data that needs to be encoded.
767      * @param out Stream to which to write encoded data.
768      * @param lineBreaks Whether to insert line breaks every 76 characters in the output.
769      * @throws IOException if there is a problem reading or writing.
770      *
771      * @since ostermillerutils 1.00.00
772      */

773     public static void encode(InputStream in, OutputStream out, boolean lineBreaks) throws IOException {
774         // Base64 encoding converts three bytes of input to
775
// four bytes of output
776
int[] inBuffer = new int[3];
777         int lineCount = 0;
778
779         boolean done = false;
780         while (!done && (inBuffer[0] = in.read()) != END_OF_INPUT){
781             // Fill the buffer
782
inBuffer[1] = in.read();
783             inBuffer[2] = in.read();
784
785             // Calculate the out Buffer
786
// The first byte of our in buffer will always be valid
787
// but we must check to make sure the other two bytes
788
// are not END_OF_INPUT before using them.
789
// The basic idea is that the three bytes get split into
790
// four bytes along these lines:
791
// [AAAAAABB] [BBBBCCCC] [CCDDDDDD]
792
// [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD]
793
// bytes are considered to be zero when absent.
794
// the four bytes are then mapped to common ASCII symbols
795

796             // A's: first six bits of first byte
797
out.write(base64Chars[ inBuffer[0] >> 2 ]);
798             if (inBuffer[1] != END_OF_INPUT){
799                 // B's: last two bits of first byte, first four bits of second byte
800
out.write(base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]);
801                 if (inBuffer[2] != END_OF_INPUT){
802                     // C's: last four bits of second byte, first two bits of third byte
803
out.write(base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]);
804                     // D's: last six bits of third byte
805
out.write(base64Chars [inBuffer[2] & 0x3F]);
806                 } else {
807                     // C's: last four bits of second byte
808
out.write(base64Chars [((inBuffer[1] << 2) & 0x3c)]);
809                     // an equals sign for a character that is not a Base64 character
810
out.write('=');
811                     done = true;
812                 }
813             } else {
814                 // B's: last two bits of first byte
815
out.write(base64Chars [(( inBuffer[0] << 4 ) & 0x30)]);
816                 // an equal signs for characters that is not a Base64 characters
817
out.write('=');
818                 out.write('=');
819                 done = true;
820             }
821             lineCount += 4;
822             if (lineBreaks && lineCount >= 76){
823                 out.write('\n');
824                 lineCount = 0;
825             }
826         }
827         if (lineBreaks && lineCount >= 1){
828             out.write('\n');
829             lineCount = 0;
830         }
831         out.flush();
832     }
833
834     /**
835      * Decode a Base64 encoded String.
836      * Characters that are not part of the Base64 alphabet are ignored
837      * in the input.
838      * The String is converted to and from bytes according to the platform's
839      * default character encoding.
840      *
841      * @param string The data to decode.
842      * @return A decoded String.
843      *
844      * @since ostermillerutils 1.00.00
845      */

846     public static String JavaDoc decode(String JavaDoc string){
847         return new String JavaDoc(decode(string.getBytes()));
848     }
849
850     /**
851      * Decode a Base64 encoded String.
852      * Characters that are not part of the Base64 alphabet are ignored
853      * in the input.
854      *
855      * @param string The data to decode.
856      * @param enc Character encoding to use when converting to and from bytes.
857      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
858      * @return A decoded String.
859      *
860      * @since ostermillerutils 1.00.00
861      */

862     public static String JavaDoc decode(String JavaDoc string, String JavaDoc enc) throws UnsupportedEncodingException {
863         return new String JavaDoc(decode(string.getBytes(enc)), enc);
864     }
865
866     /**
867      * Decode a Base64 encoded String.
868      * Characters that are not part of the Base64 alphabet are ignored
869      * in the input.
870      *
871      * @param string The data to decode.
872      * @param encIn Character encoding to use when converting input to bytes (should not matter because Base64 data is designed to survive most character encodings)
873      * @param encOut Character encoding to use when converting decoded bytes to output.
874      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
875      * @return A decoded String.
876      *
877      * @since ostermillerutils 1.00.00
878      */

879     public static String JavaDoc decode(String JavaDoc string, String JavaDoc encIn, String JavaDoc encOut) throws UnsupportedEncodingException {
880         return new String JavaDoc(decode(string.getBytes(encIn)), encOut);
881     }
882
883     /**
884      * Decode a Base64 encoded String.
885      * Characters that are not part of the Base64 alphabet are ignored
886      * in the input.
887      * The String is converted to and from bytes according to the platform's
888      * default character encoding.
889      *
890      * @param string The data to decode.
891      * @return A decoded String.
892      *
893      * @since ostermillerutils 1.02.16
894      */

895     public static String JavaDoc decodeToString(String JavaDoc string){
896         return new String JavaDoc(decode(string.getBytes()));
897     }
898
899     /**
900      * Decode a Base64 encoded String.
901      * Characters that are not part of the Base64 alphabet are ignored
902      * in the input.
903      *
904      * @param string The data to decode.
905      * @param enc Character encoding to use when converting to and from bytes.
906      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
907      * @return A decoded String.
908      *
909      * @since ostermillerutils 1.02.16
910      */

911     public static String JavaDoc decodeToString(String JavaDoc string, String JavaDoc enc) throws UnsupportedEncodingException {
912         return new String JavaDoc(decode(string.getBytes(enc)), enc);
913     }
914
915     /**
916      * Decode a Base64 encoded String.
917      * Characters that are not part of the Base64 alphabet are ignored
918      * in the input.
919      *
920      * @param string The data to decode.
921      * @param encIn Character encoding to use when converting input to bytes (should not matter because Base64 data is designed to survive most character encodings)
922      * @param encOut Character encoding to use when converting decoded bytes to output.
923      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
924      * @return A decoded String.
925      *
926      * @since ostermillerutils 1.02.16
927      */

928     public static String JavaDoc decodeToString(String JavaDoc string, String JavaDoc encIn, String JavaDoc encOut) throws UnsupportedEncodingException {
929         return new String JavaDoc(decode(string.getBytes(encIn)), encOut);
930     }
931
932     /**
933      * Decode a Base64 encoded String to an OutputStream.
934      * Characters that are not part of the Base64 alphabet are ignored
935      * in the input.
936      * The String is converted from bytes according to the platform's
937      * default character encoding.
938      *
939      * @param string The data to decode.
940      * @param out Stream to which to write decoded data.
941      * @throws IOException if an IO error occurs.
942      *
943      * @since ostermillerutils 1.02.16
944      */

945     public static void decodeToStream(String JavaDoc string, OutputStream out) throws IOException {
946         decode(new ByteArrayInputStream(string.getBytes()), out);
947     }
948
949     /**
950      * Decode a Base64 encoded String to an OutputStream.
951      * Characters that are not part of the Base64 alphabet are ignored
952      * in the input.
953      *
954      * @param string The data to decode.
955      * @param enc Character encoding to use when converting to and from bytes.
956      * @param out Stream to which to write decoded data.
957      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
958      * @throws IOException if an IO error occurs.
959      *
960      * @since ostermillerutils 1.02.16
961      */

962     public static void decodeToStream(String JavaDoc string, String JavaDoc enc, OutputStream out) throws UnsupportedEncodingException, IOException {
963         decode(new ByteArrayInputStream(string.getBytes(enc)), out);
964     }
965
966     /**
967      * Decode a Base64 encoded String.
968      * Characters that are not part of the Base64 alphabet are ignored
969      * in the input.
970      * The String is converted from bytes according to the platform's
971      * default character encoding.
972      *
973      * @param string The data to decode.
974      * @return decoded data.
975      *
976      * @since ostermillerutils 1.02.16
977      */

978     public static byte[] decodeToBytes(String JavaDoc string){
979         return decode(string.getBytes());
980     }
981
982     /**
983      * Decode a Base64 encoded String.
984      * Characters that are not part of the Base64 alphabet are ignored
985      * in the input.
986      *
987      * @param string The data to decode.
988      * @param enc Character encoding to use when converting from bytes.
989      * @throws UnsupportedEncodingException if the character encoding specified is not supported.
990      * @return decoded data.
991      *
992      * @since ostermillerutils 1.02.16
993      */

994     public static byte[] decodeToBytes(String JavaDoc string, String JavaDoc enc) throws UnsupportedEncodingException {
995         return decode(string.getBytes(enc));
996     }
997
998     /**
999      * Decode Base64 encoded bytes.
1000     * Characters that are not part of the Base64 alphabet are ignored
1001     * in the input.
1002     * The String is converted to bytes according to the platform's
1003     * default character encoding.
1004     *
1005     * @param bytes The data to decode.
1006     * @return A decoded String.
1007     *
1008     * @since ostermillerutils 1.02.16
1009     */

1010    public static String JavaDoc decodeToString(byte[] bytes){
1011        return new String JavaDoc(decode(bytes));
1012    }
1013
1014    /**
1015     * Decode Base64 encoded bytes.
1016     * Characters that are not part of the Base64 alphabet are ignored
1017     * in the input.
1018     *
1019     * @param bytes The data to decode.
1020     * @param enc Character encoding to use when converting to and from bytes.
1021     * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1022     * @return A decoded String.
1023     *
1024     * @since ostermillerutils 1.02.16
1025     */

1026    public static String JavaDoc decodeToString(byte[] bytes, String JavaDoc enc) throws UnsupportedEncodingException {
1027        return new String JavaDoc(decode(bytes), enc);
1028    }
1029
1030    /**
1031     * Decode Base64 encoded bytes.
1032     * Characters that are not part of the Base64 alphabet are ignored
1033     * in the input.
1034     *
1035     * @param bytes The data to decode.
1036     * @return Decoded bytes.
1037     *
1038     * @since ostermillerutils 1.02.16
1039     */

1040    public static byte[] decodeToBytes(byte[] bytes){
1041        return decode(bytes);
1042    }
1043
1044    /**
1045     * Decode Base64 encoded bytes.
1046     * Characters that are not part of the Base64 alphabet are ignored
1047     * in the input.
1048     *
1049     * @param bytes The data to decode.
1050     * @return Decoded bytes.
1051     *
1052     * @since ostermillerutils 1.00.00
1053     */

1054    public static byte[] decode(byte[] bytes){
1055        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1056        // calculate the length of the resulting output.
1057
// in general it will be at most 3/4 the size of the input
1058
// but the input length must be divisible by four.
1059
// If it isn't the next largest size that is divisible
1060
// by four is used.
1061
int mod;
1062        int length = bytes.length;
1063        if ((mod = length % 4) != 0){
1064            length += 4 - mod;
1065        }
1066        length = length * 3 / 4;
1067        ByteArrayOutputStream out = new ByteArrayOutputStream(length);
1068        try {
1069            decode(in, out, false);
1070        } catch (IOException x){
1071            // This can't happen.
1072
// The input and output streams were constructed
1073
// on memory structures that don't actually use IO.
1074
throw new RuntimeException JavaDoc(x);
1075        }
1076        return out.toByteArray();
1077    }
1078
1079    /**
1080     * Decode Base64 encoded bytes to the an OutputStream.
1081     * Characters that are not part of the Base64 alphabet are ignored
1082     * in the input.
1083     *
1084     * @param bytes The data to decode.
1085     * @param out Stream to which to write decoded data.
1086     * @throws IOException if an IO error occurs.
1087     *
1088     * @since ostermillerutils 1.00.00
1089     */

1090    public static void decode(byte[] bytes, OutputStream out) throws IOException {
1091        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1092        decode(in, out, false);
1093    }
1094
1095    /**
1096     * Decode Base64 encoded bytes to the an OutputStream.
1097     * Characters that are not part of the Base64 alphabet are ignored
1098     * in the input.
1099     *
1100     * @param bytes The data to decode.
1101     * @param out Stream to which to write decoded data.
1102     * @throws IOException if an IO error occurs.
1103     *
1104     * @since ostermillerutils 1.02.16
1105     */

1106    public static void decodeToStream(byte[] bytes, OutputStream out) throws IOException {
1107        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
1108        decode(in, out, false);
1109    }
1110
1111    /**
1112     * Decode Base64 encoded data from one file to the other.
1113     * Characters in the Base64 alphabet, white space and equals sign are
1114     * expected to be in urlencoded data. The presence of other characters
1115     * could be a sign that the data is corrupted.
1116     *
1117     * @param fIn File to be decoded (will be overwritten).
1118     * @throws IOException if an IO error occurs.
1119     * @throws Base64DecodingException if unexpected data is encountered.
1120     *
1121     * @since ostermillerutils 1.00.00
1122     */

1123    public static void decode(File fIn) throws IOException {
1124        decode(fIn, fIn, true);
1125    }
1126
1127    /**
1128     * Decode Base64 encoded data from one file to the other.
1129     * Characters in the Base64 alphabet, white space and equals sign are
1130     * expected to be in urlencoded data. The presence of other characters
1131     * could be a sign that the data is corrupted.
1132     *
1133     * @param fIn File to be decoded (will be overwritten).
1134     * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1135     * @throws IOException if an IO error occurs.
1136     * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1137     *
1138     * @since ostermillerutils 1.00.00
1139     */

1140    public static void decode(File fIn, boolean throwExceptions) throws IOException {
1141        decode(fIn, fIn, throwExceptions);
1142    }
1143
1144    /**
1145     * Decode Base64 encoded data from one file to the other.
1146     * Characters in the Base64 alphabet, white space and equals sign are
1147     * expected to be in urlencoded data. The presence of other characters
1148     * could be a sign that the data is corrupted.
1149     *
1150     * @param fIn File to be decoded.
1151     * @param fOut File to which the results should be written (may be the same as fIn).
1152     * @throws IOException if an IO error occurs.
1153     * @throws Base64DecodingException if unexpected data is encountered.
1154     *
1155     * @since ostermillerutils 1.00.00
1156     */

1157    public static void decode(File fIn, File fOut) throws IOException {
1158        decode(fIn, fOut, true);
1159    }
1160
1161    /**
1162     * Decode Base64 encoded data from one file to the other.
1163     * Characters in the Base64 alphabet, white space and equals sign are
1164     * expected to be in urlencoded data. The presence of other characters
1165     * could be a sign that the data is corrupted.
1166     *
1167     * @param fIn File to be decoded.
1168     * @param fOut File to which the results should be written (may be the same as fIn).
1169     * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1170     * @throws IOException if an IO error occurs.
1171     * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1172     *
1173     * @since ostermillerutils 1.00.00
1174     */

1175    public static void decode(File fIn, File fOut, boolean throwExceptions) throws IOException {
1176        File temp = null;
1177        InputStream in = null;
1178        OutputStream out = null;
1179        try {
1180            in = new BufferedInputStream(new FileInputStream(fIn));
1181            temp = File.createTempFile("Base64", null, null);
1182            out = new BufferedOutputStream(new FileOutputStream(temp));
1183            decode(in, out, throwExceptions);
1184            in.close();
1185            in = null;
1186            out.flush();
1187            out.close();
1188            out = null;
1189            FileHelper.move(temp, fOut, true);
1190        } finally {
1191            if (in != null){
1192                try {
1193                    in.close();
1194                } catch (IOException ignore){
1195                     if (throwExceptions) throw ignore;
1196                }
1197                in = null;
1198            }
1199            if (out != null){
1200                try {
1201                    out.flush();
1202                    out.close();
1203                } catch (IOException ignore){
1204                     if (throwExceptions) throw ignore;
1205                }
1206                out = null;
1207            }
1208        }
1209    }
1210
1211    /**
1212     * Reads the next (decoded) Base64 character from the input stream.
1213     * Non Base64 characters are skipped.
1214     *
1215     * @param in Stream from which bytes are read.
1216     * @param throwExceptions Throw an exception if an unexpected character is encountered.
1217     * @return the next Base64 character from the stream or -1 if there are no more Base64 characters on the stream.
1218     * @throws IOException if an IO Error occurs.
1219     * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1220     *
1221     * @since ostermillerutils 1.00.00
1222     */

1223    private static final int readBase64(InputStream in, boolean throwExceptions) throws IOException {
1224        int read;
1225        int numPadding = 0;
1226        do {
1227            read = in.read();
1228            if (read == END_OF_INPUT) return END_OF_INPUT;
1229            read = reverseBase64Chars[(byte)read];
1230            if (throwExceptions && (read == NON_BASE_64 || (numPadding > 0 && read > NON_BASE_64))){
1231                throw new Base64DecodingException (
1232                    MessageFormat.format(
1233                        labels.getString("unexpectedchar"),
1234                        (Object JavaDoc[])new String JavaDoc[] {
1235                            "'" + (char)read + "' (0x" + Integer.toHexString(read) + ")"
1236                        }
1237                    ),
1238                    (char)read
1239                );
1240            }
1241            if (read == NON_BASE_64_PADDING){
1242                numPadding++;
1243            }
1244        } while (read <= NON_BASE_64);
1245        return read;
1246    }
1247
1248    /**
1249     * Decode Base64 encoded data from the InputStream to a byte array.
1250     * Characters that are not part of the Base64 alphabet are ignored
1251     * in the input.
1252     *
1253     * @param in Stream from which to read data that needs to be decoded.
1254     * @return decoded data.
1255     * @throws IOException if an IO error occurs.
1256     *
1257     * @since ostermillerutils 1.00.00
1258     */

1259    public static byte[] decodeToBytes(InputStream in) throws IOException {
1260        ByteArrayOutputStream out = new ByteArrayOutputStream();
1261        decode(in, out, false);
1262        return out.toByteArray();
1263    }
1264
1265    /**
1266     * Decode Base64 encoded data from the InputStream to a String.
1267     * Characters that are not part of the Base64 alphabet are ignored
1268     * in the input.
1269     * Bytes are converted to characters in the output String according to the platform's
1270     * default character encoding.
1271     *
1272     * @param in Stream from which to read data that needs to be decoded.
1273     * @return decoded data.
1274     * @throws IOException if an IO error occurs.
1275     *
1276     * @since ostermillerutils 1.02.16
1277     */

1278    public static String JavaDoc decodeToString(InputStream in) throws IOException {
1279        return new String JavaDoc(decodeToBytes(in));
1280    }
1281
1282    /**
1283     * Decode Base64 encoded data from the InputStream to a String.
1284     * Characters that are not part of the Base64 alphabet are ignored
1285     * in the input.
1286     *
1287     * @param in Stream from which to read data that needs to be decoded.
1288     * @param enc Character encoding to use when converting bytes to characters.
1289     * @return decoded data.
1290     * @throws IOException if an IO error occurs.Throws:
1291     * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1292     *
1293     * @since ostermillerutils 1.02.16
1294     */

1295    public static String JavaDoc decodeToString(InputStream in, String JavaDoc enc) throws IOException {
1296        return new String JavaDoc(decodeToBytes(in), enc);
1297    }
1298
1299    /**
1300     * Decode Base64 encoded data from the InputStream to the OutputStream.
1301     * Characters in the Base64 alphabet, white space and equals sign are
1302     * expected to be in urlencoded data. The presence of other characters
1303     * could be a sign that the data is corrupted.
1304     *
1305     * @param in Stream from which to read data that needs to be decoded.
1306     * @param out Stream to which to write decoded data.
1307     * @throws IOException if an IO error occurs.
1308     * @throws Base64DecodingException if unexpected data is encountered.
1309     *
1310     * @since ostermillerutils 1.00.00
1311     */

1312    public static void decode(InputStream in, OutputStream out) throws IOException {
1313        decode(in, out, true);
1314    }
1315
1316    /**
1317     * Decode Base64 encoded data from the InputStream to the OutputStream.
1318     * Characters in the Base64 alphabet, white space and equals sign are
1319     * expected to be in urlencoded data. The presence of other characters
1320     * could be a sign that the data is corrupted.
1321     *
1322     * @param in Stream from which to read data that needs to be decoded.
1323     * @param out Stream to which to write decoded data.
1324     * @param throwExceptions Whether to throw exceptions when unexpected data is encountered.
1325     * @throws IOException if an IO error occurs.
1326     * @throws Base64DecodingException if unexpected data is encountered when throwExceptions is specified.
1327     *
1328     * @since ostermillerutils 1.00.00
1329     */

1330    public static void decode(InputStream in, OutputStream out, boolean throwExceptions) throws IOException {
1331        // Base64 decoding converts four bytes of input to three bytes of output
1332
int[] inBuffer = new int[4];
1333
1334        // read bytes unmapping them from their ASCII encoding in the process
1335
// we must read at least two bytes to be able to output anything
1336
boolean done = false;
1337        while (!done && (inBuffer[0] = readBase64(in, throwExceptions)) != END_OF_INPUT
1338            && (inBuffer[1] = readBase64(in, throwExceptions)) != END_OF_INPUT){
1339            // Fill the buffer
1340
inBuffer[2] = readBase64(in, throwExceptions);
1341            inBuffer[3] = readBase64(in, throwExceptions);
1342
1343            // Calculate the output
1344
// The first two bytes of our in buffer will always be valid
1345
// but we must check to make sure the other two bytes
1346
// are not END_OF_INPUT before using them.
1347
// The basic idea is that the four bytes will get reconstituted
1348
// into three bytes along these lines:
1349
// [xxAAAAAA] [xxBBBBBB] [xxCCCCCC] [xxDDDDDD]
1350
// [AAAAAABB] [BBBBCCCC] [CCDDDDDD]
1351
// bytes are considered to be zero when absent.
1352

1353            // six A and two B
1354
out.write(inBuffer[0] << 2 | inBuffer[1] >> 4);
1355            if (inBuffer[2] != END_OF_INPUT){
1356                // four B and four C
1357
out.write(inBuffer[1] << 4 | inBuffer[2] >> 2);
1358                if (inBuffer[3] != END_OF_INPUT){
1359                    // two C and six D
1360
out.write(inBuffer[2] << 6 | inBuffer[3]);
1361                } else {
1362                    done = true;
1363                }
1364            } else {
1365                done = true;
1366            }
1367        }
1368        out.flush();
1369    }
1370
1371    /**
1372     * Determines if the byte array is in base64 format.
1373     * <p>
1374     * Data will be considered to be in base64 format if it contains
1375     * only base64 characters and whitespace with equals sign padding
1376     * on the end so that the number of base64 characters is divisible
1377     * by four.
1378     * <p>
1379     * It is possible for data to be in base64 format but for it to not
1380     * meet these stringent requirements. It is also possible for data
1381     * to meet these requirements even though decoding it would not make
1382     * any sense. This method should be used as a guide but it is not
1383     * authoritative because of the possibility of these false positives
1384     * and false negatives.
1385     * <p>
1386     * Additionally, extra data such as headers or footers may throw
1387     * this method off the scent and cause it to return false.
1388     *
1389     * @param bytes data that could be in base64 format.
1390     *
1391     * @since ostermillerutils 1.00.00
1392     */

1393    public static boolean isBase64(byte[] bytes){
1394        try {
1395            return isBase64(new ByteArrayInputStream(bytes));
1396        } catch (IOException x){
1397            // This can't happen.
1398
// The input and output streams were constructed
1399
// on memory structures that don't actually use IO.
1400
return false;
1401        }
1402    }
1403
1404    /**
1405     * Determines if the String is in base64 format.
1406     * The String is converted to and from bytes according to the platform's
1407     * default character encoding.
1408     * <p>
1409     * Data will be considered to be in base64 format if it contains
1410     * only base64 characters and whitespace with equals sign padding
1411     * on the end so that the number of base64 characters is divisible
1412     * by four.
1413     * <p>
1414     * It is possible for data to be in base64 format but for it to not
1415     * meet these stringent requirements. It is also possible for data
1416     * to meet these requirements even though decoding it would not make
1417     * any sense. This method should be used as a guide but it is not
1418     * authoritative because of the possibility of these false positives
1419     * and false negatives.
1420     * <p>
1421     * Additionally, extra data such as headers or footers may throw
1422     * this method off the scent and cause it to return false.
1423     *
1424     * @param string String that may be in base64 format.
1425     * @return Best guess as to whether the data is in base64 format.
1426     *
1427     * @since ostermillerutils 1.00.00
1428     */

1429    public static boolean isBase64(String JavaDoc string){
1430        return isBase64(string.getBytes());
1431    }
1432
1433    /**
1434     * Determines if the String is in base64 format.
1435     * <p>
1436     * Data will be considered to be in base64 format if it contains
1437     * only base64 characters and whitespace with equals sign padding
1438     * on the end so that the number of base64 characters is divisible
1439     * by four.
1440     * <p>
1441     * It is possible for data to be in base64 format but for it to not
1442     * meet these stringent requirements. It is also possible for data
1443     * to meet these requirements even though decoding it would not make
1444     * any sense. This method should be used as a guide but it is not
1445     * authoritative because of the possibility of these false positives
1446     * and false negatives.
1447     * <p>
1448     * Additionally, extra data such as headers or footers may throw
1449     * this method off the scent and cause it to return false.
1450     *
1451     * @param string String that may be in base64 format.
1452     * @param enc Character encoding to use when converting to bytes.
1453     * @return Best guess as to whether the data is in base64 format.
1454     * @throws UnsupportedEncodingException if the character encoding specified is not supported.
1455     */

1456    public static boolean isBase64(String JavaDoc string, String JavaDoc enc) throws UnsupportedEncodingException {
1457        return isBase64(string.getBytes(enc));
1458    }
1459
1460    /**
1461     * Determines if the File is in base64 format.
1462     * <p>
1463     * Data will be considered to be in base64 format if it contains
1464     * only base64 characters and whitespace with equals sign padding
1465     * on the end so that the number of base64 characters is divisible
1466     * by four.
1467     * <p>
1468     * It is possible for data to be in base64 format but for it to not
1469     * meet these stringent requirements. It is also possible for data
1470     * to meet these requirements even though decoding it would not make
1471     * any sense. This method should be used as a guide but it is not
1472     * authoritative because of the possibility of these false positives
1473     * and false negatives.
1474     * <p>
1475     * Additionally, extra data such as headers or footers may throw
1476     * this method off the scent and cause it to return false.
1477     *
1478     * @param fIn File that may be in base64 format.
1479     * @return Best guess as to whether the data is in base64 format.
1480     * @throws IOException if an IO error occurs.
1481     *
1482     * @since ostermillerutils 1.00.00
1483     */

1484    public static boolean isBase64(File fIn) throws IOException {
1485        return isBase64(new BufferedInputStream(new FileInputStream(fIn)));
1486    }
1487
1488    /**
1489     * Reads data from the stream and determines if it is
1490     * in base64 format.
1491     * <p>
1492     * Data will be considered to be in base64 format if it contains
1493     * only base64 characters and whitespace with equals sign padding
1494     * on the end so that the number of base64 characters is divisible
1495     * by four.
1496     * <p>
1497     * It is possible for data to be in base64 format but for it to not
1498     * meet these stringent requirements. It is also possible for data
1499     * to meet these requirements even though decoding it would not make
1500     * any sense. This method should be used as a guide but it is not
1501     * authoritative because of the possibility of these false positives
1502     * and false negatives.
1503     * <p>
1504     * Additionally, extra data such as headers or footers may throw
1505     * this method off the scent and cause it to return false.
1506     *
1507     * @param in Stream from which to read data to be tested.
1508     * @return Best guess as to whether the data is in base64 format.
1509     * @throws IOException if an IO error occurs.
1510     *
1511     * @since ostermillerutils 1.00.00
1512     */

1513    public static boolean isBase64(InputStream in) throws IOException {
1514        long numBase64Chars = 0;
1515        int numPadding = 0;
1516        int read;
1517
1518        while ((read = in.read()) != -1){
1519            read = reverseBase64Chars[read];
1520            if (read == NON_BASE_64){
1521                return false;
1522            } else if (read == NON_BASE_64_WHITESPACE){
1523            } else if (read == NON_BASE_64_PADDING){
1524                numPadding++;
1525                numBase64Chars++;
1526            } else if (numPadding > 0){
1527                return false;
1528            } else {
1529                numBase64Chars++;
1530            }
1531        }
1532        if (numBase64Chars == 0) return false;
1533        if (numBase64Chars % 4 != 0) return false;
1534        return true;
1535    }
1536}
1537
Popular Tags