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    }