001    /**
002     *
003     * Copyright 2003-2006 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  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.mail.util;
019    
020    import java.io.IOException;
021    import java.io.InputStream;
022    import java.io.OutputStream;
023    import java.io.PrintStream;
024    
025    public class Base64Encoder
026        implements Encoder
027    {
028        protected final byte[] encodingTable =
029            {
030                (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
031                (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
032                (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
033                (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
034                (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
035                (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
036                (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
037                (byte)'v',
038                (byte)'w', (byte)'x', (byte)'y', (byte)'z',
039                (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
040                (byte)'7', (byte)'8', (byte)'9',
041                (byte)'+', (byte)'/'
042            };
043    
044        protected byte    padding = (byte)'=';
045    
046        /*
047         * set up the decoding table.
048         */
049        protected final byte[] decodingTable = new byte[256];
050    
051        protected void initialiseDecodingTable()
052        {
053            for (int i = 0; i < encodingTable.length; i++)
054            {
055                decodingTable[encodingTable[i]] = (byte)i;
056            }
057        }
058    
059        public Base64Encoder()
060        {
061            initialiseDecodingTable();
062        }
063    
064        /**
065         * encode the input data producing a base 64 output stream.
066         *
067         * @return the number of bytes produced.
068         */
069        public int encode(
070            byte[]                data,
071            int                    off,
072            int                    length,
073            OutputStream    out)
074            throws IOException
075        {
076            int modulus = length % 3;
077            int dataLength = (length - modulus);
078            int a1, a2, a3;
079    
080            for (int i = off; i < off + dataLength; i += 3)
081            {
082                a1 = data[i] & 0xff;
083                a2 = data[i + 1] & 0xff;
084                a3 = data[i + 2] & 0xff;
085    
086                out.write(encodingTable[(a1 >>> 2) & 0x3f]);
087                out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
088                out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
089                out.write(encodingTable[a3 & 0x3f]);
090            }
091    
092            /*
093             * process the tail end.
094             */
095            int    b1, b2, b3;
096            int    d1, d2;
097    
098            switch (modulus)
099            {
100            case 0:        /* nothing left to do */
101                break;
102            case 1:
103                d1 = data[off + dataLength] & 0xff;
104                b1 = (d1 >>> 2) & 0x3f;
105                b2 = (d1 << 4) & 0x3f;
106    
107                out.write(encodingTable[b1]);
108                out.write(encodingTable[b2]);
109                out.write(padding);
110                out.write(padding);
111                break;
112            case 2:
113                d1 = data[off + dataLength] & 0xff;
114                d2 = data[off + dataLength + 1] & 0xff;
115    
116                b1 = (d1 >>> 2) & 0x3f;
117                b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
118                b3 = (d2 << 2) & 0x3f;
119    
120                out.write(encodingTable[b1]);
121                out.write(encodingTable[b2]);
122                out.write(encodingTable[b3]);
123                out.write(padding);
124                break;
125            }
126    
127            return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
128        }
129    
130        private boolean ignore(
131            char    c)
132        {
133            return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
134        }
135    
136        /**
137         * decode the base 64 encoded byte data writing it to the given output stream,
138         * whitespace characters will be ignored.
139         *
140         * @return the number of bytes produced.
141         */
142        public int decode(
143            byte[]                data,
144            int                    off,
145            int                    length,
146            OutputStream    out)
147            throws IOException
148        {
149            byte[]    bytes;
150            byte    b1, b2, b3, b4;
151            int        outLen = 0;
152    
153            int        end = off + length;
154    
155            while (end > 0)
156            {
157                if (!ignore((char)data[end - 1]))
158                {
159                    break;
160                }
161    
162                end--;
163            }
164    
165            int  i = off;
166            int  finish = end - 4;
167    
168            while (i < finish)
169            {
170                while ((i < finish) && ignore((char)data[i]))
171                {
172                    i++;
173                }
174    
175                b1 = decodingTable[data[i++]];
176    
177                while ((i < finish) && ignore((char)data[i]))
178                {
179                    i++;
180                }
181    
182                b2 = decodingTable[data[i++]];
183    
184                while ((i < finish) && ignore((char)data[i]))
185                {
186                    i++;
187                }
188    
189                b3 = decodingTable[data[i++]];
190    
191                while ((i < finish) && ignore((char)data[i]))
192                {
193                    i++;
194                }
195    
196                b4 = decodingTable[data[i++]];
197    
198                out.write((b1 << 2) | (b2 >> 4));
199                out.write((b2 << 4) | (b3 >> 2));
200                out.write((b3 << 6) | b4);
201    
202                outLen += 3;
203            }
204    
205            if (data[end - 2] == padding)
206            {
207                b1 = decodingTable[data[end - 4]];
208                b2 = decodingTable[data[end - 3]];
209    
210                out.write((b1 << 2) | (b2 >> 4));
211    
212                outLen += 1;
213            }
214            else if (data[end - 1] == padding)
215            {
216                b1 = decodingTable[data[end - 4]];
217                b2 = decodingTable[data[end - 3]];
218                b3 = decodingTable[data[end - 2]];
219    
220                out.write((b1 << 2) | (b2 >> 4));
221                out.write((b2 << 4) | (b3 >> 2));
222    
223                outLen += 2;
224            }
225            else
226            {
227                b1 = decodingTable[data[end - 4]];
228                b2 = decodingTable[data[end - 3]];
229                b3 = decodingTable[data[end - 2]];
230                b4 = decodingTable[data[end - 1]];
231    
232                out.write((b1 << 2) | (b2 >> 4));
233                out.write((b2 << 4) | (b3 >> 2));
234                out.write((b3 << 6) | b4);
235    
236                outLen += 3;
237            }
238    
239            return outLen;
240        }
241    
242        /**
243         * decode the base 64 encoded String data writing it to the given output stream,
244         * whitespace characters will be ignored.
245         *
246         * @return the number of bytes produced.
247         */
248        public int decode(
249            String                data,
250            OutputStream    out)
251            throws IOException
252        {
253            byte[]    bytes;
254            byte    b1, b2, b3, b4;
255            int        length = 0;
256    
257            int        end = data.length();
258    
259            while (end > 0)
260            {
261                if (!ignore(data.charAt(end - 1)))
262                {
263                    break;
264                }
265    
266                end--;
267            }
268    
269            int    i = 0;
270            int   finish = end - 4;
271    
272            while (i < finish)
273            {
274                while ((i < finish) && ignore(data.charAt(i)))
275                {
276                    i++;
277                }
278    
279                b1 = decodingTable[data.charAt(i++)];
280    
281                while ((i < finish) && ignore(data.charAt(i)))
282                {
283                    i++;
284                }
285                b2 = decodingTable[data.charAt(i++)];
286    
287                while ((i < finish) && ignore(data.charAt(i)))
288                {
289                    i++;
290                }
291                b3 = decodingTable[data.charAt(i++)];
292    
293                while ((i < finish) && ignore(data.charAt(i)))
294                {
295                    i++;
296                }
297                b4 = decodingTable[data.charAt(i++)];
298    
299                out.write((b1 << 2) | (b2 >> 4));
300                out.write((b2 << 4) | (b3 >> 2));
301                out.write((b3 << 6) | b4);
302    
303                length += 3;
304            }
305    
306            if (data.charAt(end - 2) == padding)
307            {
308                b1 = decodingTable[data.charAt(end - 4)];
309                b2 = decodingTable[data.charAt(end - 3)];
310    
311                out.write((b1 << 2) | (b2 >> 4));
312    
313                length += 1;
314            }
315            else if (data.charAt(end - 1) == padding)
316            {
317                b1 = decodingTable[data.charAt(end - 4)];
318                b2 = decodingTable[data.charAt(end - 3)];
319                b3 = decodingTable[data.charAt(end - 2)];
320    
321                out.write((b1 << 2) | (b2 >> 4));
322                out.write((b2 << 4) | (b3 >> 2));
323    
324                length += 2;
325            }
326            else
327            {
328                b1 = decodingTable[data.charAt(end - 4)];
329                b2 = decodingTable[data.charAt(end - 3)];
330                b3 = decodingTable[data.charAt(end - 2)];
331                b4 = decodingTable[data.charAt(end - 1)];
332    
333                out.write((b1 << 2) | (b2 >> 4));
334                out.write((b2 << 4) | (b3 >> 2));
335                out.write((b3 << 6) | b4);
336    
337                length += 3;
338            }
339    
340            return length;
341        }
342    
343        /**
344         * decode the base 64 encoded byte data writing it to the provided byte array buffer.
345         *
346         * @return the number of bytes produced.
347         */
348        public int decode(byte[] data, int off, int length, byte[] out) throws IOException
349        {
350            byte[]    bytes;
351            byte    b1, b2, b3, b4;
352            int        outLen = 0;
353    
354            int        end = off + length;
355    
356            while (end > 0)
357            {
358                if (!ignore((char)data[end - 1]))
359                {
360                    break;
361                }
362    
363                end--;
364            }
365    
366            int  i = off;
367            int  finish = end - 4;
368    
369            while (i < finish)
370            {
371                while ((i < finish) && ignore((char)data[i]))
372                {
373                    i++;
374                }
375    
376                b1 = decodingTable[data[i++]];
377    
378                while ((i < finish) && ignore((char)data[i]))
379                {
380                    i++;
381                }
382    
383                b2 = decodingTable[data[i++]];
384    
385                while ((i < finish) && ignore((char)data[i]))
386                {
387                    i++;
388                }
389    
390                b3 = decodingTable[data[i++]];
391    
392                while ((i < finish) && ignore((char)data[i]))
393                {
394                    i++;
395                }
396    
397                b4 = decodingTable[data[i++]];
398    
399                out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
400                out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
401                out[outLen++] = (byte)((b3 << 6) | b4);
402            }
403    
404            if (data[end - 2] == padding)
405            {
406                b1 = decodingTable[data[end - 4]];
407                b2 = decodingTable[data[end - 3]];
408    
409                out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
410            }
411            else if (data[end - 1] == padding)
412            {
413                b1 = decodingTable[data[end - 4]];
414                b2 = decodingTable[data[end - 3]];
415                b3 = decodingTable[data[end - 2]];
416    
417                out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
418                out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
419            }
420            else
421            {
422                b1 = decodingTable[data[end - 4]];
423                b2 = decodingTable[data[end - 3]];
424                b3 = decodingTable[data[end - 2]];
425                b4 = decodingTable[data[end - 1]];
426    
427                out[outLen++] = (byte)((b1 << 2) | (b2 >> 4));
428                out[outLen++] = (byte)((b2 << 4) | (b3 >> 2));
429                out[outLen++] = (byte)((b3 << 6) | b4);
430            }
431    
432            return outLen;
433        }
434    
435        /**
436         * Test if a character is a valid Base64 encoding character.  This
437         * must be either a valid digit or the padding character ("=").
438         *
439         * @param ch     The test character.
440         *
441         * @return true if this is valid in Base64 encoded data, false otherwise.
442         */
443        public boolean isValidBase64(int ch) {
444            // 'A' has the value 0 in the decoding table, so we need a special one for that
445            return ch == padding || ch == 'A' || decodingTable[ch] != 0;
446        }
447    
448    
449        /**
450         * Perform RFC-2047 word encoding using Base64 data encoding.
451         *
452         * @param in      The source for the encoded data.
453         * @param charset The charset tag to be added to each encoded data section.
454         * @param out     The output stream where the encoded data is to be written.
455         * @param fold    Controls whether separate sections of encoded data are separated by
456         *                linebreaks or whitespace.
457         *
458         * @exception IOException
459         */
460        public void encodeWord(InputStream in, String charset, OutputStream out, boolean fold) throws IOException
461        {
462            PrintStream writer = new PrintStream(out);
463    
464            // encoded words are restricted to 76 bytes, including the control adornments.
465            int limit = 76 - 7 - charset.length();
466            boolean firstLine = true;
467            StringBuffer encodedString = new StringBuffer(76);
468    
469            while (true) {
470                // encode the next segment.
471                encode(in, encodedString, limit);
472                // if we're out of data, nothing will be encoded.
473                if (encodedString.length() == 0) {
474                    break;
475                }
476    
477                // if we have more than one segment, we need to insert separators.  Depending on whether folding
478                // was requested, this is either a blank or a linebreak.
479                if (!firstLine) {
480                    if (fold) {
481                        writer.print("\r\n");
482                    }
483                    else {
484                        writer.print(" ");
485                    }
486                }
487    
488                // add the encoded word header
489                writer.print("=?");
490                writer.print(charset);
491                writer.print("?B?");
492                // the data
493                writer.print(encodedString.toString());
494                // and the word terminator.
495                writer.print("?=");
496                writer.flush();
497    
498                // reset our string buffer for the next segment.
499                encodedString.setLength(0);
500            }
501        }
502    
503        /**
504         * encode the input data producing a base 64 output stream.
505         *
506         * @return the number of bytes produced.
507         */
508        public void encode(InputStream in, StringBuffer out, int limit) throws IOException
509        {
510            int count = limit / 4;
511            byte [] inBuffer = new byte[3];
512    
513            while (count-- > 0) {
514    
515                int readCount = in.read(inBuffer);
516                // did we get a full triplet?  that's an easy encoding.
517                if (readCount == 3) {
518                    int  a1 = inBuffer[0] & 0xff;
519                    int  a2 = inBuffer[1] & 0xff;
520                    int  a3 = inBuffer[2] & 0xff;
521    
522                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
523                    out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
524                    out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
525                    out.append((char)encodingTable[a3 & 0x3f]);
526    
527                }
528                else if (readCount <= 0) {
529                    // eof condition, don'e entirely.
530                    return;
531                }
532                else if (readCount == 1) {
533                    int  a1 = inBuffer[0] & 0xff;
534                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
535                    out.append((char)encodingTable[(a1 << 4) & 0x3f]);
536                    out.append((char)padding);
537                    out.append((char)padding);
538                    return;
539                }
540                else if (readCount == 2) {
541                    int  a1 = inBuffer[0] & 0xff;
542                    int  a2 = inBuffer[1] & 0xff;
543    
544                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
545                    out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
546                    out.append((char)encodingTable[(a2 << 2) & 0x3f]);
547                    out.append((char)padding);
548                    return;
549                }
550            }
551        }
552    }