##### Coverage Report - org.apache.commons.fileupload.util.mime.Base64Decoder

Classes in this File | Line Coverage | Branch Coverage | Complexity
Base64Decoder |
94% | 37/39
94% | 17/18
| 7.0;7

1 |  |
     /*
2 |  |
      * Licensed to the Apache Software Foundation (ASF) under one or more
3 |  |
      * contributor license agreements.  See the NOTICE file distributed with
4 |  |
      * this work for additional information regarding copyright ownership.
5 |  |
      * The ASF licenses this file to You under the Apache License, Version 2.0
6 |  |
      * (the "License"); you may not use this file except in compliance with
7 |  |
      * the License.  You may obtain a copy of the License at
8 |  |
      *
9 |  |
      *      http://www.apache.org/licenses/LICENSE-2.0
10 |  |
      *
11 |  |
      * Unless required by applicable law or agreed to in writing, software
12 |  |
      * distributed under the License is distributed on an "AS IS" BASIS,
13 |  |
      * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 |  |
      * See the License for the specific language governing permissions and
15 |  |
      * limitations under the License.
16 |  |
      */
17 |  |
     package org.apache.commons.fileupload.util.mime;
18 |  |
19 |  |
     import java.io.IOException;
20 |  |
     import java.io.OutputStream;
21 |  |
22 |  |
     /**
23 |  |
      * @since 1.3
24 |  |
      */
25 |  |
     final class Base64Decoder {
26 |  |
27 |  |
         /**
28 |  |
          * Decoding table value for invalid bytes.
29 |  |
          */
30 |  |
         private static final int INVALID_BYTE = -1; // must be outside range 0-63
31 |  |
32 |  |
         /**
33 |  |
          * Decoding table value for padding bytes, so can detect PAD afer conversion.
34 |  |
          */
35 |  |
         private static final int PAD_BYTE = -2; // must be outside range 0-63
36 |  |
37 |  |
         /**
38 |  |
          * Mask to treat byte as unsigned integer.
39 |  |
          */
40 |  |
         private static final int MASK_BYTE_UNSIGNED = 0xFF;
41 |  |
42 |  |
         /**
43 |  |
          * Number of bytes per encoded chunk - 4 6bit bytes produce 3 8bit bytes on output.
44 |  |
          */
45 |  |
         private static final int INPUT_BYTES_PER_CHUNK = 4;
46 |  |
47 |  |
         /**
48 |  |
          * Set up the encoding table.
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 |  |
          * The padding byte.
66 |  |
          */
67 |  |
         private static final byte PADDING = (byte) '=';
68 |  |
69 |  |
         /**
70 |  |
          * Set up the decoding table; this is indexed by a byte converted to an unsigned int,
71 |  |
          * so must be at least as large as the number of different byte values,
72 |  |
          * positive and negative and zero.
73 |  |
          */
74 | 1 |
         private static final byte[] DECODING_TABLE = new byte[Byte.MAX_VALUE - Byte.MIN_VALUE + 1];
75 |  |
76 |  |
         static {
77 |  |
             // Initialise as all invalid characters
78 | 257 |
             for (int i = 0; i < DECODING_TABLE.length; i++) {
79 | 256 |
                 DECODING_TABLE[i] = INVALID_BYTE;
80 |  |
             }
81 |  |
             // set up valid characters
82 | 65 |
             for (int i = 0; i < ENCODING_TABLE.length; i++) {
83 | 64 |
                 DECODING_TABLE[ENCODING_TABLE[i]] = (byte) i;
84 |  |
             }
85 |  |
             // Allow pad byte to be easily detected after conversion
86 | 1 |
             DECODING_TABLE[PADDING] = PAD_BYTE;
87 | 1 |
         }
88 |  |
89 |  |
         /**
90 |  |
          * Hidden constructor, this class must not be instantiated.
91 |  |
          */
92 | 0 |
         private Base64Decoder() {
93 |  |
             // do nothing
94 | 0 |
         }
95 |  |
96 |  |
         /**
97 |  |
          * Decode the base 64 encoded byte data writing it to the given output stream,
98 |  |
          * whitespace characters will be ignored.
99 |  |
          *
100 |  |
          * @param data the buffer containing the Base64-encoded data
101 |  |
          * @param out the output stream to hold the decoded bytes
102 |  |
          *
103 |  |
          * @return the number of bytes produced.
104 |  |
          * @throws IOException thrown when the padding is incorrect or the input is truncated.
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; // Ignore invalid bytes
115 |  |
                 }
116 | 379 |
                 cache[cachedBytes++] = d;
117 | 379 |
                 if (cachedBytes == INPUT_BYTES_PER_CHUNK) {
118 |  |
                     // CHECKSTYLE IGNORE MagicNumber FOR NEXT 4 LINES
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 |  |
                     // Convert 4 6-bit bytes to 3 8-bit bytes
127 |  |
                     // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE
128 | 90 |
                     out.write((b1 << 2) | (b2 >> 4)); // 6 bits of b1 plus 2 bits of b2
129 | 90 |
                     outLen++;
130 | 90 |
                     if (b3 != PAD_BYTE) {
131 |  |
                         // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE
132 | 82 |
                         out.write((b2 << 4) | (b3 >> 2)); // 4 bits of b2 plus 4 bits of b3
133 | 82 |
                         outLen++;
134 | 82 |
                         if (b4 != PAD_BYTE) {
135 |  |
                             // CHECKSTYLE IGNORE MagicNumber FOR NEXT 1 LINE
136 | 72 |
                             out.write((b3 << 6) | b4);        // 2 bits of b3 plus 6 bits of b4
137 | 72 |
                             outLen++;
138 |  |
                         }
139 | 8 |
                     } else if (b4 != PAD_BYTE) { // if byte 3 is pad, byte 4 must be pad too
140 | 1 |
                         throw new // line wrap to avoid 120 char limit
141 |  |
                         IOException("Invalid Base64 input: incorrect padding, 4th byte must be padding if 3rd byte is");
142 |  |
                     }
143 | 89 |
                     cachedBytes = 0;
144 |  |
                 }
145 |  |
             }
146 |  |
             // Check for anything left over
147 | 25 |
             if (cachedBytes != 0) {
148 | 6 |
                 throw new IOException("Invalid Base64 input: truncated");
149 |  |
             }
150 | 19 |
             return outLen;
151 |  |
         }
152 |  |
     }