001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    
018    package org.apache.geronimo.util.encoders;
019    
020    import java.io.IOException;
021    import java.io.OutputStream;
022    
023    public class Base64Encoder
024        implements Encoder
025    {
026        protected final byte[] encodingTable =
027            {
028                (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
029                (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
030                (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
031                (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
032                (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
033                (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
034                (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
035                (byte)'v',
036                (byte)'w', (byte)'x', (byte)'y', (byte)'z',
037                (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
038                (byte)'7', (byte)'8', (byte)'9',
039                (byte)'+', (byte)'/'
040            };
041    
042        protected byte    padding = (byte)'=';
043    
044        /*
045         * set up the decoding table.
046         */
047        protected final byte[] decodingTable = new byte[128];
048    
049        protected void initialiseDecodingTable()
050        {
051            for (int i = 0; i < encodingTable.length; i++)
052            {
053                decodingTable[encodingTable[i]] = (byte)i;
054            }
055        }
056    
057        public Base64Encoder()
058        {
059            initialiseDecodingTable();
060        }
061    
062        /**
063         * encode the input data producing a base 64 output stream.
064         *
065         * @return the number of bytes produced.
066         */
067        public int encode(
068            byte[]                data,
069            int                    off,
070            int                    length,
071            OutputStream    out)
072            throws IOException
073        {
074            int modulus = length % 3;
075            int dataLength = (length - modulus);
076            int a1, a2, a3;
077    
078            for (int i = off; i < off + dataLength; i += 3)
079            {
080                a1 = data[i] & 0xff;
081                a2 = data[i + 1] & 0xff;
082                a3 = data[i + 2] & 0xff;
083    
084                out.write(encodingTable[(a1 >>> 2) & 0x3f]);
085                out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
086                out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
087                out.write(encodingTable[a3 & 0x3f]);
088            }
089    
090            /*
091             * process the tail end.
092             */
093            int    b1, b2, b3;
094            int    d1, d2;
095    
096            switch (modulus)
097            {
098            case 0:        /* nothing left to do */
099                break;
100            case 1:
101                d1 = data[off + dataLength] & 0xff;
102                b1 = (d1 >>> 2) & 0x3f;
103                b2 = (d1 << 4) & 0x3f;
104    
105                out.write(encodingTable[b1]);
106                out.write(encodingTable[b2]);
107                out.write(padding);
108                out.write(padding);
109                break;
110            case 2:
111                d1 = data[off + dataLength] & 0xff;
112                d2 = data[off + dataLength + 1] & 0xff;
113    
114                b1 = (d1 >>> 2) & 0x3f;
115                b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
116                b3 = (d2 << 2) & 0x3f;
117    
118                out.write(encodingTable[b1]);
119                out.write(encodingTable[b2]);
120                out.write(encodingTable[b3]);
121                out.write(padding);
122                break;
123            }
124    
125            return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
126        }
127    
128        private boolean ignore(
129            char    c)
130        {
131            return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
132        }
133    
134        /**
135         * decode the base 64 encoded byte data writing it to the given output stream,
136         * whitespace characters will be ignored.
137         *
138         * @return the number of bytes produced.
139         */
140        public int decode(
141            byte[]                data,
142            int                    off,
143            int                    length,
144            OutputStream    out)
145            throws IOException
146        {
147            byte[]    bytes;
148            byte    b1, b2, b3, b4;
149            int        outLen = 0;
150    
151            int        end = off + length;
152    
153            while (end > 0)
154            {
155                if (!ignore((char)data[end - 1]))
156                {
157                    break;
158                }
159    
160                end--;
161            }
162    
163            int  i = off;
164            int  finish = end - 4;
165    
166            while (i < finish)
167            {
168                while ((i < finish) && ignore((char)data[i]))
169                {
170                    i++;
171                }
172    
173                b1 = decodingTable[data[i++]];
174    
175                while ((i < finish) && ignore((char)data[i]))
176                {
177                    i++;
178                }
179    
180                b2 = decodingTable[data[i++]];
181    
182                while ((i < finish) && ignore((char)data[i]))
183                {
184                    i++;
185                }
186    
187                b3 = decodingTable[data[i++]];
188    
189                while ((i < finish) && ignore((char)data[i]))
190                {
191                    i++;
192                }
193    
194                b4 = decodingTable[data[i++]];
195    
196                out.write((b1 << 2) | (b2 >> 4));
197                out.write((b2 << 4) | (b3 >> 2));
198                out.write((b3 << 6) | b4);
199    
200                outLen += 3;
201            }
202    
203            if (data[end - 2] == padding)
204            {
205                b1 = decodingTable[data[end - 4]];
206                b2 = decodingTable[data[end - 3]];
207    
208                out.write((b1 << 2) | (b2 >> 4));
209    
210                outLen += 1;
211            }
212            else if (data[end - 1] == padding)
213            {
214                b1 = decodingTable[data[end - 4]];
215                b2 = decodingTable[data[end - 3]];
216                b3 = decodingTable[data[end - 2]];
217    
218                out.write((b1 << 2) | (b2 >> 4));
219                out.write((b2 << 4) | (b3 >> 2));
220    
221                outLen += 2;
222            }
223            else
224            {
225                b1 = decodingTable[data[end - 4]];
226                b2 = decodingTable[data[end - 3]];
227                b3 = decodingTable[data[end - 2]];
228                b4 = decodingTable[data[end - 1]];
229    
230                out.write((b1 << 2) | (b2 >> 4));
231                out.write((b2 << 4) | (b3 >> 2));
232                out.write((b3 << 6) | b4);
233    
234                outLen += 3;
235            }
236    
237            return outLen;
238        }
239    
240        /**
241         * decode the base 64 encoded String data writing it to the given output stream,
242         * whitespace characters will be ignored.
243         *
244         * @return the number of bytes produced.
245         */
246        public int decode(
247            String                data,
248            OutputStream    out)
249            throws IOException
250        {
251            byte[]    bytes;
252            byte    b1, b2, b3, b4;
253            int        length = 0;
254    
255            int        end = data.length();
256    
257            while (end > 0)
258            {
259                if (!ignore(data.charAt(end - 1)))
260                {
261                    break;
262                }
263    
264                end--;
265            }
266    
267            int    i = 0;
268            int   finish = end - 4;
269    
270            while (i < finish)
271            {
272                while ((i < finish) && ignore(data.charAt(i)))
273                {
274                    i++;
275                }
276    
277                b1 = decodingTable[data.charAt(i++)];
278    
279                while ((i < finish) && ignore(data.charAt(i)))
280                {
281                    i++;
282                }
283                b2 = decodingTable[data.charAt(i++)];
284    
285                while ((i < finish) && ignore(data.charAt(i)))
286                {
287                    i++;
288                }
289                b3 = decodingTable[data.charAt(i++)];
290    
291                while ((i < finish) && ignore(data.charAt(i)))
292                {
293                    i++;
294                }
295                b4 = decodingTable[data.charAt(i++)];
296    
297                out.write((b1 << 2) | (b2 >> 4));
298                out.write((b2 << 4) | (b3 >> 2));
299                out.write((b3 << 6) | b4);
300    
301                length += 3;
302            }
303    
304            if (data.charAt(end - 2) == padding)
305            {
306                b1 = decodingTable[data.charAt(end - 4)];
307                b2 = decodingTable[data.charAt(end - 3)];
308    
309                out.write((b1 << 2) | (b2 >> 4));
310    
311                length += 1;
312            }
313            else if (data.charAt(end - 1) == padding)
314            {
315                b1 = decodingTable[data.charAt(end - 4)];
316                b2 = decodingTable[data.charAt(end - 3)];
317                b3 = decodingTable[data.charAt(end - 2)];
318    
319                out.write((b1 << 2) | (b2 >> 4));
320                out.write((b2 << 4) | (b3 >> 2));
321    
322                length += 2;
323            }
324            else
325            {
326                b1 = decodingTable[data.charAt(end - 4)];
327                b2 = decodingTable[data.charAt(end - 3)];
328                b3 = decodingTable[data.charAt(end - 2)];
329                b4 = decodingTable[data.charAt(end - 1)];
330    
331                out.write((b1 << 2) | (b2 >> 4));
332                out.write((b2 << 4) | (b3 >> 2));
333                out.write((b3 << 6) | b4);
334    
335                length += 3;
336            }
337    
338            return length;
339        }
340    }