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 = 76 - 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            }
503        }
504    
505        /**
506         * encode the input data producing a base 64 output stream.
507         *
508         * @return the number of bytes produced.
509         */
510        public void encode(InputStream in, StringBuffer out, int limit) throws IOException
511        {
512            int count = limit / 4;
513            byte [] inBuffer = new byte[3];
514    
515            while (count-- > 0) {
516    
517                int readCount = in.read(inBuffer);
518                // did we get a full triplet?  that's an easy encoding.
519                if (readCount == 3) {
520                    int  a1 = inBuffer[0] & 0xff;
521                    int  a2 = inBuffer[1] & 0xff;
522                    int  a3 = inBuffer[2] & 0xff;
523    
524                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
525                    out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
526                    out.append((char)encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
527                    out.append((char)encodingTable[a3 & 0x3f]);
528    
529                }
530                else if (readCount <= 0) {
531                    // eof condition, don'e entirely.
532                    return;
533                }
534                else if (readCount == 1) {
535                    int  a1 = inBuffer[0] & 0xff;
536                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
537                    out.append((char)encodingTable[(a1 << 4) & 0x3f]);
538                    out.append((char)padding);
539                    out.append((char)padding);
540                    return;
541                }
542                else if (readCount == 2) {
543                    int  a1 = inBuffer[0] & 0xff;
544                    int  a2 = inBuffer[1] & 0xff;
545    
546                    out.append((char)encodingTable[(a1 >>> 2) & 0x3f]);
547                    out.append((char)encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
548                    out.append((char)encodingTable[(a2 << 2) & 0x3f]);
549                    out.append((char)padding);
550                    return;
551                }
552            }
553        }
554    }