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 package org.apache.myfaces.trinidad.util;
20
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.Reader;
25
26
27 /**
28 * An InputStream that decodes data from base64 representation into a binary
29 * format.
30 * It takes a Reader as its single argument to its constructor and base64
31 * characters read in from the Reader are correspondingly made available to be
32 * read out in the corresponding binary format via the read() method.
33 */
34 public class Base64InputStream extends InputStream
35 {
36 /*
37 * @param in a character stream of valid base64 characters. the reader
38 * should not contain invalid base64 characters (such as newlines).
39 *
40 */
41 public Base64InputStream(Reader in)
42 {
43 _in = in;
44 _byteBufferIndex = Integer.MAX_VALUE;
45 _maxByteBufferIndex = 0;
46 _byteBuffer = new byte[(int)(_QUANTUM_SIZE*0.75)];
47 }
48
49
50
51 /**
52 * Read a single character.
53 *
54 * Overrides InputStream.read()
55 *
56 * @return The byte read, as an integer,
57 * or -1 if the end of the stream has been reached
58 *
59 */
60 @Override
61 public int read() throws IOException
62 {
63 int result = -1;
64
65 if ( _byteBufferIndex <= _maxByteBufferIndex )
66 {
67 result = _byteBuffer[_byteBufferIndex];
68 result = result & 0xff;
69 _byteBufferIndex++;
70 }
71 else
72 {
73 // exhausted our buffer, so fill it up and try again.
74 // Only try to read another byte if byteBuffer has been refilled
75 // with something new.
76
77 int b = _fillByteBuffer();
78 if (b>-1) {
79 return read();
80 }
81 }
82
83 return result;
84 }
85
86 @Override
87 public void close() throws IOException
88 {
89 _in.close();
90 }
91
92 @Override
93 public int available() throws IOException
94 {
95 return _in.ready() ? 1 : 0;
96 }
97
98 /**
99 * Reads in _QUANTUM_SIZE number of base64 characters from the reader
100 * and converts them into bytes and places these bytes into the decodedBuffer
101 * array.
102 *
103 * Note: This method assumes that the reader contains ONLY valid Base64
104 * characters.
105 *
106 * @return the highest index of the decodedbuffer that this method filled up
107 *
108 *
109 */
110 private int _fillByteBuffer() throws IOException
111 {
112 //fill encodedBuffer with up to _QUANTUM_SIZE valid base64 chars from reader
113 int numValidCharsRead;
114 char[] encodedBuffer = new char[ _QUANTUM_SIZE ]; // base64 encoded chars
115
116 numValidCharsRead = _in.read( encodedBuffer, 0, _QUANTUM_SIZE );
117
118 // reset the index
119 _byteBufferIndex = -1;
120
121 char eb1, eb2, eb3, eb4;
122 char c1, c2, c3, c4;
123
124 for (int i = 0; i < numValidCharsRead; i += 4)
125 {
126 eb1 = encodedBuffer[i];
127 eb2 = encodedBuffer[i+1];
128 eb3 = encodedBuffer[i+2];
129 eb4 = encodedBuffer[i+3];
130
131 c1 = _decode(eb1);
132 c2 = _decode(eb2);
133 c3 = _decode(eb3);
134 c4 = _decode(eb4);
135
136 _byteBufferIndex++;
137 _byteBuffer[_byteBufferIndex] = (byte)( (c1<<2) | ((c2>>4)&0x03) );
138 if (eb3 != '=')
139 {
140 _byteBufferIndex++;
141 _byteBuffer[_byteBufferIndex] = (byte)( (c2<<4) | ((c3>>2)&0x0f) );
142 }
143 if (eb4 != '=')
144 {
145 _byteBufferIndex++;
146 _byteBuffer[_byteBufferIndex] = (byte)( (c3<<6) | (c4&0x3f) );
147 }
148 }
149
150 // set the maximum index to be the number of bytes written to the buffer
151 _maxByteBufferIndex = _byteBufferIndex;
152 // reset the index so we can walk through the byte buffer
153 _byteBufferIndex = 0;
154
155 return _maxByteBufferIndex;
156
157 }
158
159
160 /**
161 * Converts a base64 character into its decimal equivalent.
162 *
163 * @param c the base64 character as an int
164 *
165 * @return decoded value of the input base64 character
166 */
167 static private char _decode(int c)
168 {
169 if (c >= 'A' && c <= 'Z')
170 {
171 return (char)(c - 'A');
172 }
173 else if (c >= 'a' && c <= 'z')
174 {
175 return (char)(c - 'a' + 26);
176 }
177 else if (c >= '0' && c <= '9')
178 {
179 return (char)(c - '0' + 52);
180 }
181 else if (c == '+')
182 {
183 return 0x3e;
184 }
185 else if (c == '/')
186 {
187 return 0x3f;
188 }
189 else if (c == '=')
190 {
191 return '=';
192 }
193 else
194 {
195 return 0;
196 }
197 }
198
199 /** the number of base64 encoded characters to read from reader at a time **/
200 /** this number should be a multiple of 4 **/
201 private static final int _QUANTUM_SIZE = 512;
202
203 /** input stream of base64 encoded data **/
204 private final Reader _in;
205
206 /** an index into the decoded byte buffer **/
207 private int _byteBufferIndex;
208
209 /** the number of bytes in the byte buffer **/
210 private int _maxByteBufferIndex;
211
212 /** contains decoded bytes **/
213 private byte[] _byteBuffer;
214 }