Home | History | Annotate | Download | only in adapter
      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 
     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
     64     public void close() throws IOException {
     65         s.close();
     66     }
     67 
     68     @Override
     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      */
     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 
    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