1 package org.apache.maven.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.io.ByteArrayOutputStream;
22
23
24
25
26 /**
27 * Encode/Decode Base-64.
28 *
29 * @author John Casey
30 */
31 public final class Base64
32 {
33
34
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
59
60
61
62
63 StringBuffer buffer = new StringBuffer();
64
65 for ( int i = 0; i < data.length; i += 3 )
66 {
67
68
69
70
71
72 int neutral = ( data[i] < 0 ? data[i] + 256 : data[i] );
73
74 int block = ( neutral & 0xff );
75
76
77
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
93
94
95
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
109
110
111
112
113
114 char[] encoded = new char[4];
115
116 encoded[0] = toBase64Char( ( block >>> 18 ) & 0x3f );
117
118
119
120
121
122 encoded[1] = toBase64Char( ( block >>> 12 ) & 0x3f );
123
124
125
126
127
128 if ( inLastSegment && ( padding > 1 ) )
129 {
130 encoded[2] = '=';
131 }
132 else
133 {
134 encoded[2] = toBase64Char( ( block >>> 6 ) & 0x3f );
135 }
136
137
138
139
140
141 if ( inLastSegment && ( padding > 0 ) )
142 {
143 encoded[3] = '=';
144 }
145 else
146 {
147 encoded[3] = toBase64Char( block & 0x3f );
148 }
149
150
151
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
184
185
186
187 String data = src;
188
189 if ( useLineDelimiter )
190 {
191 data = deCanonicalize( src );
192 }
193
194
195
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
205
206
207
208
209 int block = ( toBase64Int( input[i] ) & 0x3f );
210
211
212
213
214
215 block <<= 6;
216 block |= ( toBase64Int( input[i + 1] ) & 0x3f );
217
218
219
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
236
237
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
251
252
253
254
255 baos.write( ( block >>> 16 ) & 0xff );
256
257
258
259
260
261 if ( !inPadding || !twoCharPadding )
262 {
263 baos.write( ( block >>> 8 ) & 0xff );
264
265
266
267
268 }
269
270 if ( !inPadding )
271 {
272 baos.write( block & 0xff );
273
274
275
276
277 }
278 }
279
280 byte[] result = baos.toByteArray();
281
282
283
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 }