001 /*
002 * Java Base64 - A pure Java library for reading and writing Base64
003 * encoded streams.
004 *
005 * Copyright (C) 2007-2009 Carlo Pelliccia (www.sauronsoftware.it)
006 *
007 * This program is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU Lesser General Public License version
009 * 2.1, as published by the Free Software Foundation.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License version 2.1 along with this program.
018 * If not, see <http://www.gnu.org/licenses/>.
019 */
020 package it.sauronsoftware.base64;
021
022 import java.io.IOException;
023 import java.io.InputStream;
024
025 /**
026 * <p>
027 * A base64 encoding input stream.
028 * </p>
029 *
030 * <p>
031 * A <em>Base64InputStream</em> reads from an underlying stream which is
032 * supposed to be a base64 encoded stream. <em>Base64InputStream</em> decodes
033 * the data read from the underlying stream and returns the decoded bytes to the
034 * caller.
035 * </p>
036 *
037 * @author Carlo Pelliccia
038 */
039 public class Base64InputStream extends InputStream {
040
041 /**
042 * The underlying stream.
043 */
044 private InputStream inputStream;
045
046 /**
047 * The buffer.
048 */
049 private int[] buffer;
050
051 /**
052 * A counter for values in the buffer.
053 */
054 private int bufferCounter = 0;
055
056 /**
057 * End-of-stream flag.
058 */
059 private boolean eof = false;
060
061 /**
062 * <p>
063 * It builds a base64 decoding input stream.
064 * </p>
065 *
066 * @param inputStream
067 * The underlying stream, from which the encoded data is read.
068 */
069 public Base64InputStream(InputStream inputStream) {
070 this.inputStream = inputStream;
071 }
072
073 public int read() throws IOException {
074 if (buffer == null || bufferCounter == buffer.length) {
075 if (eof) {
076 return -1;
077 }
078 acquire();
079 if (buffer.length == 0) {
080 buffer = null;
081 return -1;
082 }
083 bufferCounter = 0;
084 }
085 return buffer[bufferCounter++];
086 }
087
088 /**
089 * Reads from the underlying stream, decodes the data and puts the decoded
090 * bytes into the buffer.
091 */
092 private void acquire() throws IOException {
093 char[] four = new char[4];
094 int i = 0;
095 do {
096 int b = inputStream.read();
097 if (b == -1) {
098 if (i != 0) {
099 throw new IOException("Bad base64 stream");
100 } else {
101 buffer = new int[0];
102 eof = true;
103 return;
104 }
105 }
106 char c = (char) b;
107 if (Shared.chars.indexOf(c) != -1 || c == Shared.pad) {
108 four[i++] = c;
109 } else if (c != '\r' && c != '\n') {
110 throw new IOException("Bad base64 stream");
111 }
112 } while (i < 4);
113 boolean padded = false;
114 for (i = 0; i < 4; i++) {
115 if (four[i] != Shared.pad) {
116 if (padded) {
117 throw new IOException("Bad base64 stream");
118 }
119 } else {
120 if (!padded) {
121 padded = true;
122 }
123 }
124 }
125 int l;
126 if (four[3] == Shared.pad) {
127 if (inputStream.read() != -1) {
128 throw new IOException("Bad base64 stream");
129 }
130 eof = true;
131 if (four[2] == Shared.pad) {
132 l = 1;
133 } else {
134 l = 2;
135 }
136 } else {
137 l = 3;
138 }
139 int aux = 0;
140 for (i = 0; i < 4; i++) {
141 if (four[i] != Shared.pad) {
142 aux = aux | (Shared.chars.indexOf(four[i]) << (6 * (3 - i)));
143 }
144 }
145 buffer = new int[l];
146 for (i = 0; i < l; i++) {
147 buffer[i] = (aux >>> (8 * (2 - i))) & 0xFF;
148 }
149 }
150
151 public void close() throws IOException {
152 inputStream.close();
153 }
154 }