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