21 | * I am placing this code in the Public Domain. Do with it as you will. 22 | * This software comes with no guarantees or warranties but with 23 | * plenty of well-wishing instead! 24 | * Please visit 25 | * http://iharder.net/xmlizable 26 | * periodically to check for updates or to contribute improvements. 27 | *
28 | * 29 | * @author Robert Harder 30 | * @author rharder@usa.net 31 | * @version 1.3 32 | */ 33 | 34 | /** 35 | * Base64 converter class. This code is not a complete MIME encoder; 36 | * it simply converts binary data to base64 data and back. 37 | * 38 | *Note {@link CharBase64} is a GWT-compatible implementation of this
39 | * class.
40 | */
41 | public class Base64 {
42 | /** Specify encoding (value is {@code true}). */
43 | public final static boolean ENCODE = true;
44 |
45 | /** Specify decoding (value is {@code false}). */
46 | public final static boolean DECODE = false;
47 |
48 | /** The equals sign (=) as a byte. */
49 | private final static byte EQUALS_SIGN = (byte) '=';
50 |
51 | /** The new line character (\n) as a byte. */
52 | private final static byte NEW_LINE = (byte) '\n';
53 |
54 | /**
55 | * The 64 valid Base64 values.
56 | */
57 | private final static byte[] ALPHABET =
58 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
59 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
60 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
61 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
62 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
63 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
64 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
65 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
66 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
67 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
68 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
69 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
70 | (byte) '9', (byte) '+', (byte) '/'};
71 |
72 | /**
73 | * The 64 valid web safe Base64 values.
74 | */
75 | private final static byte[] WEBSAFE_ALPHABET =
76 | {(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F',
77 | (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K',
78 | (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P',
79 | (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U',
80 | (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z',
81 | (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e',
82 | (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j',
83 | (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o',
84 | (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't',
85 | (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y',
86 | (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3',
87 | (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8',
88 | (byte) '9', (byte) '-', (byte) '_'};
89 |
90 | /**
91 | * Translates a Base64 value to either its 6-bit reconstruction value
92 | * or a negative number indicating some other meaning.
93 | **/
94 | private final static byte[] DECODABET = {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
95 | -5, -5, // Whitespace: Tab and Linefeed
96 | -9, -9, // Decimal 11 - 12
97 | -5, // Whitespace: Carriage Return
98 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
99 | -9, -9, -9, -9, -9, // Decimal 27 - 31
100 | -5, // Whitespace: Space
101 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
102 | 62, // Plus sign at decimal 43
103 | -9, -9, -9, // Decimal 44 - 46
104 | 63, // Slash at decimal 47
105 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
106 | -9, -9, -9, // Decimal 58 - 60
107 | -1, // Equals sign at decimal 61
108 | -9, -9, -9, // Decimal 62 - 64
109 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
110 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
111 | -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
112 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
113 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
114 | -9, -9, -9, -9, -9 // Decimal 123 - 127
115 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
116 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
117 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
118 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
119 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
120 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
121 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
122 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
123 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
124 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
125 | };
126 |
127 | /** The web safe decodabet */
128 | private final static byte[] WEBSAFE_DECODABET =
129 | {-9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8
130 | -5, -5, // Whitespace: Tab and Linefeed
131 | -9, -9, // Decimal 11 - 12
132 | -5, // Whitespace: Carriage Return
133 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
134 | -9, -9, -9, -9, -9, // Decimal 27 - 31
135 | -5, // Whitespace: Space
136 | -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 44
137 | 62, // Dash '-' sign at decimal 45
138 | -9, -9, // Decimal 46-47
139 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
140 | -9, -9, -9, // Decimal 58 - 60
141 | -1, // Equals sign at decimal 61
142 | -9, -9, -9, // Decimal 62 - 64
143 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
144 | 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
145 | -9, -9, -9, -9, // Decimal 91-94
146 | 63, // Underscore '_' at decimal 95
147 | -9, // Decimal 96
148 | 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
149 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
150 | -9, -9, -9, -9, -9 // Decimal 123 - 127
151 | /* ,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 128 - 139
152 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152
153 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165
154 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178
155 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191
156 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204
157 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217
158 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230
159 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243
160 | -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */
161 | };
162 |
163 | // Indicates white space in encoding
164 | private final static byte WHITE_SPACE_ENC = -5;
165 | // Indicates equals sign in encoding
166 | private final static byte EQUALS_SIGN_ENC = -1;
167 |
168 | /** Defeats instantiation. */
169 | private Base64() {
170 | }
171 |
172 | /* ******** E N C O D I N G M E T H O D S ******** */
173 |
174 | /**
175 | * Encodes up to three bytes of the array source
176 | * and writes the resulting four Base64 bytes to destination.
177 | * The source and destination arrays can be manipulated
178 | * anywhere along their length by specifying
179 | * srcOffset and destOffset.
180 | * This method does not check to make sure your arrays
181 | * are large enough to accommodate srcOffset + 3 for
182 | * the source array or destOffset + 4 for
183 | * the destination array.
184 | * The actual number of significant bytes in your array is
185 | * given by numSigBytes.
186 | *
187 | * @param source the array to convert
188 | * @param srcOffset the index where conversion begins
189 | * @param numSigBytes the number of significant bytes in your array
190 | * @param destination the array to hold the conversion
191 | * @param destOffset the index where output will be put
192 | * @param alphabet is the encoding alphabet
193 | * @return the destination array
194 | * @since 1.3
195 | */
196 | private static byte[] encode3to4(byte[] source, int srcOffset,
197 | int numSigBytes, byte[] destination, int destOffset, byte[] alphabet) {
198 | // 1 2 3
199 | // 01234567890123456789012345678901 Bit position
200 | // --------000000001111111122222222 Array position from threeBytes
201 | // --------| || || || | Six bit groups to index alphabet
202 | // >>18 >>12 >> 6 >> 0 Right shift necessary
203 | // 0x3f 0x3f 0x3f Additional AND
204 |
205 | // Create buffer with zero-padding if there are only one or two
206 | // significant bytes passed in the array.
207 | // We have to shift left 24 in order to flush out the 1's that appear
208 | // when Java treats a value as negative that is cast from a byte to an int.
209 | int inBuff =
210 | (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
211 | | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
212 | | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
213 |
214 | switch (numSigBytes) {
215 | case 3:
216 | destination[destOffset] = alphabet[(inBuff >>> 18)];
217 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
218 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
219 | destination[destOffset + 3] = alphabet[(inBuff) & 0x3f];
220 | return destination;
221 | case 2:
222 | destination[destOffset] = alphabet[(inBuff >>> 18)];
223 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
224 | destination[destOffset + 2] = alphabet[(inBuff >>> 6) & 0x3f];
225 | destination[destOffset + 3] = EQUALS_SIGN;
226 | return destination;
227 | case 1:
228 | destination[destOffset] = alphabet[(inBuff >>> 18)];
229 | destination[destOffset + 1] = alphabet[(inBuff >>> 12) & 0x3f];
230 | destination[destOffset + 2] = EQUALS_SIGN;
231 | destination[destOffset + 3] = EQUALS_SIGN;
232 | return destination;
233 | default:
234 | return destination;
235 | } // end switch
236 | } // end encode3to4
237 |
238 | /**
239 | * Encodes a byte array into Base64 notation.
240 | * Equivalent to calling
241 | * {@code encodeBytes(source, 0, source.length)}
242 | *
243 | * @param source The data to convert
244 | * @since 1.4
245 | */
246 | public static String encode(byte[] source) {
247 | return encode(source, 0, source.length, ALPHABET, true);
248 | }
249 |
250 | /**
251 | * Encodes a byte array into web safe Base64 notation.
252 | *
253 | * @param source The data to convert
254 | * @param doPadding is {@code true} to pad result with '=' chars
255 | * if it does not fall on 3 byte boundaries
256 | */
257 | public static String encodeWebSafe(byte[] source, boolean doPadding) {
258 | return encode(source, 0, source.length, WEBSAFE_ALPHABET, doPadding);
259 | }
260 |
261 | /**
262 | * Encodes a byte array into Base64 notation.
263 | *
264 | * @param source the data to convert
265 | * @param off offset in array where conversion should begin
266 | * @param len length of data to convert
267 | * @param alphabet the encoding alphabet
268 | * @param doPadding is {@code true} to pad result with '=' chars
269 | * if it does not fall on 3 byte boundaries
270 | * @since 1.4
271 | */
272 | public static String encode(byte[] source, int off, int len, byte[] alphabet,
273 | boolean doPadding) {
274 | byte[] outBuff = encode(source, off, len, alphabet, Integer.MAX_VALUE);
275 | int outLen = outBuff.length;
276 |
277 | // If doPadding is false, set length to truncate '='
278 | // padding characters
279 | while (doPadding == false && outLen > 0) {
280 | if (outBuff[outLen - 1] != '=') {
281 | break;
282 | }
283 | outLen -= 1;
284 | }
285 |
286 | return new String(outBuff, 0, outLen);
287 | }
288 |
289 | /**
290 | * Encodes a byte array into Base64 notation.
291 | *
292 | * @param source the data to convert
293 | * @param off offset in array where conversion should begin
294 | * @param len length of data to convert
295 | * @param alphabet is the encoding alphabet
296 | * @param maxLineLength maximum length of one line.
297 | * @return the BASE64-encoded byte array
298 | */
299 | public static byte[] encode(byte[] source, int off, int len, byte[] alphabet,
300 | int maxLineLength) {
301 | int lenDiv3 = (len + 2) / 3; // ceil(len / 3)
302 | int len43 = lenDiv3 * 4;
303 | byte[] outBuff = new byte[len43 // Main 4:3
304 | + (len43 / maxLineLength)]; // New lines
305 |
306 | int d = 0;
307 | int e = 0;
308 | int len2 = len - 2;
309 | int lineLength = 0;
310 | for (; d < len2; d += 3, e += 4) {
311 |
312 | // The following block of code is the same as
313 | // encode3to4( source, d + off, 3, outBuff, e, alphabet );
314 | // but inlined for faster encoding (~20% improvement)
315 | int inBuff =
316 | ((source[d + off] << 24) >>> 8)
317 | | ((source[d + 1 + off] << 24) >>> 16)
318 | | ((source[d + 2 + off] << 24) >>> 24);
319 | outBuff[e] = alphabet[(inBuff >>> 18)];
320 | outBuff[e + 1] = alphabet[(inBuff >>> 12) & 0x3f];
321 | outBuff[e + 2] = alphabet[(inBuff >>> 6) & 0x3f];
322 | outBuff[e + 3] = alphabet[(inBuff) & 0x3f];
323 |
324 | lineLength += 4;
325 | if (lineLength == maxLineLength) {
326 | outBuff[e + 4] = NEW_LINE;
327 | e++;
328 | lineLength = 0;
329 | } // end if: end of line
330 | } // end for: each piece of array
331 |
332 | if (d < len) {
333 | encode3to4(source, d + off, len - d, outBuff, e, alphabet);
334 |
335 | lineLength += 4;
336 | if (lineLength == maxLineLength) {
337 | // Add a last newline
338 | outBuff[e + 4] = NEW_LINE;
339 | e++;
340 | }
341 | e += 4;
342 | }
343 |
344 | assert (e == outBuff.length);
345 | return outBuff;
346 | }
347 |
348 |
349 | /* ******** D E C O D I N G M E T H O D S ******** */
350 |
351 |
352 | /**
353 | * Decodes four bytes from array source
354 | * and writes the resulting bytes (up to three of them)
355 | * to destination.
356 | * The source and destination arrays can be manipulated
357 | * anywhere along their length by specifying
358 | * srcOffset and destOffset.
359 | * This method does not check to make sure your arrays
360 | * are large enough to accommodate srcOffset + 4 for
361 | * the source array or destOffset + 3 for
362 | * the destination array.
363 | * This method returns the actual number of bytes that
364 | * were converted from the Base64 encoding.
365 | *
366 | *
367 | * @param source the array to convert
368 | * @param srcOffset the index where conversion begins
369 | * @param destination the array to hold the conversion
370 | * @param destOffset the index where output will be put
371 | * @param decodabet the decodabet for decoding Base64 content
372 | * @return the number of decoded bytes converted
373 | * @since 1.3
374 | */
375 | private static int decode4to3(byte[] source, int srcOffset,
376 | byte[] destination, int destOffset, byte[] decodabet) {
377 | // Example: Dk==
378 | if (source[srcOffset + 2] == EQUALS_SIGN) {
379 | int outBuff =
380 | ((decodabet[source[srcOffset]] << 24) >>> 6)
381 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12);
382 |
383 | destination[destOffset] = (byte) (outBuff >>> 16);
384 | return 1;
385 | } else if (source[srcOffset + 3] == EQUALS_SIGN) {
386 | // Example: DkL=
387 | int outBuff =
388 | ((decodabet[source[srcOffset]] << 24) >>> 6)
389 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
390 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18);
391 |
392 | destination[destOffset] = (byte) (outBuff >>> 16);
393 | destination[destOffset + 1] = (byte) (outBuff >>> 8);
394 | return 2;
395 | } else {
396 | // Example: DkLE
397 | int outBuff =
398 | ((decodabet[source[srcOffset]] << 24) >>> 6)
399 | | ((decodabet[source[srcOffset + 1]] << 24) >>> 12)
400 | | ((decodabet[source[srcOffset + 2]] << 24) >>> 18)
401 | | ((decodabet[source[srcOffset + 3]] << 24) >>> 24);
402 |
403 | destination[destOffset] = (byte) (outBuff >> 16);
404 | destination[destOffset + 1] = (byte) (outBuff >> 8);
405 | destination[destOffset + 2] = (byte) (outBuff);
406 | return 3;
407 | }
408 | } // end decodeToBytes
409 |
410 |
411 | /**
412 | * Decodes data from Base64 notation.
413 | *
414 | * @param s the string to decode (decoded in default encoding)
415 | * @return the decoded data
416 | * @since 1.4
417 | */
418 | public static byte[] decode(String s) throws Base64DecoderException {
419 | byte[] bytes = s.getBytes();
420 | return decode(bytes, 0, bytes.length);
421 | }
422 |
423 | /**
424 | * Decodes data from web safe Base64 notation.
425 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
426 | *
427 | * @param s the string to decode (decoded in default encoding)
428 | * @return the decoded data
429 | */
430 | public static byte[] decodeWebSafe(String s) throws Base64DecoderException {
431 | byte[] bytes = s.getBytes();
432 | return decodeWebSafe(bytes, 0, bytes.length);
433 | }
434 |
435 | /**
436 | * Decodes Base64 content in byte array format and returns
437 | * the decoded byte array.
438 | *
439 | * @param source The Base64 encoded data
440 | * @return decoded data
441 | * @since 1.3
442 | * @throws Base64DecoderException
443 | */
444 | public static byte[] decode(byte[] source) throws Base64DecoderException {
445 | return decode(source, 0, source.length);
446 | }
447 |
448 | /**
449 | * Decodes web safe Base64 content in byte array format and returns
450 | * the decoded data.
451 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
452 | *
453 | * @param source the string to decode (decoded in default encoding)
454 | * @return the decoded data
455 | */
456 | public static byte[] decodeWebSafe(byte[] source)
457 | throws Base64DecoderException {
458 | return decodeWebSafe(source, 0, source.length);
459 | }
460 |
461 | /**
462 | * Decodes Base64 content in byte array format and returns
463 | * the decoded byte array.
464 | *
465 | * @param source the Base64 encoded data
466 | * @param off the offset of where to begin decoding
467 | * @param len the length of characters to decode
468 | * @return decoded data
469 | * @since 1.3
470 | * @throws Base64DecoderException
471 | */
472 | public static byte[] decode(byte[] source, int off, int len)
473 | throws Base64DecoderException {
474 | return decode(source, off, len, DECODABET);
475 | }
476 |
477 | /**
478 | * Decodes web safe Base64 content in byte array format and returns
479 | * the decoded byte array.
480 | * Web safe encoding uses '-' instead of '+', '_' instead of '/'
481 | *
482 | * @param source the Base64 encoded data
483 | * @param off the offset of where to begin decoding
484 | * @param len the length of characters to decode
485 | * @return decoded data
486 | */
487 | public static byte[] decodeWebSafe(byte[] source, int off, int len)
488 | throws Base64DecoderException {
489 | return decode(source, off, len, WEBSAFE_DECODABET);
490 | }
491 |
492 | /**
493 | * Decodes Base64 content using the supplied decodabet and returns
494 | * the decoded byte array.
495 | *
496 | * @param source the Base64 encoded data
497 | * @param off the offset of where to begin decoding
498 | * @param len the length of characters to decode
499 | * @param decodabet the decodabet for decoding Base64 content
500 | * @return decoded data
501 | */
502 | public static byte[] decode(byte[] source, int off, int len, byte[] decodabet)
503 | throws Base64DecoderException {
504 | int len34 = len * 3 / 4;
505 | byte[] outBuff = new byte[2 + len34]; // Upper limit on size of output
506 | int outBuffPosn = 0;
507 |
508 | byte[] b4 = new byte[4];
509 | int b4Posn = 0;
510 | int i = 0;
511 | byte sbiCrop = 0;
512 | byte sbiDecode = 0;
513 | for (i = 0; i < len; i++) {
514 | sbiCrop = (byte) (source[i + off] & 0x7f); // Only the low seven bits
515 | sbiDecode = decodabet[sbiCrop];
516 |
517 | if (sbiDecode >= WHITE_SPACE_ENC) { // White space Equals sign or better
518 | if (sbiDecode >= EQUALS_SIGN_ENC) {
519 | // An equals sign (for padding) must not occur at position 0 or 1
520 | // and must be the last byte[s] in the encoded value
521 | if (sbiCrop == EQUALS_SIGN) {
522 | int bytesLeft = len - i;
523 | byte lastByte = (byte) (source[len - 1 + off] & 0x7f);
524 | if (b4Posn == 0 || b4Posn == 1) {
525 | throw new Base64DecoderException(
526 | "invalid padding byte '=' at byte offset " + i);
527 | } else if ((b4Posn == 3 && bytesLeft > 2)
528 | || (b4Posn == 4 && bytesLeft > 1)) {
529 | throw new Base64DecoderException(
530 | "padding byte '=' falsely signals end of encoded value "
531 | + "at offset " + i);
532 | } else if (lastByte != EQUALS_SIGN && lastByte != NEW_LINE) {
533 | throw new Base64DecoderException(
534 | "encoded value has invalid trailing byte");
535 | }
536 | break;
537 | }
538 |
539 | b4[b4Posn++] = sbiCrop;
540 | if (b4Posn == 4) {
541 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
542 | b4Posn = 0;
543 | }
544 | }
545 | } else {
546 | throw new Base64DecoderException("Bad Base64 input character at " + i
547 | + ": " + source[i + off] + "(decimal)");
548 | }
549 | }
550 |
551 | // Because web safe encoding allows non padding base64 encodes, we
552 | // need to pad the rest of the b4 buffer with equal signs when
553 | // b4Posn != 0. There can be at most 2 equal signs at the end of
554 | // four characters, so the b4 buffer must have two or three
555 | // characters. This also catches the case where the input is
556 | // padded with EQUALS_SIGN
557 | if (b4Posn != 0) {
558 | if (b4Posn == 1) {
559 | throw new Base64DecoderException("single trailing character at offset "
560 | + (len - 1));
561 | }
562 | b4[b4Posn++] = EQUALS_SIGN;
563 | outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, decodabet);
564 | }
565 |
566 | byte[] out = new byte[outBuffPosn];
567 | System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
568 | return out;
569 | }
570 | }
571 |
--------------------------------------------------------------------------------
/android/src/main/java/com/util/Base64DecoderException.java:
--------------------------------------------------------------------------------
1 | // Copyright 2002, Google, Inc.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package com.util;
16 |
17 | /**
18 | * Exception thrown when encountering an invalid Base64 input character.
19 | *
20 | * @author nelson
21 | */
22 | public class Base64DecoderException extends Exception {
23 | public Base64DecoderException() {
24 | super();
25 | }
26 |
27 | public Base64DecoderException(String s) {
28 | super(s);
29 | }
30 |
31 | private static final long serialVersionUID = 1L;
32 | }
33 |
--------------------------------------------------------------------------------
/android/src/main/java/com/util/IabException.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2012 Google Inc.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package com.util;
17 |
18 | /**
19 | * Exception thrown when something went wrong with in-app billing.
20 | * An IabException has an associated IabResult (an error).
21 | * To get the IAB result that caused this exception to be thrown,
22 | * call {@link #getResult()}.
23 | */
24 | public class IabException extends Exception {
25 | IabResult mResult;
26 |
27 | public IabException(IabResult r) {
28 | this(r, null);
29 | }
30 | public IabException(int response, String message) {
31 | this(new IabResult(response, message));
32 | }
33 | public IabException(IabResult r, Exception cause) {
34 | super(r.getMessage(), cause);
35 | mResult = r;
36 | }
37 | public IabException(int response, String message, Exception cause) {
38 | this(new IabResult(response, message), cause);
39 | }
40 |
41 | /** Returns the IAB result (error) that this exception signals. */
42 | public IabResult getResult() { return mResult; }
43 | }
44 |
--------------------------------------------------------------------------------
/android/src/main/java/com/util/IabHelper.java:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2012 Google Inc.
2 | *
3 | * Licensed under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License.
5 | * You may obtain a copy of the License at
6 | *
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | *
9 | * Unless required by applicable law or agreed to in writing, software
10 | * distributed under the License is distributed on an "AS IS" BASIS,
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | * See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | package com.util;
17 |
18 | import android.app.Activity;
19 | import android.app.PendingIntent;
20 | import android.content.ComponentName;
21 | import android.content.Context;
22 | import android.content.Intent;
23 | import android.content.IntentSender;
24 | import android.content.ServiceConnection;
25 | import android.content.pm.PackageManager;
26 | import android.content.pm.ResolveInfo;
27 | import android.os.Bundle;
28 | import android.os.Handler;
29 | import android.os.IBinder;
30 | import android.os.RemoteException;
31 | import android.text.TextUtils;
32 | import android.util.Log;
33 |
34 | import com.android.vending.billing.IInAppBillingService;
35 |
36 | import org.json.JSONException;
37 |
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 |
42 | /**
43 | * Provides convenience methods for in-app billing. You can create one instance of this
44 | * class for your application and use it to process in-app billing operations.
45 | * It provides synchronous (blocking) and asynchronous (non-blocking) methods for
46 | * many common in-app billing operations, as well as automatic signature
47 | * verification.
48 | *
49 | * After instantiating, you must perform setup in order to start using the object.
50 | * To perform setup, call the {@link #startSetup} method and provide a listener;
51 | * that listener will be notified when setup is complete, after which (and not before)
52 | * you may call other methods.
53 | *
54 | * After setup is complete, you will typically want to request an inventory of owned
55 | * items and subscriptions. See {@link #queryInventory}, {@link #queryInventoryAsync}
56 | * and related methods.
57 | *
58 | * When you are done with this object, don't forget to call {@link #dispose}
59 | * to ensure proper cleanup. This object holds a binding to the in-app billing
60 | * service, which will leak unless you dispose of it correctly. If you created
61 | * the object on an Activity's onCreate method, then the recommended
62 | * place to dispose of it is the Activity's onDestroy method.
63 | *
64 | * A note about threading: When using this object from a background thread, you may
65 | * call the blocking versions of methods; when using from a UI thread, call
66 | * only the asynchronous versions and handle the results via callbacks.
67 | * Also, notice that you can only call one asynchronous operation at a time;
68 | * attempting to start a second asynchronous operation while the first one
69 | * has not yet completed will result in an exception being thrown.
70 | *
71 | * @author Bruno Oliveira (Google)
72 | *
73 | */
74 | public class IabHelper {
75 | // Is debug logging enabled?
76 | boolean mDebugLog = false;
77 | String mDebugTag = "IabHelper";
78 |
79 | // Is setup done?
80 | boolean mSetupDone = false;
81 |
82 | // Has this object been disposed of? (If so, we should ignore callbacks, etc)
83 | boolean mDisposed = false;
84 |
85 | // Are subscriptions supported?
86 | boolean mSubscriptionsSupported = false;
87 |
88 | // Is an asynchronous operation in progress?
89 | // (only one at a time can be in progress)
90 | boolean mAsyncInProgress = false;
91 |
92 | // (for logging/debugging)
93 | // if mAsyncInProgress == true, what asynchronous operation is in progress?
94 | String mAsyncOperation = "";
95 |
96 | // Context we were passed during initialization
97 | Context mContext;
98 |
99 | // Connection to the service
100 | IInAppBillingService mService;
101 | ServiceConnection mServiceConn;
102 |
103 | // The request code used to launch purchase flow
104 | int mRequestCode;
105 |
106 | // The item type of the current purchase flow
107 | String mPurchasingItemType;
108 |
109 | // Public key for verifying signature, in base64 encoding
110 | String mSignatureBase64 = null;
111 |
112 | // Billing response codes
113 | public static final int BILLING_RESPONSE_RESULT_OK = 0;
114 | public static final int BILLING_RESPONSE_RESULT_USER_CANCELED = 1;
115 | public static final int BILLING_RESPONSE_RESULT_BILLING_UNAVAILABLE = 3;
116 | public static final int BILLING_RESPONSE_RESULT_ITEM_UNAVAILABLE = 4;
117 | public static final int BILLING_RESPONSE_RESULT_DEVELOPER_ERROR = 5;
118 | public static final int BILLING_RESPONSE_RESULT_ERROR = 6;
119 | public static final int BILLING_RESPONSE_RESULT_ITEM_ALREADY_OWNED = 7;
120 | public static final int BILLING_RESPONSE_RESULT_ITEM_NOT_OWNED = 8;
121 |
122 | // IAB Helper error codes
123 | public static final int IABHELPER_ERROR_BASE = -1000;
124 | public static final int IABHELPER_REMOTE_EXCEPTION = -1001;
125 | public static final int IABHELPER_BAD_RESPONSE = -1002;
126 | public static final int IABHELPER_VERIFICATION_FAILED = -1003;
127 | public static final int IABHELPER_SEND_INTENT_FAILED = -1004;
128 | public static final int IABHELPER_USER_CANCELLED = -1005;
129 | public static final int IABHELPER_UNKNOWN_PURCHASE_RESPONSE = -1006;
130 | public static final int IABHELPER_MISSING_TOKEN = -1007;
131 | public static final int IABHELPER_UNKNOWN_ERROR = -1008;
132 | public static final int IABHELPER_SUBSCRIPTIONS_NOT_AVAILABLE = -1009;
133 | public static final int IABHELPER_INVALID_CONSUMPTION = -1010;
134 |
135 | // Keys for the responses from InAppBillingService
136 | public static final String RESPONSE_CODE = "RESPONSE_CODE";
137 | public static final String RESPONSE_GET_SKU_DETAILS_LIST = "DETAILS_LIST";
138 | public static final String RESPONSE_BUY_INTENT = "BUY_INTENT";
139 | public static final String RESPONSE_INAPP_PURCHASE_DATA = "INAPP_PURCHASE_DATA";
140 | public static final String RESPONSE_INAPP_SIGNATURE = "INAPP_DATA_SIGNATURE";
141 | public static final String RESPONSE_INAPP_ITEM_LIST = "INAPP_PURCHASE_ITEM_LIST";
142 | public static final String RESPONSE_INAPP_PURCHASE_DATA_LIST = "INAPP_PURCHASE_DATA_LIST";
143 | public static final String RESPONSE_INAPP_SIGNATURE_LIST = "INAPP_DATA_SIGNATURE_LIST";
144 | public static final String INAPP_CONTINUATION_TOKEN = "INAPP_CONTINUATION_TOKEN";
145 |
146 | // Keys for the response from getPurchaseConfig
147 | private static final String INTENT_V2_SUPPORT = "INTENT_V2_SUPPORT";
148 |
149 | // Item types
150 | public static final String ITEM_TYPE_INAPP = "inapp";
151 | public static final String ITEM_TYPE_SUBS = "subs";
152 |
153 | // some fields on the getSkuDetails response bundle
154 | public static final String GET_SKU_DETAILS_ITEM_LIST = "ITEM_ID_LIST";
155 | public static final String GET_SKU_DETAILS_ITEM_TYPE_LIST = "ITEM_TYPE_LIST";
156 |
157 | /**
158 | * Creates an instance. After creation, it will not yet be ready to use. You must perform
159 | * setup by calling {@link #startSetup} and wait for setup to complete. This constructor does not
160 | * block and is safe to call from a UI thread.
161 | *
162 | * @param ctx Your application or Activity context. Needed to bind to the in-app billing service.
163 | * @param base64PublicKey Your application's public key, encoded in base64.
164 | * This is used for verification of purchase signatures. You can find your app's base64-encoded
165 | * public key in your application's page on Google Play Developer Console. Note that this
166 | * is NOT your "developer public key".
167 | */
168 | public IabHelper(Context ctx, String base64PublicKey) {
169 | mContext = ctx.getApplicationContext();
170 | mSignatureBase64 = base64PublicKey;
171 | logDebug("IAB helper created.");
172 | }
173 |
174 | /**
175 | * Enables or disable debug logging through LogCat.
176 | */
177 | public void enableDebugLogging(boolean enable, String tag) {
178 | checkNotDisposed();
179 | mDebugLog = enable;
180 | mDebugTag = tag;
181 | }
182 |
183 | public void enableDebugLogging(boolean enable) {
184 | checkNotDisposed();
185 | mDebugLog = enable;
186 | }
187 |
188 | /**
189 | * Callback for setup process. This listener's {@link #onIabSetupFinished} method is called
190 | * when the setup process is complete.
191 | */
192 | public interface OnIabSetupFinishedListener {
193 | /**
194 | * Called to notify that setup is complete.
195 | *
196 | * @param result The result of the setup process.
197 | */
198 | public void onIabSetupFinished(IabResult result);
199 | }
200 |
201 | /**
202 | * Starts the setup process. This will start up the setup process asynchronously.
203 | * You will be notified through the listener when the setup process is complete.
204 | * This method is safe to call from a UI thread.
205 | *
206 | * @param listener The listener to notify when the setup process is complete.
207 | */
208 | public void startSetup(String vendorIntent, String vendorPackage, final OnIabSetupFinishedListener listener) {
209 | // If already set up, can't do it again.
210 | checkNotDisposed();
211 | if (mSetupDone) throw new IllegalStateException("IAB helper is already set up.");
212 |
213 | // Connection to IAB service
214 | logDebug("Starting in-app billing setup.");
215 | mServiceConn = new ServiceConnection() {
216 | @Override
217 | public void onServiceDisconnected(ComponentName name) {
218 | logDebug("Billing service disconnected.");
219 | mService = null;
220 | }
221 |
222 | @Override
223 | public void onServiceConnected(ComponentName name, IBinder service) {
224 | if (mDisposed) return;
225 | logDebug("Billing service connected.");
226 | mService = IInAppBillingService.Stub.asInterface(service);
227 | String packageName = mContext.getPackageName();
228 | try {
229 | logDebug("Checking for in-app billing 3 support.");
230 |
231 | // check for in-app billing v3 support
232 | int response = mService.isBillingSupported(3, packageName, ITEM_TYPE_INAPP);
233 | if (response != BILLING_RESPONSE_RESULT_OK) {
234 | if (listener != null) listener.onIabSetupFinished(new IabResult(response,
235 | "Error checking for billing v3 support."));
236 |
237 | // if in-app purchases aren't supported, neither are subscriptions.
238 | mSubscriptionsSupported = false;
239 | return;
240 | }
241 | logDebug("In-app billing version 3 supported for " + packageName);
242 |
243 | // check for v3 subscriptions support
244 | response = mService.isBillingSupported(3, packageName, ITEM_TYPE_SUBS);
245 | if (response == BILLING_RESPONSE_RESULT_OK) {
246 | logDebug("Subscriptions AVAILABLE.");
247 | mSubscriptionsSupported = true;
248 | }
249 | else {
250 | logDebug("Subscriptions NOT AVAILABLE. Response: " + response);
251 | }
252 |
253 | mSetupDone = true;
254 | }
255 | catch (RemoteException e) {
256 | if (listener != null) {
257 | listener.onIabSetupFinished(new IabResult(IABHELPER_REMOTE_EXCEPTION,
258 | "RemoteException while setting up in-app billing."));
259 | }
260 | e.printStackTrace();
261 | return;
262 | }
263 |
264 | if (listener != null) {
265 | listener.onIabSetupFinished(new IabResult(BILLING_RESPONSE_RESULT_OK, "Setup successful."));
266 | }
267 | }
268 | };
269 |
270 | Intent serviceIntent = new Intent(vendorIntent);
271 | serviceIntent.setPackage(vendorPackage);
272 |
273 | PackageManager pm=mContext.getPackageManager();
274 | List