001 package org.apache.ojb.broker.util;
002
003 /* Copyright 2002-2005 The Apache Software Foundation
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 import org.apache.ojb.broker.util.logging.LoggerFactory;
019
020 public class Base64
021 {
022
023 /** Specify encoding (value is <tt>true</tt>). */
024 public final static boolean ENCODE = true;
025
026
027 /** Specify decoding (value is <tt>false</tt>). */
028 public final static boolean DECODE = false;
029
030
031 /** Maximum line length (76) of Base64 output. */
032 private final static int MAX_LINE_LENGTH = 76;
033
034
035 /** The equals sign (=) as a byte. */
036 private final static byte EQUALS_SIGN = (byte)'=';
037
038
039 /** The new line character (\n) as a byte. */
040 private final static byte NEW_LINE = (byte)'\n';
041
042
043 /** The 64 valid Base64 values. */
044 private final static byte[] ALPHABET =
045 {
046 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
047 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
048 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
049 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
050 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
051 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
052 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
053 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
054 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
055 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
056 };
057
058 /**
059 * Translates a Base64 value to either its 6-bit reconstruction value
060 * or a negative number indicating some other meaning.
061 **/
062 private final static byte[] DECODABET =
063 {
064 -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8
065 -5,-5, // Whitespace: Tab and Linefeed
066 -9,-9, // Decimal 11 - 12
067 -5, // Whitespace: Carriage Return
068 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26
069 -9,-9,-9,-9,-9, // Decimal 27 - 31
070 -5, // Whitespace: Space
071 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42
072 62, // Plus sign at decimal 43
073 -9,-9,-9, // Decimal 44 - 46
074 63, // Slash at decimal 47
075 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine
076 -9,-9,-9, // Decimal 58 - 60
077 -1, // Equals sign at decimal 61
078 -9,-9,-9, // Decimal 62 - 64
079 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N'
080 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z'
081 -9,-9,-9,-9,-9,-9, // Decimal 91 - 96
082 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm'
083 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z'
084 -9,-9,-9,-9 // Decimal 123 - 126
085 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139
086 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
087 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
088 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
089 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
090 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
091 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
092 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
093 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
094 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
095 };
096
097 private final static byte BAD_ENCODING = -9; // Indicates error in encoding
098 private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
099 private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
100
101
102 /** Defeats instantiation. */
103 private Base64(){}
104
105
106
107 /**
108 * Testing. Feel free--in fact I encourage you--to throw out
109 * this entire "main" method when you actually deploy this code.
110 */
111 public static void main( String[] args )
112 {
113 try
114 {
115 // Test encoding/decoding byte arrays
116 {
117 byte[] bytes1 = { (byte)2,(byte)2,(byte)3,(byte)0,(byte)9 }; // My zip code
118 byte[] bytes2 = { (byte)99,(byte)2,(byte)2,(byte)3,(byte)0,(byte)9 };
119 System.out.println( "Bytes 2,2,3,0,9 as Base64: " + encodeBytes( bytes1 ) );
120 System.out.println( "Bytes 2,2,3,0,9 w/ offset: " + encodeBytes( bytes2, 1, bytes2.length-1 ) );
121 byte[] dbytes = decode( encodeBytes( bytes1 ) );
122 System.out.print( encodeBytes( bytes1 ) + " decoded: " );
123 for( int i = 0; i < dbytes.length; i++ )
124 System.out.print( dbytes[i] + (i<dbytes.length-1?",":"\n") );
125 } // end testing byte arrays
126
127
128
129
130 // Test Input Stream
131 {
132 // Read GIF stored in base64 form.
133 java.io.FileInputStream fis = new java.io.FileInputStream( "test.gif.b64" );
134 Base64.InputStream b64is = new Base64.InputStream( fis, DECODE );
135
136 byte[] bytes = new byte[0];
137 int b = -1;
138 while( (b = b64is.read()) >= 0 ){
139 byte[] temp = new byte[ bytes.length + 1 ];
140 System.arraycopy( bytes,0, temp,0,bytes.length );
141 temp[bytes.length] = (byte)b;
142 bytes = temp;
143 } // end while: terribly inefficient way to read data
144 b64is.close();
145 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon( bytes );
146 javax.swing.JLabel jlabel = new javax.swing.JLabel( "Read from test.gif.b64", iicon,0 );
147 javax.swing.JFrame jframe = new javax.swing.JFrame();
148 jframe.getContentPane().add( jlabel );
149 jframe.pack();
150 jframe.show();
151
152 // Write raw bytes to file
153 java.io.FileOutputStream fos = new java.io.FileOutputStream( "test.gif_out" );
154 fos.write( bytes );
155 fos.close();
156
157 // Read raw bytes and encode
158 fis = new java.io.FileInputStream( "test.gif_out" );
159 b64is = new Base64.InputStream( fis, ENCODE );
160 byte[] ebytes = new byte[0];
161 b = -1;
162 while( (b = b64is.read()) >= 0 ){
163 byte[] temp = new byte[ ebytes.length + 1 ];
164 System.arraycopy( ebytes,0, temp,0,ebytes.length );
165 temp[ebytes.length] = (byte)b;
166 ebytes = temp;
167 } // end while: terribly inefficient way to read data
168 b64is.close();
169 String s = new String( ebytes );
170 javax.swing.JTextArea jta = new javax.swing.JTextArea( s );
171 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane( jta );
172 jframe = new javax.swing.JFrame();
173 jframe.setTitle( "Read from test.gif_out" );
174 jframe.getContentPane().add( jsp );
175 jframe.pack();
176 jframe.show();
177
178 // Write encoded bytes to file
179 fos = new java.io.FileOutputStream( "test.gif.b64_out" );
180 fos.write( ebytes );
181
182 // Read GIF stored in base64 form.
183 fis = new java.io.FileInputStream( "test.gif.b64_out" );
184 b64is = new Base64.InputStream( fis, DECODE );
185 byte[] edbytes = new byte[0];
186 b = -1;
187 while( (b = b64is.read()) >= 0 ){
188 byte[] temp = new byte[ edbytes.length + 1 ];
189 System.arraycopy( edbytes,0, temp,0,edbytes.length );
190 temp[edbytes.length] = (byte)b;
191 edbytes = temp;
192 } // end while: terribly inefficient way to read data
193 b64is.close();
194 iicon = new javax.swing.ImageIcon( edbytes );
195 jlabel = new javax.swing.JLabel( "Read from test.gif.b64_out", iicon,0 );
196 jframe = new javax.swing.JFrame();
197 jframe.getContentPane().add( jlabel );
198 jframe.pack();
199 jframe.show();
200 } // end: Test Input Stream
201
202
203 // Test Output Stream
204 {
205 // Read raw bytes
206 java.io.FileInputStream fis = new java.io.FileInputStream( "test.gif_out" );
207 byte[] rbytes = new byte[0];
208 int b = -1;
209 while( (b = fis.read()) >= 0 ){
210 byte[] temp = new byte[ rbytes.length + 1 ];
211 System.arraycopy( rbytes,0, temp,0,rbytes.length );
212 temp[rbytes.length] = (byte)b;
213 rbytes = temp;
214 } // end while: terribly inefficient way to read data
215 fis.close();
216
217 // Write raw bytes to encoded file
218 java.io.FileOutputStream fos = new java.io.FileOutputStream("test.gif.b64_out2");
219 Base64.OutputStream b64os = new Base64.OutputStream( fos, ENCODE );
220 b64os.write( rbytes );
221 b64os.close();
222
223
224 // Read raw bytes that are actually encoded (but we'll ignore that)
225 fis = new java.io.FileInputStream( "test.gif.b64_out2" );
226 byte[] rebytes = new byte[0];
227 b = -1;
228 while( (b = fis.read()) >= 0 ){
229 byte[] temp = new byte[ rebytes.length + 1 ];
230 System.arraycopy( rebytes,0, temp,0,rebytes.length );
231 temp[rebytes.length] = (byte)b;
232 rebytes = temp;
233 } // end while: terribly inefficient way to read data
234 fis.close();
235 String s = new String( rebytes );
236 javax.swing.JTextArea jta = new javax.swing.JTextArea( s );
237 javax.swing.JScrollPane jsp = new javax.swing.JScrollPane( jta );
238 javax.swing.JFrame jframe = new javax.swing.JFrame();
239 jframe.setTitle( "Read from test.gif.b64_out2" );
240 jframe.getContentPane().add( jsp );
241 jframe.pack();
242 jframe.show();
243
244 // Write encoded bytes to decoded raw file
245 fos = new java.io.FileOutputStream("test.gif_out2");
246 b64os = new Base64.OutputStream( fos, DECODE );
247 b64os.write( rebytes );
248 b64os.close();
249 javax.swing.ImageIcon iicon = new javax.swing.ImageIcon( "test.gif_out2" );
250 javax.swing.JLabel jlabel = new javax.swing.JLabel( "Read from test.gif_out2", iicon,0 );
251 jframe = new javax.swing.JFrame();
252 jframe.getContentPane().add( jlabel );
253 jframe.pack();
254 jframe.show();
255
256 } // end: Test Output Stream
257
258
259 // Test wagner's files
260 {
261 java.io.FileInputStream fis = new java.io.FileInputStream("D:\\temp\\testencoding.txt");
262 Base64.InputStream b64is = new Base64.InputStream( fis, DECODE );
263 java.io.FileOutputStream fos = new java.io.FileOutputStream("D:\\temp\\file.zip");
264 int b;
265 while( (b=b64is.read()) >= 0 )
266 fos.write( b );
267 fos.close();
268 b64is.close();
269
270 } // end test wagner's file
271
272 } // end try
273 catch( Exception e)
274 { e.printStackTrace();
275 }
276 } // end main
277
278
279 /* ******** E N C O D I N G M E T H O D S ******** */
280
281
282 /**
283 * Encodes the first three bytes of array <var>threeBytes</var>
284 * and returns a four-byte array in Base64 notation.
285 *
286 * @param threeBytes the array to convert
287 * @return four byte array in Base64 notation.
288 * @since 1.3
289 */
290 private static byte[] encode3to4( byte[] threeBytes )
291 { return encode3to4( threeBytes, 3 );
292 } // end encodeToBytes
293
294
295
296 /**
297 * Encodes up to the first three bytes of array <var>threeBytes</var>
298 * and returns a four-byte array in Base64 notation.
299 * The actual number of significant bytes in your array is
300 * given by <var>numSigBytes</var>.
301 * The array <var>threeBytes</var> needs only be as big as
302 * <var>numSigBytes</var>.
303 *
304 * @param threeBytes the array to convert
305 * @param numSigBytes the number of significant bytes in your array
306 * @return four byte array in Base64 notation.
307 * @since 1.3
308 */
309 private static byte[] encode3to4( byte[] threeBytes, int numSigBytes )
310 { byte[] dest = new byte[4];
311 encode3to4( threeBytes, 0, numSigBytes, dest, 0 );
312 return dest;
313 }
314
315
316
317 /**
318 * Encodes up to three bytes of the array <var>source</var>
319 * and writes the resulting four Base64 bytes to <var>destination</var>.
320 * The source and destination arrays can be manipulated
321 * anywhere along their length by specifying
322 * <var>srcOffset</var> and <var>destOffset</var>.
323 * This method does not check to make sure your arrays
324 * are large enough to accomodate <var>srcOffset</var> + 3 for
325 * the <var>source</var> array or <var>destOffset</var> + 4 for
326 * the <var>destination</var> array.
327 * The actual number of significant bytes in your array is
328 * given by <var>numSigBytes</var>.
329 *
330 * @param source the array to convert
331 * @param srcOffset the index where conversion begins
332 * @param numSigBytes the number of significant bytes in your array
333 * @param destination the array to hold the conversion
334 * @param destOffset the index where output will be put
335 * @return the <var>destination</var> array
336 * @since 1.3
337 */
338 private static byte[] encode3to4(
339 byte[] source, int srcOffset, int numSigBytes,
340 byte[] destination, int destOffset )
341 {
342 // 1 2 3
343 // 01234567890123456789012345678901 Bit position
344 // --------000000001111111122222222 Array position from threeBytes
345 // --------| || || || | Six bit groups to index ALPHABET
346 // >>18 >>12 >> 6 >> 0 Right shift necessary
347 // 0x3f 0x3f 0x3f Additional AND
348
349 // Create buffer with zero-padding if there are only one or two
350 // significant bytes passed in the array.
351 // We have to shift left 24 in order to flush out the 1's that appear
352 // when Java treats a value as negative that is cast from a byte to an int.
353 int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 )
354 | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 )
355 | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 );
356
357 switch( numSigBytes )
358 {
359 case 3:
360 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
361 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
362 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
363 destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ];
364 return destination;
365
366 case 2:
367 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
368 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
369 destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ];
370 destination[ destOffset + 3 ] = EQUALS_SIGN;
371 return destination;
372
373 case 1:
374 destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ];
375 destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ];
376 destination[ destOffset + 2 ] = EQUALS_SIGN;
377 destination[ destOffset + 3 ] = EQUALS_SIGN;
378 return destination;
379
380 default:
381 return destination;
382 } // end switch
383 } // end encode3to4
384
385 /**
386 * Serializes an object and returns the Base64-encoded
387 * version of that serialized object. If the object
388 * cannot be serialized or there is another error,
389 * the method will return <tt>null</tt>.
390 *
391 * @param serializableObject The object to encode
392 * @return The Base64-encoded object
393 * @since 1.4
394 */
395 public static String encodeObject( java.io.Serializable serializableObject )
396 {
397 return encodeObject( serializableObject, true );
398 } // end encodeObject
399
400 /**
401 * Serializes an object and returns the Base64-encoded
402 * version of that serialized object. If the object
403 * cannot be serialized or there is another error,
404 * the method will return <tt>null</tt>.
405 *
406 * @param serializableObject The object to encode
407 * @param breakLines Break lines at 80 characters or less.
408 * @return The Base64-encoded object
409 * @since 1.4
410 */
411 public static String encodeObject(java.io.Serializable serializableObject, boolean breakLines)
412 {
413 java.io.ByteArrayOutputStream baos = null;
414 java.io.OutputStream b64os = null;
415 java.io.ObjectOutputStream oos = null;
416
417 try
418 {
419 baos = new java.io.ByteArrayOutputStream();
420 b64os = new Base64.OutputStream(baos, Base64.ENCODE, breakLines);
421 oos = new java.io.ObjectOutputStream(b64os);
422
423 oos.writeObject(serializableObject);
424 } // end try
425 catch (java.io.IOException e)
426 {
427 e.printStackTrace();
428 return null;
429 } // end catch
430 finally
431 {
432 try
433 {
434 oos.close();
435 }
436 catch (Exception e)
437 {
438 // ignore it
439 }
440 try
441 {
442 b64os.close();
443 }
444 catch (Exception e)
445 {
446 // ignore it
447 }
448 try
449 {
450 baos.close();
451 }
452 catch (Exception e)
453 {
454 // ignore it
455 }
456 } // end finally
457
458 return new String(baos.toByteArray());
459 } // end encode
460
461
462 /**
463 * Encodes a byte array into Base64 notation.
464 * Equivalen to calling
465 * <code>encodeBytes( source, 0, source.length )</code>
466 *
467 * @param source The data to convert
468 * @since 1.4
469 */
470 public static String encodeBytes( byte[] source )
471 {
472 return encodeBytes( source, true );
473 } // end encodeBytes
474
475 /**
476 * Encodes a byte array into Base64 notation.
477 * Equivalen to calling
478 * <code>encodeBytes( source, 0, source.length )</code>
479 *
480 * @param source The data to convert
481 * @param breakLines Break lines at 80 characters or less.
482 * @since 1.4
483 */
484 public static String encodeBytes( byte[] source, boolean breakLines )
485 {
486 return encodeBytes( source, 0, source.length, breakLines );
487 } // end encodeBytes
488
489
490 /**
491 * Encodes a byte array into Base64 notation.
492 *
493 * @param source The data to convert
494 * @param off Offset in array where conversion should begin
495 * @param len Length of data to convert
496 * @since 1.4
497 */
498 public static String encodeBytes( byte[] source, int off, int len )
499 {
500 return encodeBytes( source, off, len, true );
501 } // end encodeBytes
502
503
504 /**
505 * Encodes a byte array into Base64 notation.
506 *
507 * @param source The data to convert
508 * @param off Offset in array where conversion should begin
509 * @param len Length of data to convert
510 * @param breakLines Break lines at 80 characters or less.
511 * @since 1.4
512 */
513 public static String encodeBytes( byte[] source, int off, int len, boolean breakLines )
514 {
515 int len43 = len * 4 / 3;
516 byte[] outBuff = new byte[ ( len43 ) // Main 4:3
517 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding
518 + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines
519 int d = 0;
520 int e = 0;
521 int len2 = len - 2;
522 int lineLength = 0;
523 for( ; d < len2; d+=3, e+=4 )
524 {
525 encode3to4( source, d+off, 3, outBuff, e );
526
527 lineLength += 4;
528 if( breakLines && lineLength == MAX_LINE_LENGTH )
529 {
530 outBuff[e+4] = NEW_LINE;
531 e++;
532 lineLength = 0;
533 } // end if: end of line
534 } // en dfor: each piece of array
535
536 if( d < len )
537 {
538 encode3to4( source, d+off, len - d, outBuff, e );
539 e += 4;
540 } // end if: some padding needed
541
542 return new String( outBuff, 0, e );
543 } // end encodeBytes
544
545
546 /**
547 * Encodes a string in Base64 notation with line breaks
548 * after every 75 Base64 characters.
549 *
550 * @param s the string to encode
551 * @return the encoded string
552 * @since 1.3
553 */
554 public static String encodeString( String s )
555 {
556 return encodeString( s, true );
557 } // end encodeString
558
559 /**
560 * Encodes a string in Base64 notation with line breaks
561 * after every 75 Base64 characters.
562 *
563 * @param s the string to encode
564 * @param breakLines Break lines at 80 characters or less.
565 * @return the encoded string
566 * @since 1.3
567 */
568 public static String encodeString( String s, boolean breakLines )
569 {
570 return encodeBytes( s.getBytes(), breakLines );
571 } // end encodeString
572
573
574
575
576 /* ******** D E C O D I N G M E T H O D S ******** */
577
578
579 /**
580 * Decodes the first four bytes of array <var>fourBytes</var>
581 * and returns an array up to three bytes long with the
582 * decoded values.
583 *
584 * @param fourBytes the array with Base64 content
585 * @return array with decoded values
586 * @since 1.3
587 */
588 private static byte[] decode4to3( byte[] fourBytes )
589 {
590 byte[] outBuff1 = new byte[3];
591 int count = decode4to3( fourBytes, 0, outBuff1, 0 );
592 byte[] outBuff2 = new byte[ count ];
593
594 System.arraycopy( outBuff1, 0, outBuff2, 0, count );
595 return outBuff2;
596 }
597
598
599
600
601 /**
602 * Decodes four bytes from array <var>source</var>
603 * and writes the resulting bytes (up to three of them)
604 * to <var>destination</var>.
605 * The source and destination arrays can be manipulated
606 * anywhere along their length by specifying
607 * <var>srcOffset</var> and <var>destOffset</var>.
608 * This method does not check to make sure your arrays
609 * are large enough to accomodate <var>srcOffset</var> + 4 for
610 * the <var>source</var> array or <var>destOffset</var> + 3 for
611 * the <var>destination</var> array.
612 * This method returns the actual number of bytes that
613 * were converted from the Base64 encoding.
614 *
615 *
616 * @param source the array to convert
617 * @param srcOffset the index where conversion begins
618 * @param destination the array to hold the conversion
619 * @param destOffset the index where output will be put
620 * @return the number of decoded bytes converted
621 * @since 1.3
622 */
623 private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset )
624 {
625 // Example: Dk==
626 if( source[ srcOffset + 2] == EQUALS_SIGN )
627 {
628 // Two ways to do the same thing. Don't know which way I like best.
629 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
630 // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
631 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
632 | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 );
633
634 destination[ destOffset ] = (byte)( outBuff >>> 16 );
635 return 1;
636 }
637
638 // Example: DkL=
639 else if( source[ srcOffset + 3 ] == EQUALS_SIGN )
640 {
641 // Two ways to do the same thing. Don't know which way I like best.
642 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
643 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
644 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 );
645 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
646 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
647 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 );
648
649 destination[ destOffset ] = (byte)( outBuff >>> 16 );
650 destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 );
651 return 2;
652 }
653
654 // Example: DkLE
655 else
656 {
657 try{
658 // Two ways to do the same thing. Don't know which way I like best.
659 //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
660 // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 )
661 // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 )
662 // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 );
663 int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 )
664 | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 )
665 | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6)
666 | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) );
667
668
669 destination[ destOffset ] = (byte)( outBuff >> 16 );
670 destination[ destOffset + 1 ] = (byte)( outBuff >> 8 );
671 destination[ destOffset + 2 ] = (byte)( outBuff );
672
673 return 3;
674 }catch( Exception e){
675 LoggerFactory.getDefaultLogger().error(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) );
676 LoggerFactory.getDefaultLogger().error(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) );
677 LoggerFactory.getDefaultLogger().error(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) );
678 LoggerFactory.getDefaultLogger().error(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) );
679 return -1;
680 } // end catch
681 }
682 } // end decodeToBytes
683
684
685
686 /**
687 * Decodes data from Base64 notation.
688 *
689 * @param s the string to decode
690 * @return the decoded data
691 * @since 1.4
692 */
693 public static byte[] decode( String s )
694 {
695 byte[] bytes = s.getBytes();
696 return decode( bytes, 0, bytes.length );
697 } // end decode
698
699
700 /**
701 * Decodes data from Base64 notation and
702 * returns it as a string.
703 * Equivlaent to calling
704 * <code>new String( decode( s ) )</code>
705 *
706 * @param s the strind to decode
707 * @return The data as a string
708 * @since 1.4
709 */
710 public static String decodeToString( String s )
711 { return new String( decode( s ) );
712 } // end decodeToString
713
714
715 /**
716 * Attempts to decode Base64 data and deserialize a Java
717 * Object within. Returns <tt>null if there was an error.
718 *
719 * @param encodedObject The Base64 data to decode
720 * @return The decoded and deserialized object
721 * @since 1.4
722 */
723 public static Object decodeToObject(String encodedObject)
724 {
725 byte[] objBytes = decode(encodedObject);
726
727 java.io.ByteArrayInputStream bais = null;
728 java.io.ObjectInputStream ois = null;
729
730 try
731 {
732 bais = new java.io.ByteArrayInputStream(objBytes);
733 ois = new java.io.ObjectInputStream(bais);
734
735 return ois.readObject();
736 } // end try
737 catch (java.io.IOException e)
738 {
739 e.printStackTrace();
740 return null;
741 } // end catch
742 catch (java.lang.ClassNotFoundException e)
743 {
744 e.printStackTrace();
745 return null;
746 } // end catch
747 finally
748 {
749 try
750 {
751 bais.close();
752 }
753 catch (Exception e)
754 {
755 // ignore it
756 }
757 try
758 {
759 ois.close();
760 }
761 catch (Exception e)
762 {
763 // ignore it
764 }
765 } // end finally
766 } // end decodeObject
767
768
769 /**
770 * Decodes Base64 content in byte array format and returns
771 * the decoded byte array.
772 *
773 * @param source The Base64 encoded data
774 * @param off The offset of where to begin decoding
775 * @param len The length of characters to decode
776 * @return decoded data
777 * @since 1.3
778 */
779 public static byte[] decode( byte[] source, int off, int len )
780 {
781 int len34 = len * 3 / 4;
782 byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output
783 int outBuffPosn = 0;
784
785 byte[] b4 = new byte[4];
786 int b4Posn = 0;
787 int i = 0;
788 byte sbiCrop = 0;
789 byte sbiDecode = 0;
790 for( i = 0; i < len; i++ )
791 {
792 sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
793 sbiDecode = DECODABET[ sbiCrop ];
794
795 if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better
796 {
797 if( sbiDecode >= EQUALS_SIGN_ENC )
798 {
799 b4[ b4Posn++ ] = sbiCrop;
800 if( b4Posn > 3 )
801 {
802 outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn );
803 b4Posn = 0;
804
805 // If that was the equals sign, break out of 'for' loop
806 if( sbiCrop == EQUALS_SIGN )
807 break;
808 } // end if: quartet built
809
810 } // end if: equals sign or better
811
812 } // end if: white space, equals sign or better
813 else
814 {
815 LoggerFactory.getDefaultLogger().error( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" );
816 return null;
817 } // end else:
818 } // each input character
819
820 byte[] out = new byte[ outBuffPosn ];
821 System.arraycopy( outBuff, 0, out, 0, outBuffPosn );
822 return out;
823 } // end decode
824
825
826
827
828 /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
829
830
831
832 /**
833 * A {@link Base64.InputStream} will read data from another
834 * {@link java.io.InputStream}, given in the constructor,
835 * and encode/decode to/from Base64 notation on the fly.
836 *
837 * @see Base64
838 * @see java.io.FilterInputStream
839 * @since 1.3
840 */
841 public static class InputStream extends java.io.FilterInputStream
842 {
843 private boolean encode; // Encoding or decoding
844 private int position; // Current position in the buffer
845 private byte[] buffer; // Small buffer holding converted data
846 private int bufferLength; // Length of buffer (3 or 4)
847 private int numSigBytes; // Number of meaningful bytes in the buffer
848 private int lineLength;
849 private boolean breakLines; // Break lines at less than 80 characters
850
851
852 /**
853 * Constructs a {@link Base64.InputStream} in DECODE mode.
854 *
855 * @param in the {@link java.io.InputStream} from which to read data.
856 * @since 1.3
857 */
858 public InputStream( java.io.InputStream in )
859 {
860 this( in, Base64.DECODE );
861 } // end constructor
862
863
864 /**
865 * Constructs a {@link Base64.InputStream} in
866 * either ENCODE or DECODE mode.
867 *
868 * @param in the {@link java.io.InputStream} from which to read data.
869 * @param encode Conversion direction
870 * @see Base64#ENCODE
871 * @see Base64#DECODE
872 * @since 1.3
873 */
874 public InputStream( java.io.InputStream in, boolean encode )
875 {
876 this( in, encode, true );
877 } // end constructor
878
879
880 /**
881 * Constructs a {@link Base64.InputStream} in
882 * either ENCODE or DECODE mode.
883 *
884 * @param in the {@link java.io.InputStream} from which to read data.
885 * @param encode Conversion direction
886 * @param breakLines Break lines at less than 80 characters.
887 * @see Base64#ENCODE
888 * @see Base64#DECODE
889 * @since 1.3
890 */
891 public InputStream( java.io.InputStream in, boolean encode, boolean breakLines )
892 {
893 super( in );
894 this.breakLines = breakLines;
895 this.encode = encode;
896 this.bufferLength = encode ? 4 : 3;
897 this.buffer = new byte[ bufferLength ];
898 this.position = -1;
899 this.lineLength = 0;
900 } // end constructor
901
902 /**
903 * Reads enough of the input stream to convert
904 * to/from Base64 and returns the next byte.
905 *
906 * @return next byte
907 * @since 1.3
908 */
909 public int read() throws java.io.IOException
910 {
911 // Do we need to get data?
912 if( position < 0 )
913 {
914 if( encode )
915 {
916 byte[] b3 = new byte[3];
917 int numBinaryBytes = 0;
918 for( int i = 0; i < 3; i++ )
919 {
920 try
921 {
922 int b = in.read();
923
924 // If end of stream, b is -1.
925 if( b >= 0 )
926 {
927 b3[i] = (byte)b;
928 numBinaryBytes++;
929 } // end if: not end of stream
930
931 } // end try: read
932 catch( java.io.IOException e )
933 {
934 // Only a problem if we got no data at all.
935 if( i == 0 )
936 throw e;
937
938 } // end catch
939 } // end for: each needed input byte
940
941 if( numBinaryBytes > 0 )
942 {
943 encode3to4( b3, 0, numBinaryBytes, buffer, 0 );
944 position = 0;
945 numSigBytes = 4;
946 } // end if: got data
947 else
948 {
949 return -1;
950 } // end else
951 } // end if: encoding
952
953 // Else decoding
954 else
955 {
956 byte[] b4 = new byte[4];
957 int i = 0;
958 for( i = 0; i < 4; i++ )
959 {
960 // Read four "meaningful" bytes:
961 int b = 0;
962 do{ b = in.read(); }
963 while( b >= 0 && DECODABET[ b & 0x7f ] <= WHITE_SPACE_ENC );
964
965 if( b < 0 )
966 break; // Reads a -1 if end of stream
967
968 b4[i] = (byte)b;
969 } // end for: each needed input byte
970
971 if( i == 4 )
972 {
973 numSigBytes = decode4to3( b4, 0, buffer, 0 );
974 position = 0;
975 } // end if: got four characters
976 else if( i == 0 ){
977 return -1;
978 } // end else if: also padded correctly
979 else
980 {
981 // Must have broken out from above.
982 throw new java.io.IOException( "Improperly padded Base64 input." );
983 } // end
984
985 } // end else: decode
986 } // end else: get data
987
988 // Got data?
989 if( position >= 0 )
990 {
991 // End of relevant data?
992 if( /*!encode &&*/ position >= numSigBytes )
993 return -1;
994
995 if( encode && breakLines && lineLength >= MAX_LINE_LENGTH )
996 {
997 lineLength = 0;
998 return '\n';
999 } // end if
1000 else
1001 {
1002 lineLength++; // This isn't important when decoding
1003 // but throwing an extra "if" seems
1004 // just as wasteful.
1005
1006 int b = buffer[ position++ ];
1007
1008 if( position >= bufferLength )
1009 position = -1;
1010
1011 return b & 0xFF; // This is how you "cast" a byte that's
1012 // intended to be unsigned.
1013 } // end else
1014 } // end if: position >= 0
1015
1016 // Else error
1017 else
1018 {
1019 // When JDK1.4 is more accepted, use an assertion here.
1020 throw new java.io.IOException( "Error in Base64 code reading stream." );
1021 } // end else
1022 } // end read
1023
1024
1025 /**
1026 * Calls {@link #read} repeatedly until the end of stream
1027 * is reached or <var>len</var> bytes are read.
1028 * Returns number of bytes read into array or -1 if
1029 * end of stream is encountered.
1030 *
1031 * @param dest array to hold values
1032 * @param off offset for array
1033 * @param len max number of bytes to read into array
1034 * @return bytes read into array or -1 if end of stream is encountered.
1035 * @since 1.3
1036 */
1037 public int read( byte[] dest, int off, int len ) throws java.io.IOException
1038 {
1039 int i;
1040 int b;
1041 for( i = 0; i < len; i++ )
1042 {
1043 b = read();
1044
1045 //if( b < 0 && i == 0 )
1046 // return -1;
1047
1048 if( b >= 0 )
1049 dest[off + i] = (byte)b;
1050 else if( i == 0 )
1051 return -1;
1052 else
1053 break; // Out of 'for' loop
1054 } // end for: each byte read
1055 return i;
1056 } // end read
1057
1058 } // end inner class InputStream
1059
1060
1061
1062
1063
1064
1065 /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */
1066
1067
1068
1069 /**
1070 * A {@link Base64.OutputStream} will write data to another
1071 * {@link java.io.OutputStream}, given in the constructor,
1072 * and encode/decode to/from Base64 notation on the fly.
1073 *
1074 * @see Base64
1075 * @see java.io.FilterOutputStream
1076 * @since 1.3
1077 */
1078 public static class OutputStream extends java.io.FilterOutputStream
1079 {
1080 private boolean encode;
1081 private int position;
1082 private byte[] buffer;
1083 private int bufferLength;
1084 private int lineLength;
1085 private boolean breakLines;
1086
1087
1088 /**
1089 * Constructs a {@link Base64.OutputStream} in ENCODE mode.
1090 *
1091 * @param out the {@link java.io.OutputStream} to which data will be written.
1092 * @since 1.3
1093 */
1094 public OutputStream( java.io.OutputStream out )
1095 {
1096 this( out, Base64.ENCODE );
1097 } // end constructor
1098
1099
1100 /**
1101 * Constructs a {@link Base64.OutputStream} in
1102 * either ENCODE or DECODE mode.
1103 *
1104 * @param out the {@link java.io.OutputStream} to which data will be written.
1105 * @param encode Conversion direction
1106 * @see Base64#ENCODE
1107 * @see Base64#DECODE
1108 * @since 1.3
1109 */
1110 public OutputStream( java.io.OutputStream out, boolean encode )
1111 {
1112 this( out, encode, true );
1113 } // end constructor
1114
1115
1116 /**
1117 * Constructs a {@link Base64.OutputStream} in
1118 * either ENCODE or DECODE mode.
1119 *
1120 * @param out the {@link java.io.OutputStream} to which data will be written.
1121 * @param encode Conversion direction
1122 * @param breakLines Break lines to be less than 80 characters.
1123 * @see Base64#ENCODE
1124 * @see Base64#DECODE
1125 * @since 1.3
1126 */
1127 public OutputStream( java.io.OutputStream out, boolean encode, boolean breakLines )
1128 {
1129 super( out );
1130 this.breakLines = breakLines;
1131 this.encode = encode;
1132 this.bufferLength = encode ? 3 : 4;
1133 this.buffer = new byte[ bufferLength ];
1134 this.position = 0;
1135 this.lineLength = 0;
1136 } // end constructor
1137
1138
1139 /**
1140 * Writes the byte to the output stream after
1141 * converting to/from Base64 notation.
1142 * When encoding, bytes are buffered three
1143 * at a time before the output stream actually
1144 * gets a write() call.
1145 * When decoding, bytes are buffered four
1146 * at a time.
1147 *
1148 * @param theByte the byte to write
1149 * @since 1.3
1150 */
1151 public void write(int theByte) throws java.io.IOException
1152 {
1153 if( encode )
1154 {
1155 buffer[ position++ ] = (byte)theByte;
1156 if( position >= bufferLength ) // Enough to encode.
1157 {
1158 out.write( Base64.encode3to4( buffer, bufferLength ) );
1159
1160 lineLength += 4;
1161 if( breakLines && lineLength >= MAX_LINE_LENGTH )
1162 {
1163 out.write( NEW_LINE );
1164 lineLength = 0;
1165 } // end if: end of line
1166
1167 position = 0;
1168 } // end if: enough to output
1169 } // end if: encoding
1170
1171 // Else, Decoding
1172 else
1173 {
1174 // Meaningful Base64 character?
1175 if( DECODABET[ theByte & 0x7f ] > WHITE_SPACE_ENC )
1176 {
1177 buffer[ position++ ] = (byte)theByte;
1178 if( position >= bufferLength ) // Enough to output.
1179 {
1180 out.write( Base64.decode4to3( buffer ) );
1181 position = 0;
1182 } // end if: enough to output
1183 } // end if: meaningful base64 character
1184 else if( DECODABET[ theByte & 0x7f ] != WHITE_SPACE_ENC )
1185 {
1186 throw new java.io.IOException( "Invalid character in Base64 data." );
1187 } // end else: not white space either
1188 } // end else: decoding
1189 } // end write
1190
1191
1192
1193 /**
1194 * Calls {@link #write} repeatedly until <var>len</var>
1195 * bytes are written.
1196 *
1197 * @param theBytes array from which to read bytes
1198 * @param off offset for array
1199 * @param len max number of bytes to read into array
1200 * @since 1.3
1201 */
1202 public void write( byte[] theBytes, int off, int len ) throws java.io.IOException
1203 {
1204 for( int i = 0; i < len; i++ )
1205 {
1206 write( theBytes[ off + i ] );
1207 } // end for: each byte written
1208
1209 } // end write
1210
1211
1212 /**
1213 * Appropriately pads Base64 notation when encoding
1214 * or throws an exception if Base64 input is not
1215 * properly padded when decoding.
1216 *
1217 * @since 1.3
1218 */
1219 public void flush() throws java.io.IOException
1220 {
1221 super.flush();
1222
1223 if( position > 0 )
1224 {
1225 if( encode )
1226 {
1227 out.write( Base64.encode3to4( buffer, position ) );
1228 } // end if: encoding
1229 else
1230 {
1231 throw new java.io.IOException( "Base64 input not properly padded." );
1232 } // end else: decoding
1233 } // end if: buffer partially full
1234
1235 out.flush();
1236 } // end flush
1237
1238
1239 /**
1240 * Flushes and closes stream.
1241 *
1242 * @since 1.3
1243 */
1244 public void close() throws java.io.IOException
1245 {
1246 super.close();
1247 //this.flush();
1248
1249 out.close();
1250
1251 buffer = null;
1252 out = null;
1253 } // end close
1254
1255 } // end inner class OutputStream
1256
1257
1258 } // end class Base64