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