Base64InputStream.java revision 8978aac1977408b05e386ae846c30920c7faa0a6
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 20package org.apache.james.mime4j.decoder; 21 22import java.io.IOException; 23import java.io.InputStream; 24 25import org.apache.commons.logging.Log; 26import org.apache.commons.logging.LogFactory; 27 28/** 29 * Performs Base-64 decoding on an underlying stream. 30 * 31 * 32 * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $ 33 */ 34public class Base64InputStream extends InputStream { 35 private static Log log = LogFactory.getLog(Base64InputStream.class); 36 37 private final InputStream s; 38 private final ByteQueue byteq = new ByteQueue(3); 39 private boolean done = false; 40 41 public Base64InputStream(InputStream s) { 42 this.s = s; 43 } 44 45 /** 46 * Closes the underlying stream. 47 * 48 * @throws IOException on I/O errors. 49 */ 50 public void close() throws IOException { 51 s.close(); 52 } 53 54 public int read() throws IOException { 55 if (byteq.count() == 0) { 56 fillBuffer(); 57 if (byteq.count() == 0) { 58 return -1; 59 } 60 } 61 62 byte val = byteq.dequeue(); 63 if (val >= 0) 64 return val; 65 else 66 return val & 0xFF; 67 } 68 69 /** 70 * Retrieve data from the underlying stream, decode it, 71 * and put the results in the byteq. 72 * @throws IOException 73 */ 74 private void fillBuffer() throws IOException { 75 byte[] data = new byte[4]; 76 int pos = 0; 77 78 int i; 79 while (!done) { 80 switch (i = s.read()) { 81 case -1: 82 if (pos > 0) { 83 log.warn("Unexpected EOF in MIME parser, dropping " 84 + pos + " sextets"); 85 } 86 return; 87 case '=': 88 decodeAndEnqueue(data, pos); 89 done = true; 90 break; 91 default: 92 byte sX = TRANSLATION[i]; 93 if (sX < 0) 94 continue; 95 data[pos++] = sX; 96 if (pos == data.length) { 97 decodeAndEnqueue(data, pos); 98 return; 99 } 100 break; 101 } 102 } 103 } 104 105 private void decodeAndEnqueue(byte[] data, int len) { 106 int accum = 0; 107 accum |= data[0] << 18; 108 accum |= data[1] << 12; 109 accum |= data[2] << 6; 110 accum |= data[3]; 111 112 byte b1 = (byte)(accum >>> 16); 113 byteq.enqueue(b1); 114 115 if (len > 2) { 116 byte b2 = (byte)((accum >>> 8) & 0xFF); 117 byteq.enqueue(b2); 118 119 if (len > 3) { 120 byte b3 = (byte)(accum & 0xFF); 121 byteq.enqueue(b3); 122 } 123 } 124 } 125 126 private static byte[] TRANSLATION = { 127 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */ 128 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */ 129 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */ 130 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */ 131 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */ 132 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */ 133 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */ 134 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */ 135 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */ 136 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */ 137 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */ 138 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */ 139 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */ 140 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */ 141 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */ 142 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 0xF0 */ 143 }; 144 145 146} 147