001    /*
002     * Java Base64 - A pure Java library for reading and writing Base64
003     *               encoded streams.
004     * 
005     * Copyright (C) 2008 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 General Public License as published by
009     * the Free Software Foundation, either version 3 of the License, or
010     * (at your option) any later version.
011     *
012     * This program is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015     * GNU General Public License for more details.
016     *
017     * You should have received a copy of the GNU General Public License
018     * along with this program.  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 available() throws IOException {
074                    return buffer != null ? buffer.length : 0;
075            }
076    
077            public int read() throws IOException {
078                    if (buffer == null || bufferCounter == buffer.length) {
079                            if (eof) {
080                                    return -1;
081                            }
082                            acquire();
083                            if (buffer.length == 0) {
084                                    buffer = null;
085                                    return -1;
086                            }
087                            bufferCounter = 0;
088                    }
089                    return buffer[bufferCounter++];
090            }
091    
092            /**
093             * Reads from the underlying stream, decodes the data and puts the decoded
094             * bytes into the buffer.
095             */
096            private void acquire() throws IOException {
097                    char[] four = new char[4];
098                    int i = 0;
099                    do {
100                            int b = inputStream.read();
101                            if (b == -1) {
102                                    if (i != 0) {
103                                            throw new IOException("Bad base64 stream");
104                                    } else {
105                                            buffer = new int[0];
106                                            eof = true;
107                                            return;
108                                    }
109                            }
110                            char c = (char) b;
111                            if (Shared.chars.indexOf(c) != -1 || c == Shared.pad) {
112                                    four[i++] = c;
113                            } else {
114                                    throw new IOException("Bad base64 stream");
115                            }
116                    } while (i < 4);
117                    boolean padded = false;
118                    for (i = 0; i < 4; i++) {
119                            if (four[i] != Shared.pad) {
120                                    if (padded) {
121                                            throw new IOException("Bad base64 stream");
122                                    }
123                            } else {
124                                    if (!padded) {
125                                            padded = true;
126                                    }
127                            }
128                    }
129                    int l;
130                    if (four[3] == Shared.pad) {
131                            if (inputStream.read() != -1) {
132                                    throw new IOException("Bad base64 stream");
133                            }
134                            eof = true;
135                            if (four[2] == Shared.pad) {
136                                    l = 1;
137                            } else {
138                                    l = 2;
139                            }
140                    } else {
141                            l = 3;
142                    }
143                    int aux = 0;
144                    for (i = 0; i < 4; i++) {
145                            if (four[i] != Shared.pad) {
146                                    aux = aux | (Shared.chars.indexOf(four[i]) << (6 * (3 - i)));
147                            }
148                    }
149                    buffer = new int[l];
150                    for (i = 0; i < l; i++) {
151                            buffer[i] = (aux >>> (8 * (2 - i))) & 0xFF;
152                    }
153            }
154    
155            public void close() throws IOException {
156                    inputStream.close();
157            }
158    
159    }