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