| 1 | |
|
| 2 | |
|
| 3 | |
|
| 4 | |
|
| 5 | |
|
| 6 | |
|
| 7 | |
|
| 8 | |
|
| 9 | |
|
| 10 | |
|
| 11 | |
|
| 12 | |
|
| 13 | |
|
| 14 | |
|
| 15 | |
|
| 16 | |
|
| 17 | |
package org.apache.commons.fileupload.util.mime; |
| 18 | |
|
| 19 | |
import java.io.IOException; |
| 20 | |
import java.io.OutputStream; |
| 21 | |
|
| 22 | |
|
| 23 | |
|
| 24 | |
|
| 25 | |
final class Base64Decoder { |
| 26 | |
|
| 27 | |
|
| 28 | |
|
| 29 | |
|
| 30 | |
private static final int INVALID_BYTE = -1; |
| 31 | |
|
| 32 | |
|
| 33 | |
|
| 34 | |
|
| 35 | |
private static final int PAD_BYTE = -2; |
| 36 | |
|
| 37 | |
|
| 38 | |
|
| 39 | |
|
| 40 | |
private static final int MASK_BYTE_UNSIGNED = 0xFF; |
| 41 | |
|
| 42 | |
|
| 43 | |
|
| 44 | |
|
| 45 | |
private static final int INPUT_BYTES_PER_CHUNK = 4; |
| 46 | |
|
| 47 | |
|
| 48 | |
|
| 49 | |
|
| 50 | 1 | private static final byte[] ENCODING_TABLE = { |
| 51 | |
(byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', |
| 52 | |
(byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', |
| 53 | |
(byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', |
| 54 | |
(byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', |
| 55 | |
(byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', |
| 56 | |
(byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', |
| 57 | |
(byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', |
| 58 | |
(byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', |
| 59 | |
(byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', |
| 60 | |
(byte) '7', (byte) '8', (byte) '9', |
| 61 | |
(byte) '+', (byte) '/' |
| 62 | |
}; |
| 63 | |
|
| 64 | |
|
| 65 | |
|
| 66 | |
|
| 67 | |
private static final byte PADDING = (byte) '='; |
| 68 | |
|
| 69 | |
|
| 70 | |
|
| 71 | |
|
| 72 | |
|
| 73 | |
|
| 74 | 1 | private static final byte[] DECODING_TABLE = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1]; |
| 75 | |
|
| 76 | |
static { |
| 77 | |
|
| 78 | 257 | for (int i = 0; i < DECODING_TABLE.length; i++) { |
| 79 | 256 | DECODING_TABLE[i] = INVALID_BYTE; |
| 80 | |
} |
| 81 | |
|
| 82 | 65 | for (int i = 0; i < ENCODING_TABLE.length; i++) { |
| 83 | 64 | DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i; |
| 84 | |
} |
| 85 | |
|
| 86 | 1 | DECODING_TABLE[PADDING] = PAD_BYTE; |
| 87 | 1 | } |
| 88 | |
|
| 89 | |
|
| 90 | |
|
| 91 | |
|
| 92 | 0 | private Base64Decoder() { |
| 93 | |
|
| 94 | 0 | } |
| 95 | |
|
| 96 | |
|
| 97 | |
|
| 98 | |
|
| 99 | |
|
| 100 | |
|
| 101 | |
|
| 102 | |
|
| 103 | |
|
| 104 | |
|
| 105 | |
|
| 106 | |
public static int decode(byte[] data, OutputStream out) throws IOException { |
| 107 | 28 | int outLen = 0; |
| 108 | 28 | byte [] cache = new byte[INPUT_BYTES_PER_CHUNK]; |
| 109 | 28 | int cachedBytes = 0; |
| 110 | |
|
| 111 | 418 | for (byte b : data) { |
| 112 | 393 | final byte d = DECODING_TABLE[MASK_BYTE_UNSIGNED & b]; |
| 113 | 393 | if (d == INVALID_BYTE) { |
| 114 | 14 | continue; |
| 115 | |
} |
| 116 | 379 | cache[cachedBytes++] = d; |
| 117 | 379 | if (cachedBytes == INPUT_BYTES_PER_CHUNK) { |
| 118 | |
|
| 119 | 92 | final byte b1 = cache[0]; |
| 120 | 92 | final byte b2 = cache[1]; |
| 121 | 92 | final byte b3 = cache[2]; |
| 122 | 92 | final byte b4 = cache[3]; |
| 123 | 92 | if (b1 == PAD_BYTE || b2 == PAD_BYTE) { |
| 124 | 2 | throw new IOException("Invalid Base64 input: incorrect padding, first two bytes cannot be padding"); |
| 125 | |
} |
| 126 | |
|
| 127 | |
|
| 128 | 90 | out.write((b1 << 2) | (b2 >> 4)); |
| 129 | 90 | outLen++; |
| 130 | 90 | if (b3 != PAD_BYTE) { |
| 131 | |
|
| 132 | 82 | out.write((b2 << 4) | (b3 >> 2)); |
| 133 | 82 | outLen++; |
| 134 | 82 | if (b4 != PAD_BYTE) { |
| 135 | |
|
| 136 | 72 | out.write((b3 << 6) | b4); |
| 137 | 72 | outLen++; |
| 138 | |
} |
| 139 | 8 | } else if (b4 != PAD_BYTE) { |
| 140 | 1 | throw new |
| 141 | |
IOException("Invalid Base64 input: incorrect padding, 4th byte must be padding if 3rd byte is"); |
| 142 | |
} |
| 143 | 89 | cachedBytes = 0; |
| 144 | |
} |
| 145 | |
} |
| 146 | |
|
| 147 | 25 | if (cachedBytes != 0) { |
| 148 | 6 | throw new IOException("Invalid Base64 input: truncated"); |
| 149 | |
} |
| 150 | 19 | return outLen; |
| 151 | |
} |
| 152 | |
} |