1 package org.apache.maven.util;
2
3 /*
4 * ====================================================================
5 * Copyright 2001-2004 The Apache Software Foundation.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8 * use this file except in compliance with the License. You may obtain a copy
9 * of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16 * License for the specific language governing permissions and limitations
17 * under the License.
18 * ====================================================================
19 */
20
21 import java.io.ByteArrayOutputStream;
22
23 // import org.apache.commons.logging.Log;
24 // import org.apache.commons.logging.LogFactory;
25
26 /**
27 * Encode/Decode Base-64.
28 *
29 * @author John Casey
30 */
31 public final class Base64
32 {
33
34 // private static final Log LOG = LogFactory.getLog( Base64.class );
35
36 private static final String CRLF = System.getProperty( "line.separator" );
37
38 private static final int LINE_END = 64;
39
40 public static String encode( byte[] data )
41 {
42 return Base64.encode( data, true );
43 }
44
45 public static String encode( byte[] data, boolean useLineDelimiter )
46 {
47 if ( data == null )
48 {
49 return null;
50 }
51 else if ( data.length == 0 )
52 {
53 return "";
54 }
55
56 int padding = 3 - ( data.length % 3 );
57
58 // if ( LOG.isDebugEnabled() )
59 // {
60 // LOG.debug( "padding = " + padding + "characters." );
61 // }
62
63 StringBuffer buffer = new StringBuffer();
64
65 for ( int i = 0; i < data.length; i += 3 )
66 {
67 // if ( LOG.isDebugEnabled() )
68 // {
69 // LOG.debug( "iteration base offset = " + i );
70 // }
71
72 int neutral = ( data[i] < 0 ? data[i] + 256 : data[i] );
73
74 int block = ( neutral & 0xff );
75 // if ( LOG.isDebugEnabled() )
76 // {
77 // LOG.debug( "after first byte, block = " + Integer.toBinaryString( block ) );
78 // }
79
80 boolean inLastSegment = false;
81
82 block <<= 8;
83 if ( i + 1 < data.length )
84 {
85 neutral = ( data[i + 1] < 0 ? data[i + 1] + 256 : data[i + 1] );
86 block |= ( neutral & 0xff );
87 }
88 else
89 {
90 inLastSegment = true;
91 }
92 // if ( LOG.isDebugEnabled() )
93 // {
94 // LOG.debug( "after second byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = "
95 // + inLastSegment );
96 // }
97
98 block <<= 8;
99 if ( i + 2 < data.length )
100 {
101 neutral = ( data[i + 2] < 0 ? data[i + 2] + 256 : data[i + 2] );
102 block |= ( neutral & 0xff );
103 }
104 else
105 {
106 inLastSegment = true;
107 }
108 // if ( LOG.isDebugEnabled() )
109 // {
110 // LOG.debug( "after third byte, block = " + Integer.toBinaryString( block ) + "; inLastSegment = "
111 // + inLastSegment );
112 // }
113
114 char[] encoded = new char[4];
115
116 encoded[0] = toBase64Char( ( block >>> 18 ) & 0x3f );
117 // if ( LOG.isDebugEnabled() )
118 // {
119 // LOG.debug( "first character = " + encoded[0] );
120 // }
121
122 encoded[1] = toBase64Char( ( block >>> 12 ) & 0x3f );
123 // if ( LOG.isDebugEnabled() )
124 // {
125 // LOG.debug( "second character = " + encoded[1] );
126 // }
127
128 if ( inLastSegment && ( padding > 1 ) )
129 {
130 encoded[2] = '=';
131 }
132 else
133 {
134 encoded[2] = toBase64Char( ( block >>> 6 ) & 0x3f );
135 }
136 // if ( LOG.isDebugEnabled() )
137 // {
138 // LOG.debug( "third character = " + encoded[2] );
139 // }
140
141 if ( inLastSegment && ( padding > 0 ) )
142 {
143 encoded[3] = '=';
144 }
145 else
146 {
147 encoded[3] = toBase64Char( block & 0x3f );
148 }
149 // if ( LOG.isDebugEnabled() )
150 // {
151 // LOG.debug( "fourth character = " + encoded[3] );
152 // }
153
154 buffer.append( encoded );
155 }
156
157 if ( useLineDelimiter )
158 {
159 return canonicalize( buffer.toString() );
160 }
161 else
162 {
163 return buffer.toString();
164 }
165 }
166
167 public static byte[] decode( String src )
168 {
169 return Base64.decode( src, true );
170 }
171
172 public static byte[] decode( String src, boolean useLineDelimiter )
173 {
174 if ( src == null )
175 {
176 return null;
177 }
178 else if ( src.length() < 1 )
179 {
180 return new byte[0];
181 }
182
183 // if ( LOG.isDebugEnabled() )
184 // {
185 // LOG.debug( "pre-canonicalization = \n" + src );
186 // }
187 String data = src;
188
189 if ( useLineDelimiter )
190 {
191 data = deCanonicalize( src );
192 }
193 // if ( LOG.isDebugEnabled() )
194 // {
195 // LOG.debug( "post-canonicalization = \n" + data );
196 // }
197
198 ByteArrayOutputStream baos = new ByteArrayOutputStream();
199
200 char[] input = data.toCharArray();
201
202 for ( int i = 0; i < input.length; i += 4 )
203 {
204 // if ( LOG.isDebugEnabled() )
205 // {
206 // LOG.debug( "iteration base offset = " + i );
207 // }
208
209 int block = ( toBase64Int( input[i] ) & 0x3f );
210 // if ( LOG.isDebugEnabled() )
211 // {
212 // LOG.debug( "block after first char [" + input[i] + "] = " + Integer.toBinaryString( block ) );
213 // }
214
215 block <<= 6;
216 block |= ( toBase64Int( input[i + 1] ) & 0x3f );
217 // if ( LOG.isDebugEnabled() )
218 // {
219 // LOG.debug( "block after second char [" + input[i + 1] + "] = " + Integer.toBinaryString( block ) );
220 // }
221
222 boolean inPadding = false;
223 boolean twoCharPadding = false;
224 block <<= 6;
225 if ( input[i + 2] != '=' )
226 {
227 block |= ( toBase64Int( input[i + 2] ) & 0x3f );
228 }
229 else
230 {
231 twoCharPadding = true;
232 inPadding = true;
233 }
234
235 // if ( LOG.isDebugEnabled() )
236 // {
237 // LOG.debug( "block after third char [" + input[i + 2] + "] = " + Integer.toBinaryString( block ) );
238 // }
239
240 block <<= 6;
241 if ( input[i + 3] != '=' )
242 {
243 block |= ( toBase64Int( input[i + 3] ) & 0x3f );
244 }
245 else
246 {
247 inPadding = true;
248 }
249
250 // if ( LOG.isDebugEnabled() )
251 // {
252 // LOG.debug( "block after fourth char [" + input[i + 3] + "] = " + Integer.toBinaryString( block ) );
253 // }
254
255 baos.write( ( block >>> 16 ) & 0xff );
256 // if ( LOG.isDebugEnabled() )
257 // {
258 // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 16 ) & 0xff ) );
259 // }
260
261 if ( !inPadding || !twoCharPadding )
262 {
263 baos.write( ( block >>> 8 ) & 0xff );
264 // if ( LOG.isDebugEnabled() )
265 // {
266 // LOG.debug( "byte[" + ( index++ ) + "] = " + ( ( block >>> 8 ) & 0xff ) );
267 // }
268 }
269
270 if ( !inPadding )
271 {
272 baos.write( block & 0xff );
273 // if ( LOG.isDebugEnabled() )
274 // {
275 // LOG.debug( "byte[" + ( index++ ) + "] = " + ( block & 0xff ) );
276 // }
277 }
278 }
279
280 byte[] result = baos.toByteArray();
281 // if ( LOG.isDebugEnabled() )
282 // {
283 // LOG.debug( "byte array is " + result.length + " bytes long." );
284 // }
285
286 return result;
287 }
288
289 private static char toBase64Char( int input )
290 {
291 if ( ( input > -1 ) && ( input < 26 ) )
292 {
293 return (char) ( 'A' + input );
294 }
295 else if ( ( input > 25 ) && ( input < 52 ) )
296 {
297 return (char) ( 'a' + input - 26 );
298 }
299 else if ( ( input > 51 ) && ( input < 62 ) )
300 {
301 return (char) ( '0' + input - 52 );
302 }
303 else if ( input == 62 )
304 {
305 return '+';
306 }
307 else if ( input == 63 )
308 {
309 return '/';
310 }
311 else
312 {
313 return '?';
314 }
315 }
316
317 private static int toBase64Int( char input )
318 {
319 if ( ( input >= 'A' ) && ( input <= 'Z' ) )
320 {
321 return input - 'A';
322 }
323 else if ( ( input >= 'a' ) && ( input <= 'z' ) )
324 {
325 return input + 26 - 'a';
326 }
327 else if ( ( input >= '0' ) && ( input <= '9' ) )
328 {
329 return input + 52 - '0';
330 }
331 else if ( input == '+' )
332 {
333 return 62;
334 }
335 else if ( input == '/' )
336 {
337 return 63;
338 }
339 else
340 {
341 return 0;
342 }
343 }
344
345 private static String deCanonicalize( String data )
346 {
347 if ( data == null )
348 {
349 return null;
350 }
351
352 StringBuffer buffer = new StringBuffer( data.length() );
353 for ( int i = 0; i < data.length(); i++ )
354 {
355 char c = data.charAt( i );
356 if ( ( c != '\r' ) && ( c != '\n' ) )
357 {
358 buffer.append( c );
359 }
360 }
361
362 return buffer.toString();
363 }
364
365 private static String canonicalize( String data )
366 {
367 StringBuffer buffer = new StringBuffer( (int) ( data.length() * 1.1 ) );
368
369 int col = 0;
370 for ( int i = 0; i < data.length(); i++ )
371 {
372 if ( col == LINE_END )
373 {
374 buffer.append( CRLF );
375 col = 0;
376 }
377
378 buffer.append( data.charAt( i ) );
379 col++;
380 }
381
382 buffer.append( CRLF );
383
384 return buffer.toString();
385 }
386
387 }