1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.chemistry.opencmis.commons.impl;
20
21 /**
22 * <p>
23 * Encodes and decodes to and from Base64 notation.
24 * </p>
25 * <p>
26 * Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.
27 * </p>
28 *
29 * @author Robert Harder
30 * @author rob@iharder.net
31 * @version 2.3.7
32 */
33 public class Base64 {
34
35 /* ******** P U B L I C F I E L D S ******** */
36
37 /** No options specified. Value is zero. */
38 public static final int NO_OPTIONS = 0;
39
40 /** Specify encoding in first bit. Value is one. */
41 public static final int ENCODE = 1;
42
43 /** Specify decoding in first bit. Value is zero. */
44 public static final int DECODE = 0;
45
46 /** Specify that data should be gzip-compressed in second bit. Value is two. */
47 public static final int GZIP = 2;
48
49 /**
50 * Specify that gzipped data should <em>not</em> be automatically gunzipped.
51 */
52 public static final int DONT_GUNZIP = 4;
53
54 /** Do break lines when encoding. Value is 8. */
55 public static final int DO_BREAK_LINES = 8;
56
57 /**
58 * Encode using Base64-like encoding that is URL- and Filename-safe as
59 * described in Section 4 of RFC3548: <a
60 * href="http://www.faqs.org/rfcs/rfc3548.html"
61 * >http://www.faqs.org/rfcs/rfc3548.html</a>. It is important to note that
62 * data encoded this way is <em>not</em> officially valid Base64, or at the
63 * very least should not be called Base64 without also specifying that is
64 * was encoded using the URL- and Filename-safe dialect.
65 */
66 public static final int URL_SAFE = 16;
67
68 /**
69 * Encode using the special "ordered" dialect of Base64 described here: <a
70 * href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-
71 * 1940.html</a>.
72 */
73 public static final int ORDERED = 32;
74
75 /* ******** P R I V A T E F I E L D S ******** */
76
77 /** Maximum line length (76) of Base64 output. */
78 private static final int MAX_LINE_LENGTH = 76;
79
80 /** The equals sign (=) as a byte. */
81 private static final byte EQUALS_SIGN = (byte) '=';
82
83 /** The new line character (\n) as a byte. */
84 private static final byte NEW_LINE = (byte) '\n';
85
86 /** Preferred encoding. */
87 private static final String PREFERRED_ENCODING = "US-ASCII";
88
89 private static final byte WHITE_SPACE_ENC = -5; // Indicates white space in
90 // encoding
91 private static final byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in
92 // encoding
93
94 /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
95
96 /** The 64 valid Base64 values. */
97 /*
98 * Host platform me be something funny like EBCDIC, so we hardcode these
99 * values.
100 */
101 private static final byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
102 (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
103 (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
104 (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
105 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
106 (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
107 (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
108 (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
109
110 /**
111 * Translates a Base64 value to either its 6-bit reconstruction value or a
112 * negative number indicating some other meaning.
113 **/
114 private static final byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
115 // 0
116 // -
117 // 8
118 -5, -5, // Whitespace: Tab and Linefeed
119 -9, -9, // Decimal 11 - 12
120 -5, // Whitespace: Carriage Return
121 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
122 // 26
123 -9, -9, -9, -9, -9, // Decimal 27 - 31
124 -5, // Whitespace: Space
125 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
126 62, // Plus sign at decimal 43
127 -9, -9, -9, // Decimal 44 - 46
128 63, // Slash at decimal 47
129 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
130 -9, -9, -9, // Decimal 58 - 60
131 -1, // Equals sign at decimal 61
132 -9, -9, -9, // Decimal 62 - 64
133 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
134 // 'N'
135 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
136 // through 'Z'
137 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
138 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
139 // through 'm'
140 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
141 // through 'z'
142 -9, -9, -9, -9, -9 // Decimal 123 - 127
143 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
144 // 139
145 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
146 // 152
147 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
148 // 165
149 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
150 // 178
151 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
152 // 191
153 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
154 // 204
155 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
156 // 217
157 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
158 // 230
159 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
160 // 243
161 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
162 };
163
164 /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
165
166 /**
167 * Used in the URL- and Filename-safe dialect described in Section 4 of
168 * RFC3548: <a
169 * href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org
170 * /rfcs/rfc3548.html</a>. Notice that the last two bytes become "hyphen"
171 * and "underscore" instead of "plus" and "slash."
172 */
173 private static final byte[] _URL_SAFE_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E',
174 (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N',
175 (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W',
176 (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f',
177 (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
178 (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x',
179 (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6',
180 (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' };
181
182 /**
183 * Used in decoding URL- and Filename-safe dialects of Base64.
184 */
185 private static final byte[] _URL_SAFE_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
186 // 0
187 // -
188 // 8
189 -5, -5, // Whitespace: Tab and Linefeed
190 -9, -9, // Decimal 11 - 12
191 -5, // Whitespace: Carriage Return
192 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
193 // 26
194 -9, -9, -9, -9, -9, // Decimal 27 - 31
195 -5, // Whitespace: Space
196 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
197 -9, // Plus sign at decimal 43
198 -9, // Decimal 44
199 62, // Minus sign at decimal 45
200 -9, // Decimal 46
201 -9, // Slash at decimal 47
202 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
203 -9, -9, -9, // Decimal 58 - 60
204 -1, // Equals sign at decimal 61
205 -9, -9, -9, // Decimal 62 - 64
206 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through
207 // 'N'
208 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O'
209 // through 'Z'
210 -9, -9, -9, -9, // Decimal 91 - 94
211 63, // Underscore at decimal 95
212 -9, // Decimal 96
213 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a'
214 // through 'm'
215 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n'
216 // through 'z'
217 -9, -9, -9, -9, -9 // Decimal 123 - 127
218 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 -
219 // 139
220 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
221 // 152
222 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
223 // 165
224 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
225 // 178
226 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
227 // 191
228 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
229 // 204
230 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
231 // 217
232 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
233 // 230
234 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
235 // 243
236 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
237 };
238
239 /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */
240
241 /**
242 * I don't get the point of this technique, but someone requested it, and it
243 * is described here: <a
244 * href="http://www.faqs.org/qa/rfcc-1940.html">http://
245 * www.faqs.org/qa/rfcc-1940.html</a>.
246 */
247 private static final byte[] _ORDERED_ALPHABET = { (byte) '-', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
248 (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A', (byte) 'B', (byte) 'C',
249 (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L',
250 (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
251 (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) '_', (byte) 'a', (byte) 'b', (byte) 'c',
252 (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l',
253 (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u',
254 (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' };
255
256 /**
257 * Used in decoding the "ordered" dialect of Base64.
258 */
259 private static final byte[] _ORDERED_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal
260 // 0
261 // -
262 // 8
263 -5, -5, // Whitespace: Tab and Linefeed
264 -9, -9, // Decimal 11 - 12
265 -5, // Whitespace: Carriage Return
266 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 -
267 // 26
268 -9, -9, -9, -9, -9, // Decimal 27 - 31
269 -5, // Whitespace: Space
270 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
271 -9, // Plus sign at decimal 43
272 -9, // Decimal 44
273 0, // Minus sign at decimal 45
274 -9, // Decimal 46
275 -9, // Slash at decimal 47
276 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine
277 -9, -9, -9, // Decimal 58 - 60
278 -1, // Equals sign at decimal 61
279 -9, -9, -9, // Decimal 62 - 64
280 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A'
281 // through 'M'
282 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N'
283 // through 'Z'
284 -9, -9, -9, -9, // Decimal 91 - 94
285 37, // Underscore at decimal 95
286 -9, // Decimal 96
287 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a'
288 // through 'm'
289 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n'
290 // through 'z'
291 -9, -9, -9, -9, -9 // Decimal 123 - 127
292 , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128
293 // - 139
294 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 -
295 // 152
296 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 -
297 // 165
298 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 -
299 // 178
300 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 -
301 // 191
302 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 -
303 // 204
304 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 -
305 // 217
306 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 -
307 // 230
308 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 -
309 // 243
310 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
311 };
312
313 /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
314
315 /**
316 * Returns one of the _SOMETHING_ALPHABET byte arrays depending on the
317 * options specified. It's possible, though silly, to specify ORDERED
318 * <b>and</b> URLSAFE in which case one of them will be picked, though there
319 * is no guarantee as to which one will be picked.
320 */
321 private static final byte[] getAlphabet(int options) {
322 if ((options & URL_SAFE) == URL_SAFE) {
323 return _URL_SAFE_ALPHABET;
324 } else if ((options & ORDERED) == ORDERED) {
325 return _ORDERED_ALPHABET;
326 } else {
327 return _STANDARD_ALPHABET;
328 }
329 } // end getAlphabet
330
331 /**
332 * Returns one of the _SOMETHING_DECODABET byte arrays depending on the
333 * options specified. It's possible, though silly, to specify ORDERED and
334 * URL_SAFE in which case one of them will be picked, though there is no
335 * guarantee as to which one will be picked.
336 */
337 private static final byte[] getDecodabet(int options) {
338 if ((options & URL_SAFE) == URL_SAFE) {
339 return _URL_SAFE_DECODABET;
340 } else if ((options & ORDERED) == ORDERED) {
341 return _ORDERED_DECODABET;
342 } else {
343 return _STANDARD_DECODABET;
344 }
345 } // end getAlphabet
346
347 /** Defeats instantiation. */
348 private Base64() {
349 }
350
351 /* ******** E N C O D I N G M E T H O D S ******** */
352
353 /**
354 * Encodes up to the first three bytes of array <var>threeBytes</var> and
355 * returns a four-byte array in Base64 notation. The actual number of
356 * significant bytes in your array is given by <var>numSigBytes</var>. The
357 * array <var>threeBytes</var> needs only be as big as
358 * <var>numSigBytes</var>. Code can reuse a byte array by passing a
359 * four-byte array as <var>b4</var>.
360 *
361 * @param b4
362 * A reusable byte array to reduce array instantiation
363 * @param threeBytes
364 * the array to convert
365 * @param numSigBytes
366 * the number of significant bytes in your array
367 * @return four byte array in Base64 notation.
368 * @since 1.5.1
369 */
370 private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) {
371 encode3to4(threeBytes, 0, numSigBytes, b4, 0, options);
372 return b4;
373 } // end encode3to4
374
375 /**
376 * <p>
377 * Encodes up to three bytes of the array <var>source</var> and writes the
378 * resulting four Base64 bytes to <var>destination</var>. The source and
379 * destination arrays can be manipulated anywhere along their length by
380 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
381 * does not check to make sure your arrays are large enough to accomodate
382 * <var>srcOffset</var> + 3 for the <var>source</var> array or
383 * <var>destOffset</var> + 4 for the <var>destination</var> array. The
384 * actual number of significant bytes in your array is given by
385 * <var>numSigBytes</var>.
386 * </p>
387 * <p>
388 * This is the lowest level of the encoding methods with all possible
389 * parameters.
390 * </p>
391 *
392 * @param source
393 * the array to convert
394 * @param srcOffset
395 * the index where conversion begins
396 * @param numSigBytes
397 * the number of significant bytes in your array
398 * @param destination
399 * the array to hold the conversion
400 * @param destOffset
401 * the index where output will be put
402 * @return the <var>destination</var> array
403 * @since 1.3
404 */
405 private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset,
406 int options) {
407
408 byte[] ALPHABET = getAlphabet(options);
409
410 // 1 2 3
411 // 01234567890123456789012345678901 Bit position
412 // --------000000001111111122222222 Array position from threeBytes
413 // --------| || || || | Six bit groups to index ALPHABET
414 // >>18 >>12 >> 6 >> 0 Right shift necessary
415 // 0x3f 0x3f 0x3f Additional AND
416
417 // Create buffer with zero-padding if there are only one or two
418 // significant bytes passed in the array.
419 // We have to shift left 24 in order to flush out the 1's that appear
420 // when Java treats a value as negative that is cast from a byte to an
421 // int.
422 int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
423 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
424 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
425
426 switch (numSigBytes) {
427 case 3:
428 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
429 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
430 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
431 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
432 return destination;
433
434 case 2:
435 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
436 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
437 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
438 destination[destOffset + 3] = EQUALS_SIGN;
439 return destination;
440
441 case 1:
442 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
443 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
444 destination[destOffset + 2] = EQUALS_SIGN;
445 destination[destOffset + 3] = EQUALS_SIGN;
446 return destination;
447
448 default:
449 return destination;
450 } // end switch
451 } // end encode3to4
452
453 /**
454 * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
455 * to the <code>encoded</code> ByteBuffer. This is an experimental feature.
456 * Currently it does not pass along any options (such as
457 * {@link #DO_BREAK_LINES} or {@link #GZIP}.
458 *
459 * @param raw
460 * input buffer
461 * @param encoded
462 * output buffer
463 * @since 2.3
464 */
465 public static void encode(java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded) {
466 byte[] raw3 = new byte[3];
467 byte[] enc4 = new byte[4];
468
469 while (raw.hasRemaining()) {
470 int rem = Math.min(3, raw.remaining());
471 raw.get(raw3, 0, rem);
472 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
473 encoded.put(enc4);
474 } // end input remaining
475 }
476
477 /**
478 * Performs Base64 encoding on the <code>raw</code> ByteBuffer, writing it
479 * to the <code>encoded</code> CharBuffer. This is an experimental feature.
480 * Currently it does not pass along any options (such as
481 * {@link #DO_BREAK_LINES} or {@link #GZIP}.
482 *
483 * @param raw
484 * input buffer
485 * @param encoded
486 * output buffer
487 * @since 2.3
488 */
489 public static void encode(java.nio.ByteBuffer raw, java.nio.CharBuffer encoded) {
490 byte[] raw3 = new byte[3];
491 byte[] enc4 = new byte[4];
492
493 while (raw.hasRemaining()) {
494 int rem = Math.min(3, raw.remaining());
495 raw.get(raw3, 0, rem);
496 Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
497 for (int i = 0; i < 4; i++) {
498 encoded.put((char) (enc4[i] & 0xFF));
499 }
500 } // end input remaining
501 }
502
503 /**
504 * Serializes an object and returns the Base64-encoded version of that
505 * serialized object.
506 *
507 * <p>
508 * As of v 2.3, if the object cannot be serialized or there is another
509 * error, the method will throw an java.io.IOException. <b>This is new to
510 * v2.3!</b> In earlier versions, it just returned a null value, but in
511 * retrospect that's a pretty poor way to handle it.
512 * </p>
513 *
514 * The object is not GZip-compressed before being encoded.
515 *
516 * @param serializableObject
517 * The object to encode
518 * @return The Base64-encoded object
519 * @throws java.io.IOException
520 * if there is an error
521 * @throws NullPointerException
522 * if serializedObject is null
523 * @since 1.4
524 */
525 public static String encodeObject(java.io.Serializable serializableObject) throws java.io.IOException {
526 return encodeObject(serializableObject, NO_OPTIONS);
527 } // end encodeObject
528
529 /**
530 * Serializes an object and returns the Base64-encoded version of that
531 * serialized object.
532 *
533 * <p>
534 * As of v 2.3, if the object cannot be serialized or there is another
535 * error, the method will throw an java.io.IOException. <b>This is new to
536 * v2.3!</b> In earlier versions, it just returned a null value, but in
537 * retrospect that's a pretty poor way to handle it.
538 * </p>
539 *
540 * The object is not GZip-compressed before being encoded.
541 * <p>
542 * Example options:
543 *
544 * <pre>
545 * GZIP: gzip-compresses object before encoding it.
546 * DO_BREAK_LINES: break lines at 76 characters
547 * </pre>
548 * <p>
549 * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or
550 * <p>
551 * Example:
552 * <code>encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
553 *
554 * @param serializableObject
555 * The object to encode
556 * @param options
557 * Specified options
558 * @return The Base64-encoded object
559 * @see Base64#GZIP
560 * @see Base64#DO_BREAK_LINES
561 * @throws java.io.IOException
562 * if there is an error
563 * @since 2.0
564 */
565 public static String encodeObject(java.io.Serializable serializableObject, int options) throws java.io.IOException {
566
567 if (serializableObject == null) {
568 throw new NullPointerException("Cannot serialize a null object.");
569 } // end if: null
570
571 // Streams
572 java.io.ByteArrayOutputStream baos = null;
573 java.io.OutputStream b64os = null;
574 java.util.zip.GZIPOutputStream gzos = null;
575 java.io.ObjectOutputStream oos = null;
576
577 try {
578 // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
579 baos = new java.io.ByteArrayOutputStream();
580 b64os = new Base64.OutputStream(baos, ENCODE | options);
581 if ((options & GZIP) != 0) {
582 // Gzip
583 gzos = new java.util.zip.GZIPOutputStream(b64os);
584 oos = new java.io.ObjectOutputStream(gzos);
585 } else {
586 // Not gzipped
587 oos = new java.io.ObjectOutputStream(b64os);
588 }
589 oos.writeObject(serializableObject);
590 } // end try
591 catch (java.io.IOException e) {
592 // Catch it and then throw it immediately so that
593 // the finally{} block is called for cleanup.
594 throw e;
595 } // end catch
596 finally {
597 try {
598 oos.close();
599 } catch (Exception e) {
600 }
601 try {
602 gzos.close();
603 } catch (Exception e) {
604 }
605 try {
606 b64os.close();
607 } catch (Exception e) {
608 }
609 try {
610 baos.close();
611 } catch (Exception e) {
612 }
613 } // end finally
614
615 // Return value according to relevant encoding.
616 try {
617 return new String(baos.toByteArray(), PREFERRED_ENCODING);
618 } // end try
619 catch (java.io.UnsupportedEncodingException uue) {
620 // Fall back to some Java default
621 return new String(baos.toByteArray());
622 } // end catch
623
624 } // end encode
625
626 /**
627 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
628 *
629 * @param source
630 * The data to convert
631 * @return The data in Base64-encoded form
632 * @throws NullPointerException
633 * if source array is null
634 * @since 1.4
635 */
636 public static String encodeBytes(byte[] source) {
637 // Since we're not going to have the GZIP encoding turned on,
638 // we're not going to have an java.io.IOException thrown, so
639 // we should not force the user to have to catch it.
640 String encoded = null;
641 try {
642 encoded = encodeBytes(source, 0, source.length, NO_OPTIONS);
643 } catch (java.io.IOException ex) {
644 assert false : ex.getMessage();
645 } // end catch
646 assert encoded != null;
647 return encoded;
648 } // end encodeBytes
649
650 /**
651 * Encodes a byte array into Base64 notation.
652 * <p>
653 * Example options:
654 *
655 * <pre>
656 * GZIP: gzip-compresses object before encoding it.
657 * DO_BREAK_LINES: break lines at 76 characters
658 * <i>Note: Technically, this makes your encoding non-compliant.</i>
659 * </pre>
660 * <p>
661 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
662 * <p>
663 * Example:
664 * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
665 *
666 *
667 * <p>
668 * As of v 2.3, if there is an error with the GZIP stream, the method will
669 * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
670 * versions, it just returned a null value, but in retrospect that's a
671 * pretty poor way to handle it.
672 * </p>
673 *
674 *
675 * @param source
676 * The data to convert
677 * @param options
678 * Specified options
679 * @return The Base64-encoded data as a String
680 * @see Base64#GZIP
681 * @see Base64#DO_BREAK_LINES
682 * @throws java.io.IOException
683 * if there is an error
684 * @throws NullPointerException
685 * if source array is null
686 * @since 2.0
687 */
688 public static String encodeBytes(byte[] source, int options) throws java.io.IOException {
689 return encodeBytes(source, 0, source.length, options);
690 } // end encodeBytes
691
692 /**
693 * Encodes a byte array into Base64 notation. Does not GZip-compress data.
694 *
695 * <p>
696 * As of v 2.3, if there is an error, the method will throw an
697 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
698 * just returned a null value, but in retrospect that's a pretty poor way to
699 * handle it.
700 * </p>
701 *
702 *
703 * @param source
704 * The data to convert
705 * @param off
706 * Offset in array where conversion should begin
707 * @param len
708 * Length of data to convert
709 * @return The Base64-encoded data as a String
710 * @throws NullPointerException
711 * if source array is null
712 * @throws IllegalArgumentException
713 * if source array, offset, or length are invalid
714 * @since 1.4
715 */
716 public static String encodeBytes(byte[] source, int off, int len) {
717 // Since we're not going to have the GZIP encoding turned on,
718 // we're not going to have an java.io.IOException thrown, so
719 // we should not force the user to have to catch it.
720 String encoded = null;
721 try {
722 encoded = encodeBytes(source, off, len, NO_OPTIONS);
723 } catch (java.io.IOException ex) {
724 assert false : ex.getMessage();
725 } // end catch
726 assert encoded != null;
727 return encoded;
728 } // end encodeBytes
729
730 /**
731 * Encodes a byte array into Base64 notation.
732 * <p>
733 * Example options:
734 *
735 * <pre>
736 * GZIP: gzip-compresses object before encoding it.
737 * DO_BREAK_LINES: break lines at 76 characters
738 * <i>Note: Technically, this makes your encoding non-compliant.</i>
739 * </pre>
740 * <p>
741 * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or
742 * <p>
743 * Example:
744 * <code>encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES )</code>
745 *
746 *
747 * <p>
748 * As of v 2.3, if there is an error with the GZIP stream, the method will
749 * throw an java.io.IOException. <b>This is new to v2.3!</b> In earlier
750 * versions, it just returned a null value, but in retrospect that's a
751 * pretty poor way to handle it.
752 * </p>
753 *
754 *
755 * @param source
756 * The data to convert
757 * @param off
758 * Offset in array where conversion should begin
759 * @param len
760 * Length of data to convert
761 * @param options
762 * Specified options
763 * @return The Base64-encoded data as a String
764 * @see Base64#GZIP
765 * @see Base64#DO_BREAK_LINES
766 * @throws java.io.IOException
767 * if there is an error
768 * @throws NullPointerException
769 * if source array is null
770 * @throws IllegalArgumentException
771 * if source array, offset, or length are invalid
772 * @since 2.0
773 */
774 public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
775 byte[] encoded = encodeBytesToBytes(source, off, len, options);
776
777 // Return value according to relevant encoding.
778 try {
779 return new String(encoded, PREFERRED_ENCODING);
780 } // end try
781 catch (java.io.UnsupportedEncodingException uue) {
782 return new String(encoded);
783 } // end catch
784
785 } // end encodeBytes
786
787 /**
788 * Similar to {@link #encodeBytes(byte[])} but returns a byte array instead
789 * of instantiating a String. This is more efficient if you're working with
790 * I/O streams and have large data sets to encode.
791 *
792 *
793 * @param source
794 * The data to convert
795 * @return The Base64-encoded data as a byte[] (of ASCII characters)
796 * @throws NullPointerException
797 * if source array is null
798 * @since 2.3.1
799 */
800 public static byte[] encodeBytesToBytes(byte[] source) {
801 byte[] encoded = null;
802 try {
803 encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS);
804 } catch (java.io.IOException ex) {
805 assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage();
806 }
807 return encoded;
808 }
809
810 /**
811 * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns a byte
812 * array instead of instantiating a String. This is more efficient if you're
813 * working with I/O streams and have large data sets to encode.
814 *
815 *
816 * @param source
817 * The data to convert
818 * @param off
819 * Offset in array where conversion should begin
820 * @param len
821 * Length of data to convert
822 * @param options
823 * Specified options
824 * @return The Base64-encoded data as a String
825 * @see Base64#GZIP
826 * @see Base64#DO_BREAK_LINES
827 * @throws java.io.IOException
828 * if there is an error
829 * @throws NullPointerException
830 * if source array is null
831 * @throws IllegalArgumentException
832 * if source array, offset, or length are invalid
833 * @since 2.3.1
834 */
835 public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException {
836
837 if (source == null) {
838 throw new NullPointerException("Cannot serialize a null array.");
839 } // end if: null
840
841 if (off < 0) {
842 throw new IllegalArgumentException("Cannot have negative offset: " + off);
843 } // end if: off < 0
844
845 if (len < 0) {
846 throw new IllegalArgumentException("Cannot have length offset: " + len);
847 } // end if: len < 0
848
849 if (off + len > source.length) {
850 throw new IllegalArgumentException(String.format(
851 "Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));
852 } // end if: off < 0
853
854 // Compress?
855 if ((options & GZIP) != 0) {
856 java.io.ByteArrayOutputStream baos = null;
857 java.util.zip.GZIPOutputStream gzos = null;
858 Base64.OutputStream b64os = null;
859
860 try {
861 // GZip -> Base64 -> ByteArray
862 baos = new java.io.ByteArrayOutputStream();
863 b64os = new Base64.OutputStream(baos, ENCODE | options);
864 gzos = new java.util.zip.GZIPOutputStream(b64os);
865
866 gzos.write(source, off, len);
867 gzos.close();
868 } // end try
869 catch (java.io.IOException e) {
870 // Catch it and then throw it immediately so that
871 // the finally{} block is called for cleanup.
872 throw e;
873 } // end catch
874 finally {
875 try {
876 gzos.close();
877 } catch (Exception e) {
878 }
879 try {
880 b64os.close();
881 } catch (Exception e) {
882 }
883 try {
884 baos.close();
885 } catch (Exception e) {
886 }
887 } // end finally
888
889 return baos.toByteArray();
890 } // end if: compress
891
892 // Else, don't compress. Better not to use streams at all then.
893 else {
894 boolean breakLines = (options & DO_BREAK_LINES) != 0;
895
896 // int len43 = len * 4 / 3;
897 // byte[] outBuff = new byte[ ( len43 ) // Main 4:3
898 // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
899 // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
900 // Try to determine more precisely how big the array needs to be.
901 // If we get it right, we don't have to do an array copy, and
902 // we save a bunch of memory.
903 int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed
904 // for actual
905 // encoding
906 if (breakLines) {
907 encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline
908 // characters
909 }
910 byte[] outBuff = new byte[encLen];
911
912 int d = 0;
913 int e = 0;
914 int len2 = len - 2;
915 int lineLength = 0;
916 for (; d < len2; d += 3, e += 4) {
917 encode3to4(source, d + off, 3, outBuff, e, options);
918
919 lineLength += 4;
920 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
921 outBuff[e + 4] = NEW_LINE;
922 e++;
923 lineLength = 0;
924 } // end if: end of line
925 } // en dfor: each piece of array
926
927 if (d < len) {
928 encode3to4(source, d + off, len - d, outBuff, e, options);
929 e += 4;
930 } // end if: some padding needed
931
932 // Only resize array if we didn't guess it right.
933 if (e <= outBuff.length - 1) {
934 // If breaking lines and the last byte falls right at
935 // the line length (76 bytes per line), there will be
936 // one extra byte, and the array will need to be resized.
937 // Not too bad of an estimate on array size, I'd say.
938 byte[] finalOut = new byte[e];
939 System.arraycopy(outBuff, 0, finalOut, 0, e);
940 // System.err.println("Having to resize array from " +
941 // outBuff.length + " to " + e );
942 return finalOut;
943 } else {
944 // System.err.println("No need to resize array.");
945 return outBuff;
946 }
947
948 } // end else: don't compress
949
950 } // end encodeBytesToBytes
951
952 /* ******** D E C O D I N G M E T H O D S ******** */
953
954 /**
955 * Decodes four bytes from array <var>source</var> and writes the resulting
956 * bytes (up to three of them) to <var>destination</var>. The source and
957 * destination arrays can be manipulated anywhere along their length by
958 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
959 * does not check to make sure your arrays are large enough to accomodate
960 * <var>srcOffset</var> + 4 for the <var>source</var> array or
961 * <var>destOffset</var> + 3 for the <var>destination</var> array. This
962 * method returns the actual number of bytes that were converted from the
963 * Base64 encoding.
964 * <p>
965 * This is the lowest level of the decoding methods with all possible
966 * parameters.
967 * </p>
968 *
969 *
970 * @param source
971 * the array to convert
972 * @param srcOffset
973 * the index where conversion begins
974 * @param destination
975 * the array to hold the conversion
976 * @param destOffset
977 * the index where output will be put
978 * @param options
979 * alphabet type is pulled from this (standard, url-safe,
980 * ordered)
981 * @return the number of decoded bytes converted
982 * @throws NullPointerException
983 * if source or destination arrays are null
984 * @throws IllegalArgumentException
985 * if srcOffset or destOffset are invalid or there is not enough
986 * room in the array.
987 * @since 1.3
988 */
989 private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset, int options) {
990
991 // Lots of error checking and exception throwing
992 if (source == null) {
993 throw new NullPointerException("Source array was null.");
994 } // end if
995 if (destination == null) {
996 throw new NullPointerException("Destination array was null.");
997 } // end if
998 if (srcOffset < 0 || srcOffset + 3 >= source.length) {
999 throw new IllegalArgumentException(String.format(
1000 "Source array with length %d cannot have offset of %d and still process four bytes.",
1001 source.length, srcOffset));
1002 } // end if
1003 if (destOffset < 0 || destOffset + 2 >= destination.length) {
1004 throw new IllegalArgumentException(String.format(
1005 "Destination array with length %d cannot have offset of %d and still store three bytes.",
1006 destination.length, destOffset));
1007 } // end if
1008
1009 byte[] DECODABET = getDecodabet(options);
1010
1011 // Example: Dk==
1012 if (source[srcOffset + 2] == EQUALS_SIGN) {
1013 // Two ways to do the same thing. Don't know which way I like best.
1014 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1015 // )
1016 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
1017 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1018 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
1019
1020 destination[destOffset] = (byte) (outBuff >>> 16);
1021 return 1;
1022 }
1023
1024 // Example: DkL=
1025 else if (source[srcOffset + 3] == EQUALS_SIGN) {
1026 // Two ways to do the same thing. Don't know which way I like best.
1027 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1028 // )
1029 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1030 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
1031 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1032 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1033 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
1034
1035 destination[destOffset] = (byte) (outBuff >>> 16);
1036 destination[destOffset + 1] = (byte) (outBuff >>> 8);
1037 return 2;
1038 }
1039
1040 // Example: DkLE
1041 else {
1042 // Two ways to do the same thing. Don't know which way I like best.
1043 // int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6
1044 // )
1045 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
1046 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
1047 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
1048 int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
1049 | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12)
1050 | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
1051
1052 destination[destOffset] = (byte) (outBuff >> 16);
1053 destination[destOffset + 1] = (byte) (outBuff >> 8);
1054 destination[destOffset + 2] = (byte) (outBuff);
1055
1056 return 3;
1057 }
1058 } // end decodeToBytes
1059
1060 /**
1061 * Low-level access to decoding ASCII characters in the form of a byte
1062 * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
1063 * generally a recommended method, although it is used internally as part of
1064 * the decoding process. Special case: if len = 0, an empty array is
1065 * returned. Still, if you need more speed and reduced memory footprint (and
1066 * aren't gzipping), consider this method.
1067 *
1068 * @param source
1069 * The Base64 encoded data
1070 * @return decoded data
1071 * @since 2.3.1
1072 */
1073 public static byte[] decode(byte[] source) throws java.io.IOException {
1074 byte[] decoded = null;
1075 // try {
1076 decoded = decode(source, 0, source.length, Base64.NO_OPTIONS);
1077 // } catch( java.io.IOException ex ) {
1078 // assert false :
1079 // "IOExceptions only come from GZipping, which is turned off: " +
1080 // ex.getMessage();
1081 // }
1082 return decoded;
1083 }
1084
1085 /**
1086 * Low-level access to decoding ASCII characters in the form of a byte
1087 * array. <strong>Ignores GUNZIP option, if it's set.</strong> This is not
1088 * generally a recommended method, although it is used internally as part of
1089 * the decoding process. Special case: if len = 0, an empty array is
1090 * returned. Still, if you need more speed and reduced memory footprint (and
1091 * aren't gzipping), consider this method.
1092 *
1093 * @param source
1094 * The Base64 encoded data
1095 * @param off
1096 * The offset of where to begin decoding
1097 * @param len
1098 * The length of characters to decode
1099 * @param options
1100 * Can specify options such as alphabet type to use
1101 * @return decoded data
1102 * @throws java.io.IOException
1103 * If bogus characters exist in source data
1104 * @since 1.3
1105 */
1106 public static byte[] decode(byte[] source, int off, int len, int options) throws java.io.IOException {
1107
1108 // Lots of error checking and exception throwing
1109 if (source == null) {
1110 throw new NullPointerException("Cannot decode null source array.");
1111 } // end if
1112 if (off < 0 || off + len > source.length) {
1113 throw new IllegalArgumentException(String.format(
1114 "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off,
1115 len));
1116 } // end if
1117
1118 if (len == 0) {
1119 return new byte[0];
1120 } else if (len < 4) {
1121 throw new IllegalArgumentException(
1122 "Base64-encoded string must have at least four characters, but length specified was " + len);
1123 } // end if
1124
1125 byte[] DECODABET = getDecodabet(options);
1126
1127 int len34 = len * 3 / 4; // Estimate on array size
1128 byte[] outBuff = new byte[len34]; // Upper limit on size of output
1129 int outBuffPosn = 0; // Keep track of where we're writing
1130
1131 byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating
1132 // white space
1133 int b4Posn = 0; // Keep track of four byte input buffer
1134 int i = 0; // Source array counter
1135 byte sbiDecode = 0; // Special value from DECODABET
1136
1137 for (i = off; i < off + len; i++) { // Loop through source
1138
1139 sbiDecode = DECODABET[source[i] & 0xFF];
1140
1141 // White space, Equals sign, or legit Base64 character
1142 // Note the values such as -5 and -9 in the
1143 // DECODABETs at the top of the file.
1144 if (sbiDecode >= WHITE_SPACE_ENC) {
1145 if (sbiDecode >= EQUALS_SIGN_ENC) {
1146 b4[b4Posn++] = source[i]; // Save non-whitespace
1147 if (b4Posn > 3) { // Time to decode?
1148 outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options);
1149 b4Posn = 0;
1150
1151 // If that was the equals sign, break out of 'for' loop
1152 if (source[i] == EQUALS_SIGN) {
1153 break;
1154 } // end if: equals sign
1155 } // end if: quartet built
1156 } // end if: equals sign or better
1157 } // end if: white space, equals sign or better
1158 else {
1159 // There's a bad input character in the Base64 stream.
1160 throw new java.io.IOException(String.format(
1161 "Bad Base64 input character decimal %d in array position %d", ((int) source[i]) & 0xFF, i));
1162 } // end else:
1163 } // each input character
1164
1165 byte[] out = new byte[outBuffPosn];
1166 System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
1167 return out;
1168 } // end decode
1169
1170 /**
1171 * Decodes data from Base64 notation, automatically detecting
1172 * gzip-compressed data and decompressing it.
1173 *
1174 * @param s
1175 * the string to decode
1176 * @return the decoded data
1177 * @throws java.io.IOException
1178 * If there is a problem
1179 * @since 1.4
1180 */
1181 public static byte[] decode(String s) throws java.io.IOException {
1182 return decode(s, NO_OPTIONS);
1183 }
1184
1185 /**
1186 * Decodes data from Base64 notation, automatically detecting
1187 * gzip-compressed data and decompressing it.
1188 *
1189 * @param s
1190 * the string to decode
1191 * @param options
1192 * encode options such as URL_SAFE
1193 * @return the decoded data
1194 * @throws java.io.IOException
1195 * if there is an error
1196 * @throws NullPointerException
1197 * if <tt>s</tt> is null
1198 * @since 1.4
1199 */
1200 public static byte[] decode(String s, int options) throws java.io.IOException {
1201
1202 if (s == null) {
1203 throw new NullPointerException("Input string was null.");
1204 } // end if
1205
1206 byte[] bytes;
1207 try {
1208 bytes = s.getBytes(PREFERRED_ENCODING);
1209 } // end try
1210 catch (java.io.UnsupportedEncodingException uee) {
1211 bytes = s.getBytes();
1212 } // end catch
1213 // </change>
1214
1215 // Decode
1216 bytes = decode(bytes, 0, bytes.length, options);
1217
1218 // Check to see if it's gzip-compressed
1219 // GZIP Magic Two-Byte Number: 0x8b1f (35615)
1220 boolean dontGunzip = (options & DONT_GUNZIP) != 0;
1221 if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) {
1222
1223 int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
1224 if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
1225 java.io.ByteArrayInputStream bais = null;
1226 java.util.zip.GZIPInputStream gzis = null;
1227 java.io.ByteArrayOutputStream baos = null;
1228 byte[] buffer = new byte[2048];
1229 int length = 0;
1230
1231 try {
1232 baos = new java.io.ByteArrayOutputStream();
1233 bais = new java.io.ByteArrayInputStream(bytes);
1234 gzis = new java.util.zip.GZIPInputStream(bais);
1235
1236 while ((length = gzis.read(buffer)) >= 0) {
1237 baos.write(buffer, 0, length);
1238 } // end while: reading input
1239
1240 // No error? Get new bytes.
1241 bytes = baos.toByteArray();
1242
1243 } // end try
1244 catch (java.io.IOException e) {
1245 e.printStackTrace();
1246 // Just return originally-decoded bytes
1247 } // end catch
1248 finally {
1249 try {
1250 baos.close();
1251 } catch (Exception e) {
1252 }
1253 try {
1254 gzis.close();
1255 } catch (Exception e) {
1256 }
1257 try {
1258 bais.close();
1259 } catch (Exception e) {
1260 }
1261 } // end finally
1262
1263 } // end if: gzipped
1264 } // end if: bytes.length >= 2
1265
1266 return bytes;
1267 } // end decode
1268
1269 /**
1270 * Attempts to decode Base64 data and deserialize a Java Object within.
1271 * Returns <tt>null</tt> if there was an error.
1272 *
1273 * @param encodedObject
1274 * The Base64 data to decode
1275 * @return The decoded and deserialized object
1276 * @throws NullPointerException
1277 * if encodedObject is null
1278 * @throws java.io.IOException
1279 * if there is a general error
1280 * @throws ClassNotFoundException
1281 * if the decoded object is of a class that cannot be found by
1282 * the JVM
1283 * @since 1.5
1284 */
1285 public static Object decodeToObject(String encodedObject) throws java.io.IOException,
1286 java.lang.ClassNotFoundException {
1287 return decodeToObject(encodedObject, NO_OPTIONS, null);
1288 }
1289
1290 /**
1291 * Attempts to decode Base64 data and deserialize a Java Object within.
1292 * Returns <tt>null</tt> if there was an error. If <tt>loader</tt> is not
1293 * null, it will be the class loader used when deserializing.
1294 *
1295 * @param encodedObject
1296 * The Base64 data to decode
1297 * @param options
1298 * Various parameters related to decoding
1299 * @param loader
1300 * Optional class loader to use in deserializing classes.
1301 * @return The decoded and deserialized object
1302 * @throws NullPointerException
1303 * if encodedObject is null
1304 * @throws java.io.IOException
1305 * if there is a general error
1306 * @throws ClassNotFoundException
1307 * if the decoded object is of a class that cannot be found by
1308 * the JVM
1309 * @since 2.3.4
1310 */
1311 public static Object decodeToObject(String encodedObject, int options, final ClassLoader loader)
1312 throws java.io.IOException, java.lang.ClassNotFoundException {
1313
1314 // Decode and gunzip if necessary
1315 byte[] objBytes = decode(encodedObject, options);
1316
1317 java.io.ByteArrayInputStream bais = null;
1318 java.io.ObjectInputStream ois = null;
1319 Object obj = null;
1320
1321 try {
1322 bais = new java.io.ByteArrayInputStream(objBytes);
1323
1324 // If no custom class loader is provided, use Java's builtin OIS.
1325 if (loader == null) {
1326 ois = new java.io.ObjectInputStream(bais);
1327 } // end if: no loader provided
1328
1329 // Else make a customized object input stream that uses
1330 // the provided class loader.
1331 else {
1332 ois = new java.io.ObjectInputStream(bais) {
1333 @Override
1334 public Class<?> resolveClass(java.io.ObjectStreamClass streamClass) throws java.io.IOException,
1335 ClassNotFoundException {
1336 Class<?> c = Class.forName(streamClass.getName(), false, loader);
1337 if (c == null) {
1338 return super.resolveClass(streamClass);
1339 } else {
1340 return c; // Class loader knows of this class.
1341 } // end else: not null
1342 } // end resolveClass
1343 }; // end ois
1344 } // end else: no custom class loader
1345
1346 obj = ois.readObject();
1347 } // end try
1348 catch (java.io.IOException e) {
1349 throw e; // Catch and throw in order to execute finally{}
1350 } // end catch
1351 catch (java.lang.ClassNotFoundException e) {
1352 throw e; // Catch and throw in order to execute finally{}
1353 } // end catch
1354 finally {
1355 try {
1356 bais.close();
1357 } catch (Exception e) {
1358 }
1359 try {
1360 ois.close();
1361 } catch (Exception e) {
1362 }
1363 } // end finally
1364
1365 return obj;
1366 } // end decodeObject
1367
1368 /**
1369 * Convenience method for encoding data to a file.
1370 *
1371 * <p>
1372 * As of v 2.3, if there is a error, the method will throw an
1373 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1374 * just returned false, but in retrospect that's a pretty poor way to handle
1375 * it.
1376 * </p>
1377 *
1378 * @param dataToEncode
1379 * byte array of data to encode in base64 form
1380 * @param filename
1381 * Filename for saving encoded data
1382 * @throws java.io.IOException
1383 * if there is an error
1384 * @throws NullPointerException
1385 * if dataToEncode is null
1386 * @since 2.1
1387 */
1388 public static void encodeToFile(byte[] dataToEncode, String filename) throws java.io.IOException {
1389
1390 if (dataToEncode == null) {
1391 throw new NullPointerException("Data to encode was null.");
1392 } // end iff
1393
1394 Base64.OutputStream bos = null;
1395 try {
1396 bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.ENCODE);
1397 bos.write(dataToEncode);
1398 } // end try
1399 catch (java.io.IOException e) {
1400 throw e; // Catch and throw to execute finally{} block
1401 } // end catch: java.io.IOException
1402 finally {
1403 try {
1404 bos.close();
1405 } catch (Exception e) {
1406 }
1407 } // end finally
1408
1409 } // end encodeToFile
1410
1411 /**
1412 * Convenience method for decoding data to a file.
1413 *
1414 * <p>
1415 * As of v 2.3, if there is a error, the method will throw an
1416 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1417 * just returned false, but in retrospect that's a pretty poor way to handle
1418 * it.
1419 * </p>
1420 *
1421 * @param dataToDecode
1422 * Base64-encoded data as a string
1423 * @param filename
1424 * Filename for saving decoded data
1425 * @throws java.io.IOException
1426 * if there is an error
1427 * @since 2.1
1428 */
1429 public static void decodeToFile(String dataToDecode, String filename) throws java.io.IOException {
1430
1431 Base64.OutputStream bos = null;
1432 try {
1433 bos = new Base64.OutputStream(new java.io.FileOutputStream(filename), Base64.DECODE);
1434 bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
1435 } // end try
1436 catch (java.io.IOException e) {
1437 throw e; // Catch and throw to execute finally{} block
1438 } // end catch: java.io.IOException
1439 finally {
1440 try {
1441 bos.close();
1442 } catch (Exception e) {
1443 }
1444 } // end finally
1445
1446 } // end decodeToFile
1447
1448 /**
1449 * Convenience method for reading a base64-encoded file and decoding it.
1450 *
1451 * <p>
1452 * As of v 2.3, if there is a error, the method will throw an
1453 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1454 * just returned false, but in retrospect that's a pretty poor way to handle
1455 * it.
1456 * </p>
1457 *
1458 * @param filename
1459 * Filename for reading encoded data
1460 * @return decoded byte array
1461 * @throws java.io.IOException
1462 * if there is an error
1463 * @since 2.1
1464 */
1465 public static byte[] decodeFromFile(String filename) throws java.io.IOException {
1466
1467 byte[] decodedData = null;
1468 Base64.InputStream bis = null;
1469 try {
1470 // Set up some useful variables
1471 java.io.File file = new java.io.File(filename);
1472 byte[] buffer = null;
1473 int length = 0;
1474 int numBytes = 0;
1475
1476 // Check for size of file
1477 if (file.length() > Integer.MAX_VALUE) {
1478 throw new java.io.IOException("File is too big for this convenience method (" + file.length()
1479 + " bytes).");
1480 } // end if: file too big for int index
1481 buffer = new byte[(int) file.length()];
1482
1483 // Open a stream
1484 bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
1485 Base64.DECODE);
1486
1487 // Read until done
1488 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1489 length += numBytes;
1490 } // end while
1491
1492 // Save in a variable to return
1493 decodedData = new byte[length];
1494 System.arraycopy(buffer, 0, decodedData, 0, length);
1495
1496 } // end try
1497 catch (java.io.IOException e) {
1498 throw e; // Catch and release to execute finally{}
1499 } // end catch: java.io.IOException
1500 finally {
1501 try {
1502 bis.close();
1503 } catch (Exception e) {
1504 }
1505 } // end finally
1506
1507 return decodedData;
1508 } // end decodeFromFile
1509
1510 /**
1511 * Convenience method for reading a binary file and base64-encoding it.
1512 *
1513 * <p>
1514 * As of v 2.3, if there is a error, the method will throw an
1515 * java.io.IOException. <b>This is new to v2.3!</b> In earlier versions, it
1516 * just returned false, but in retrospect that's a pretty poor way to handle
1517 * it.
1518 * </p>
1519 *
1520 * @param filename
1521 * Filename for reading binary data
1522 * @return base64-encoded string
1523 * @throws java.io.IOException
1524 * if there is an error
1525 * @since 2.1
1526 */
1527 public static String encodeFromFile(String filename) throws java.io.IOException {
1528
1529 String encodedData = null;
1530 Base64.InputStream bis = null;
1531 try {
1532 // Set up some useful variables
1533 java.io.File file = new java.io.File(filename);
1534 byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need
1535 // max()
1536 // for
1537 // math
1538 // on
1539 // small
1540 // files
1541 // (v2.2.1);
1542 // Need
1543 // +1
1544 // for
1545 // a
1546 // few
1547 // corner
1548 // cases
1549 // (v2.3.5)
1550 int length = 0;
1551 int numBytes = 0;
1552
1553 // Open a stream
1554 bis = new Base64.InputStream(new java.io.BufferedInputStream(new java.io.FileInputStream(file)),
1555 Base64.ENCODE);
1556
1557 // Read until done
1558 while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
1559 length += numBytes;
1560 } // end while
1561
1562 // Save in a variable to return
1563 encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
1564
1565 } // end try
1566 catch (java.io.IOException e) {
1567 throw e; // Catch and release to execute finally{}
1568 } // end catch: java.io.IOException
1569 finally {
1570 try {
1571 bis.close();
1572 } catch (Exception e) {
1573 }
1574 } // end finally
1575
1576 return encodedData;
1577 } // end encodeFromFile
1578
1579 /**
1580 * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>.
1581 *
1582 * @param infile
1583 * Input file
1584 * @param outfile
1585 * Output file
1586 * @throws java.io.IOException
1587 * if there is an error
1588 * @since 2.2
1589 */
1590 public static void encodeFileToFile(String infile, String outfile) throws java.io.IOException {
1591
1592 String encoded = Base64.encodeFromFile(infile);
1593 java.io.OutputStream out = null;
1594 try {
1595 out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1596 out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
1597 } // end try
1598 catch (java.io.IOException e) {
1599 throw e; // Catch and release to execute finally{}
1600 } // end catch
1601 finally {
1602 try {
1603 out.close();
1604 } catch (Exception ex) {
1605 }
1606 } // end finally
1607 } // end encodeFileToFile
1608
1609 /**
1610 * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>.
1611 *
1612 * @param infile
1613 * Input file
1614 * @param outfile
1615 * Output file
1616 * @throws java.io.IOException
1617 * if there is an error
1618 * @since 2.2
1619 */
1620 public static void decodeFileToFile(String infile, String outfile) throws java.io.IOException {
1621
1622 byte[] decoded = Base64.decodeFromFile(infile);
1623 java.io.OutputStream out = null;
1624 try {
1625 out = new java.io.BufferedOutputStream(new java.io.FileOutputStream(outfile));
1626 out.write(decoded);
1627 } // end try
1628 catch (java.io.IOException e) {
1629 throw e; // Catch and release to execute finally{}
1630 } // end catch
1631 finally {
1632 try {
1633 out.close();
1634 } catch (Exception ex) {
1635 }
1636 } // end finally
1637 } // end decodeFileToFile
1638
1639 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
1640
1641 /**
1642 * A {@link Base64.InputStream} will read data from another
1643 * <tt>java.io.InputStream</tt>, given in the constructor, and encode/decode
1644 * to/from Base64 notation on the fly.
1645 *
1646 * @see Base64
1647 * @since 1.3
1648 */
1649 public static class InputStream extends java.io.FilterInputStream {
1650
1651 private boolean encode; // Encoding or decoding
1652 private int position; // Current position in the buffer
1653 private byte[] buffer; // Small buffer holding converted data
1654 private int bufferLength; // Length of buffer (3 or 4)
1655 private int numSigBytes; // Number of meaningful bytes in the buffer
1656 private int lineLength;
1657 private boolean breakLines; // Break lines at less than 80 characters
1658 private int options; // Record options used to create the stream.
1659 private byte[] decodabet; // Local copies to avoid extra method calls
1660
1661 /**
1662 * Constructs a {@link Base64.InputStream} in DECODE mode.
1663 *
1664 * @param in
1665 * the <tt>java.io.InputStream</tt> from which to read data.
1666 * @since 1.3
1667 */
1668 public InputStream(java.io.InputStream in) {
1669 this(in, DECODE);
1670 } // end constructor
1671
1672 /**
1673 * Constructs a {@link Base64.InputStream} in either ENCODE or DECODE
1674 * mode.
1675 * <p>
1676 * Valid options:
1677 *
1678 * <pre>
1679 * ENCODE or DECODE: Encode or Decode as data is read.
1680 * DO_BREAK_LINES: break lines at 76 characters
1681 * (only meaningful when encoding)</i>
1682 * </pre>
1683 * <p>
1684 * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code>
1685 *
1686 *
1687 * @param in
1688 * the <tt>java.io.InputStream</tt> from which to read data.
1689 * @param options
1690 * Specified options
1691 * @see Base64#ENCODE
1692 * @see Base64#DECODE
1693 * @see Base64#DO_BREAK_LINES
1694 * @since 2.0
1695 */
1696 public InputStream(java.io.InputStream in, int options) {
1697
1698 super(in);
1699 this.options = options; // Record for later
1700 this.breakLines = (options & DO_BREAK_LINES) > 0;
1701 this.encode = (options & ENCODE) > 0;
1702 this.bufferLength = encode ? 4 : 3;
1703 this.buffer = new byte[bufferLength];
1704 this.position = -1;
1705 this.lineLength = 0;
1706 this.decodabet = getDecodabet(options);
1707 } // end constructor
1708
1709 /**
1710 * Reads enough of the input stream to convert to/from Base64 and
1711 * returns the next byte.
1712 *
1713 * @return next byte
1714 * @since 1.3
1715 */
1716 @Override
1717 public int read() throws java.io.IOException {
1718
1719 // Do we need to get data?
1720 if (position < 0) {
1721 if (encode) {
1722 byte[] b3 = new byte[3];
1723 int numBinaryBytes = 0;
1724 for (int i = 0; i < 3; i++) {
1725 int b = in.read();
1726
1727 // If end of stream, b is -1.
1728 if (b >= 0) {
1729 b3[i] = (byte) b;
1730 numBinaryBytes++;
1731 } else {
1732 break; // out of for loop
1733 } // end else: end of stream
1734
1735 } // end for: each needed input byte
1736
1737 if (numBinaryBytes > 0) {
1738 encode3to4(b3, 0, numBinaryBytes, buffer, 0, options);
1739 position = 0;
1740 numSigBytes = 4;
1741 } // end if: got data
1742 else {
1743 return -1; // Must be end of stream
1744 } // end else
1745 } // end if: encoding
1746
1747 // Else decoding
1748 else {
1749 byte[] b4 = new byte[4];
1750 int i = 0;
1751 for (i = 0; i < 4; i++) {
1752 // Read four "meaningful" bytes:
1753 int b = 0;
1754 do {
1755 b = in.read();
1756 } while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC);
1757
1758 if (b < 0) {
1759 break; // Reads a -1 if end of stream
1760 } // end if: end of stream
1761
1762 b4[i] = (byte) b;
1763 } // end for: each needed input byte
1764
1765 if (i == 4) {
1766 numSigBytes = decode4to3(b4, 0, buffer, 0, options);
1767 position = 0;
1768 } // end if: got four characters
1769 else if (i == 0) {
1770 return -1;
1771 } // end else if: also padded correctly
1772 else {
1773 // Must have broken out from above.
1774 throw new java.io.IOException("Improperly padded Base64 input.");
1775 } // end
1776
1777 } // end else: decode
1778 } // end else: get data
1779
1780 // Got data?
1781 if (position >= 0) {
1782 // End of relevant data?
1783 if ( /* !encode && */position >= numSigBytes) {
1784 return -1;
1785 } // end if: got data
1786
1787 if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) {
1788 lineLength = 0;
1789 return '\n';
1790 } // end if
1791 else {
1792 lineLength++; // This isn't important when decoding
1793 // but throwing an extra "if" seems
1794 // just as wasteful.
1795
1796 int b = buffer[position++];
1797
1798 if (position >= bufferLength) {
1799 position = -1;
1800 } // end if: end
1801
1802 return b & 0xFF; // This is how you "cast" a byte that's
1803 // intended to be unsigned.
1804 } // end else
1805 } // end if: position >= 0
1806
1807 // Else error
1808 else {
1809 throw new java.io.IOException("Error in Base64 code reading stream.");
1810 } // end else
1811 } // end read
1812
1813 /**
1814 * Calls {@link #read()} repeatedly until the end of stream is reached
1815 * or <var>len</var> bytes are read. Returns number of bytes read into
1816 * array or -1 if end of stream is encountered.
1817 *
1818 * @param dest
1819 * array to hold values
1820 * @param off
1821 * offset for array
1822 * @param len
1823 * max number of bytes to read into array
1824 * @return bytes read into array or -1 if end of stream is encountered.
1825 * @since 1.3
1826 */
1827 @Override
1828 public int read(byte[] dest, int off, int len) throws java.io.IOException {
1829 int i;
1830 int b;
1831 for (i = 0; i < len; i++) {
1832 b = read();
1833
1834 if (b >= 0) {
1835 dest[off + i] = (byte) b;
1836 } else if (i == 0) {
1837 return -1;
1838 } else {
1839 break; // Out of 'for' loop
1840 } // Out of 'for' loop
1841 } // end for: each byte read
1842 return i;
1843 } // end read
1844
1845 } // end inner class InputStream
1846
1847 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1848
1849 /**
1850 * A {@link Base64.OutputStream} will write data to another
1851 * <tt>java.io.OutputStream</tt>, given in the constructor, and
1852 * encode/decode to/from Base64 notation on the fly.
1853 *
1854 * @see Base64
1855 * @since 1.3
1856 */
1857 public static class OutputStream extends java.io.FilterOutputStream {
1858
1859 private boolean encode;
1860 private int position;
1861 private byte[] buffer;
1862 private int bufferLength;
1863 private int lineLength;
1864 private boolean breakLines;
1865 private byte[] b4; // Scratch used in a few places
1866 private boolean suspendEncoding;
1867 private int options; // Record for later
1868 private byte[] decodabet; // Local copies to avoid extra method calls
1869
1870 /**
1871 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1872 *
1873 * @param out
1874 * the <tt>java.io.OutputStream</tt> to which data will be
1875 * written.
1876 * @since 1.3
1877 */
1878 public OutputStream(java.io.OutputStream out) {
1879 this(out, ENCODE);
1880 } // end constructor
1881
1882 /**
1883 * Constructs a {@link Base64.OutputStream} in either ENCODE or DECODE
1884 * mode.
1885 * <p>
1886 * Valid options:
1887 *
1888 * <pre>
1889 * ENCODE or DECODE: Encode or Decode as data is read.
1890 * DO_BREAK_LINES: don't break lines at 76 characters
1891 * (only meaningful when encoding)</i>
1892 * </pre>
1893 * <p>
1894 * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code>
1895 *
1896 * @param out
1897 * the <tt>java.io.OutputStream</tt> to which data will be
1898 * written.
1899 * @param options
1900 * Specified options.
1901 * @see Base64#ENCODE
1902 * @see Base64#DECODE
1903 * @see Base64#DO_BREAK_LINES
1904 * @since 1.3
1905 */
1906 public OutputStream(java.io.OutputStream out, int options) {
1907 super(out);
1908 this.breakLines = (options & DO_BREAK_LINES) != 0;
1909 this.encode = (options & ENCODE) != 0;
1910 this.bufferLength = encode ? 3 : 4;
1911 this.buffer = new byte[bufferLength];
1912 this.position = 0;
1913 this.lineLength = 0;
1914 this.suspendEncoding = false;
1915 this.b4 = new byte[4];
1916 this.options = options;
1917 this.decodabet = getDecodabet(options);
1918 } // end constructor
1919
1920 /**
1921 * Writes the byte to the output stream after converting to/from Base64
1922 * notation. When encoding, bytes are buffered three at a time before
1923 * the output stream actually gets a write() call. When decoding, bytes
1924 * are buffered four at a time.
1925 *
1926 * @param theByte
1927 * the byte to write
1928 * @since 1.3
1929 */
1930 @Override
1931 public void write(int theByte) throws java.io.IOException {
1932 // Encoding suspended?
1933 if (suspendEncoding) {
1934 this.out.write(theByte);
1935 return;
1936 } // end if: supsended
1937
1938 // Encode?
1939 if (encode) {
1940 buffer[position++] = (byte) theByte;
1941 if (position >= bufferLength) { // Enough to encode.
1942
1943 this.out.write(encode3to4(b4, buffer, bufferLength, options));
1944
1945 lineLength += 4;
1946 if (breakLines && lineLength >= MAX_LINE_LENGTH) {
1947 this.out.write(NEW_LINE);
1948 lineLength = 0;
1949 } // end if: end of line
1950
1951 position = 0;
1952 } // end if: enough to output
1953 } // end if: encoding
1954
1955 // Else, Decoding
1956 else {
1957 // Meaningful Base64 character?
1958 if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) {
1959 buffer[position++] = (byte) theByte;
1960 if (position >= bufferLength) { // Enough to output.
1961
1962 int len = Base64.decode4to3(buffer, 0, b4, 0, options);
1963 out.write(b4, 0, len);
1964 position = 0;
1965 } // end if: enough to output
1966 } // end if: meaningful base64 character
1967 else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) {
1968 throw new java.io.IOException("Invalid character in Base64 data.");
1969 } // end else: not white space either
1970 } // end else: decoding
1971 } // end write
1972
1973 /**
1974 * Calls {@link #write(int)} repeatedly until <var>len</var> bytes are
1975 * written.
1976 *
1977 * @param theBytes
1978 * array from which to read bytes
1979 * @param off
1980 * offset for array
1981 * @param len
1982 * max number of bytes to read into array
1983 * @since 1.3
1984 */
1985 @Override
1986 public void write(byte[] theBytes, int off, int len) throws java.io.IOException {
1987 // Encoding suspended?
1988 if (suspendEncoding) {
1989 this.out.write(theBytes, off, len);
1990 return;
1991 } // end if: supsended
1992
1993 for (int i = 0; i < len; i++) {
1994 write(theBytes[off + i]);
1995 } // end for: each byte written
1996
1997 } // end write
1998
1999 /**
2000 * Method added by PHIL. [Thanks, PHIL. -Rob] This pads the buffer
2001 * without closing the stream.
2002 *
2003 * @throws java.io.IOException
2004 * if there's an error.
2005 */
2006 public void flushBase64() throws java.io.IOException {
2007 if (position > 0) {
2008 if (encode) {
2009 out.write(encode3to4(b4, buffer, position, options));
2010 position = 0;
2011 } // end if: encoding
2012 else {
2013 throw new java.io.IOException("Base64 input not properly padded.");
2014 } // end else: decoding
2015 } // end if: buffer partially full
2016
2017 } // end flush
2018
2019 /**
2020 * Flushes and closes (I think, in the superclass) the stream.
2021 *
2022 * @since 1.3
2023 */
2024 @Override
2025 public void close() throws java.io.IOException {
2026 // 1. Ensure that pending characters are written
2027 flushBase64();
2028
2029 // 2. Actually close the stream
2030 // Base class both flushes and closes.
2031 super.close();
2032
2033 buffer = null;
2034 out = null;
2035 } // end close
2036
2037 /**
2038 * Suspends encoding of the stream. May be helpful if you need to embed
2039 * a piece of base64-encoded data in a stream.
2040 *
2041 * @throws java.io.IOException
2042 * if there's an error flushing
2043 * @since 1.5.1
2044 */
2045 public void suspendEncoding() throws java.io.IOException {
2046 flushBase64();
2047 this.suspendEncoding = true;
2048 } // end suspendEncoding
2049
2050 /**
2051 * Resumes encoding of the stream. May be helpful if you need to embed a
2052 * piece of base64-encoded data in a stream.
2053 *
2054 * @since 1.5.1
2055 */
2056 public void resumeEncoding() {
2057 this.suspendEncoding = false;
2058 } // end resumeEncoding
2059
2060 } // end inner class OutputStream
2061
2062 } // end class Base64