• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one   *
3  * or more contributor license agreements.  See the NOTICE file *
4  * distributed with this work for additional information        *
5  * regarding copyright ownership.  The ASF licenses this file   *
6  * to you under the Apache License, Version 2.0 (the            *
7  * "License"); you may not use this file except in compliance   *
8  * with the License.  You may obtain a copy of the License at   *
9  *                                                              *
10  *   http://www.apache.org/licenses/LICENSE-2.0                 *
11  *                                                              *
12  * Unless required by applicable law or agreed to in writing,   *
13  * software distributed under the License is distributed on an  *
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15  * KIND, either express or implied.  See the License for the    *
16  * specific language governing permissions and limitations      *
17  * under the License.                                           *
18  ****************************************************************/
19 
20 /**
21  * Modified for Exchange attachment decoding 5/12/11.  Changes are bracketed with START EAS CHANGES
22  * and END EAS CHANGES
23  *
24  * Without the included changes, the final bytes of the input stream will be read here and thrown
25  * away; in that case, the WBXML parser will lose information necessary to determine that the
26  * entire stream has been processed correctly.  Since inline WBXML text is terminated with a zero
27  * byte, and since zero is not valid Base64, we terminate reading when we find a zero byte, leaving
28  * the remainder of the stream untouched (to be read by the Parser that created the
29  * Base64InputStream.
30  */
31 
32 package com.android.exchange.adapter;
33 
34 import java.io.IOException;
35 import java.io.InputStream;
36 
37 /**
38  * Performs Base-64 decoding on an underlying stream.
39  *
40  *
41  * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
42  */
43 public class Base64InputStream extends InputStream {
44     private final InputStream s;
45     private int outCount = 0;
46     private int outIndex = 0;
47     private final int[] outputBuffer = new int[3];
48     private final byte[] inputBuffer = new byte[4];
49     private boolean done = false;
50     // START EAS CHANGES
51     private boolean padSeen = false;
52     // END EAS CHANGES
53 
Base64InputStream(InputStream s)54     public Base64InputStream(InputStream s) {
55         this.s = s;
56     }
57 
58     /**
59      * Closes the underlying stream.
60      *
61      * @throws IOException on I/O errors.
62      */
63     @Override
close()64     public void close() throws IOException {
65         s.close();
66     }
67 
68     @Override
read()69     public int read() throws IOException {
70         if (outIndex == outCount) {
71             fillBuffer();
72             if (outIndex == outCount) {
73                 return -1;
74             }
75         }
76 
77         return outputBuffer[outIndex++];
78     }
79 
80     /**
81      * Retrieve data from the underlying stream, decode it,
82      * and put the results in the byteq.
83      * @throws IOException
84      */
fillBuffer()85     private void fillBuffer() throws IOException {
86         outCount = 0;
87         outIndex = 0;
88         int inCount = 0;
89 
90         int i;
91         // "done" is needed for the two successive '=' at the end
92         while (!done) {
93             switch (i = s.read()) {
94                 // START EAS CHANGES
95                 case 0:
96                     // In EAS, a zero will indicate the end of the (inline) base64 string
97                     // Stop reading at this point, so that we can continue with WBXML
98                     done = true;
99                     return;
100                 // END EAS CHANGES
101                 case -1:
102                     // No more input - just return, let outputBuffer drain out, and be done
103                     return;
104                 case '=':
105                     // START EAS CHANGES
106                     // Allow for a second '=' before we're really done (we should get a zero next)
107                     if (padSeen) {
108                         return;
109                     }
110                     // We've seen a (first) end padding character, flush what's in the buffer
111                     padSeen = true;
112                     // END EAS CHANGES
113                     // PRE-EAS LINE COMMENTED OUT
114                     //done = true;
115                     decodeAndEnqueue(inCount);
116                     return;
117                 default:
118                     byte sX = TRANSLATION[i];
119                     if (sX < 0) continue;
120                     inputBuffer[inCount++] = sX;
121                     if (inCount == 4) {
122                         decodeAndEnqueue(inCount);
123                         return;
124                     }
125                     break;
126             }
127         }
128     }
129 
decodeAndEnqueue(int len)130     private void decodeAndEnqueue(int len) {
131         int accum = 0;
132         accum |= inputBuffer[0] << 18;
133         accum |= inputBuffer[1] << 12;
134         accum |= inputBuffer[2] << 6;
135         accum |= inputBuffer[3];
136 
137         // There's a bit of duplicated code here because we want to have straight-through operation
138         // for the most common case of len==4
139         if (len == 4) {
140             outputBuffer[0] = (accum >> 16) & 0xFF;
141             outputBuffer[1] = (accum >> 8) & 0xFF;
142             outputBuffer[2] = (accum) & 0xFF;
143             outCount = 3;
144             return;
145         } else if (len == 3) {
146             outputBuffer[0] = (accum >> 16) & 0xFF;
147             outputBuffer[1] = (accum >> 8) & 0xFF;
148             outCount = 2;
149             return;
150         } else {    // len == 2
151             outputBuffer[0] = (accum >> 16) & 0xFF;
152             outCount = 1;
153             return;
154         }
155     }
156 
157     private static byte[] TRANSLATION = {
158         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
159         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
160         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
161         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
162         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40 */
163         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
164         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
165         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */
166         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
167         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
168         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
169         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
170         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
171         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
172         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
173         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1  /* 0xF0 */
174     };
175 }
176