| 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 |  |  } |