001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * 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 */
018package org.jwat.common;
019
020import java.io.ByteArrayOutputStream;
021import java.util.Arrays;
022
023/**
024 * Base64 encoder/decoder implementation based on the specifications in
025 * rfc2045/rfc3548.
026 *
027 * @author nicl
028 */
029public class Base64 {
030
031    /** Ascii table used to encode. */
032    private static String encodeTab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
033
034    /** Table used to decode. */
035    public static byte[] decodeTab = new byte[ 256 ];
036
037    /** Populate decode table. */
038    static {
039        Arrays.fill( decodeTab, (byte)0xff );
040        for ( int i=0; i<encodeTab.length(); ++i ) {
041            decodeTab[ encodeTab.charAt( i ) ] = (byte)i;
042        }
043    }
044
045    /**
046     * Static class.
047     */
048    protected Base64() {
049    }
050
051    /**
052     * Decodes an encoded string.
053     * @param in encoded string.
054     * @param bStrict if true checks for the correct number of trailing "="
055     * @return decoded string or null
056     */
057    public static String decodeToString(String in, boolean bStrict) {
058        if (in == null) {
059            return null;
060        }
061        if (in.length() == 0) {
062            return "";
063        }
064
065        StringBuffer out = new StringBuffer( 256 );
066
067        boolean b = true;
068        int idx = 0;
069        char cin;
070        int mod = 0;
071        int cIdx;
072        int cout = 0;
073
074        /*
075         * Loop.
076         */
077
078        while ( b ) {
079            if ( idx < in.length() ) {
080                cin = in.charAt( idx );
081                if ( cin == '=' ) {
082                    b = false;
083                }
084                else {
085                    ++idx;
086                    cIdx = decodeTab[ cin ];
087                    if ( cIdx != -1 ) {
088                        switch ( mod ) {
089                            case 0:
090                                cout = cIdx << 2;
091                                break;
092                            case 1:
093                                cout |= (cIdx >> 4);
094                                out.append( (char)cout );
095                                cout = (cIdx << 4) & 255;
096                                break;
097                            case 2:
098                                cout |= (cIdx >> 2);
099                                out.append( (char)cout );
100                                cout = (cIdx << 6) & 255;
101                                break;
102                            case 3:
103                                cout |= cIdx;
104                                out.append( (char)cout );
105                                break;
106                        }
107                        mod = ( mod + 1 ) % 4;
108                    }
109                    else {
110                        return null;
111                    }
112                }
113            }
114            else {
115                b = false;
116            }
117        }
118
119        /*
120         * Padding.
121         */
122
123        if ( mod == 1 ) {
124            // In state 1 only 6 bits of the next 24 bit has been decoded.
125            return null;
126        }
127        if ( bStrict ) {
128            while ( mod != 0 && idx < in.length() && in.charAt( idx ) == '=' ) {
129                ++idx;
130                mod = ( mod + 1 ) % 4;
131            }
132            if (mod != 0 || idx < in.length() ) {
133                return null;
134            }
135        }
136        return out.toString();
137    }
138
139    /**
140     * Decodes an encoded string.
141     * @param in encoded string.
142     * @param bStrict if true checks for the correct number of trailing "="
143     * @return decoded byte array or null
144     */
145    public static byte[] decodeToArray(String in, boolean bStrict) {
146        if (in == null) {
147            return null;
148        }
149        if (in.length() == 0) {
150            return new byte[0];
151        }
152
153        ByteArrayOutputStream out = new ByteArrayOutputStream();
154
155        boolean b = true;
156        int idx = 0;
157        char cin;
158        int mod = 0;
159        int cIdx;
160        int cout = 0;
161
162        /*
163         * Loop.
164         */
165
166        while ( b ) {
167            if ( idx < in.length() ) {
168                cin = in.charAt( idx );
169                if ( cin == '=' ) {
170                    b = false;
171                }
172                else {
173                    ++idx;
174                    cIdx = decodeTab[ cin ];
175                    if ( cIdx != -1 ) {
176                        switch ( mod ) {
177                            case 0:
178                                cout = cIdx << 2;
179                                break;
180                            case 1:
181                                cout |= (cIdx >> 4);
182                                out.write( cout );
183                                cout = (cIdx << 4) & 255;
184                                break;
185                            case 2:
186                                cout |= (cIdx >> 2);
187                                out.write( cout );
188                                cout = (cIdx << 6) & 255;
189                                break;
190                            case 3:
191                                cout |= cIdx;
192                                out.write( cout );
193                                break;
194                        }
195                        mod = ( mod + 1 ) % 4;
196                    }
197                    else {
198                        return null;
199                    }
200                }
201            }
202            else {
203                b = false;
204            }
205        }
206
207        /*
208         * Padding.
209         */
210
211        if ( mod == 1 ) {
212            // In state 1 only 6 bits of the next 24 bit has been decoded.
213            return null;
214        }
215        if ( bStrict ) {
216            while ( mod != 0 && idx < in.length() && in.charAt( idx ) == '=' ) {
217                ++idx;
218                mod = ( mod + 1 ) % 4;
219            }
220            if (mod != 0 || idx < in.length() ) {
221                return null;
222            }
223        }
224        return out.toByteArray();
225    }
226
227    /**
228     * Encodes a string.
229     * @param in unencoded text string.
230     * @return encoded string or null
231     */
232    public static String encodeString(String in) {
233        if (in == null) {
234            return null;
235        }
236        if (in.length() == 0) {
237            return "";
238        }
239
240        StringBuffer out = new StringBuffer( 256 );
241
242        boolean b = true;
243        int idx = 0;
244        char cin;
245        int cout = 0;
246        int mod = 0;
247
248        /*
249         * Loop.
250         */
251
252        while ( b ) {
253            if ( idx < in.length() ) {
254                cin = in.charAt( idx++ );
255                if ( cin < 256 ) {
256                    switch ( mod ) {
257                    case 0:
258                        cout = ( cin >> 2 ) & 63;
259                        out.append( encodeTab.charAt( cout ) );
260                        cout = ( cin << 4 ) & 63;
261                        break;
262                    case 1:
263                        cout |= ( cin >> 4 ) & 63;
264                        out.append( encodeTab.charAt( cout ) );
265                        cout = ( cin << 2 ) & 63;
266                        break;
267                    case 2:
268                        cout |= ( cin >> 6 ) & 63;
269                        out.append( encodeTab.charAt( cout ) );
270                        cout = cin & 63;
271                        out.append( encodeTab.charAt( cout ) );
272                        break;
273                    }
274                    mod = ( mod + 1 ) % 3;
275                }
276                else {
277                    return null;
278                }
279            }
280            else {
281                b = false;
282            }
283        }
284
285        /*
286         * Padding.
287         */
288
289        switch( mod ) {
290            case 0:
291                break;
292            case 1:
293                out.append( encodeTab.charAt( cout ) );
294                out.append( "==" );
295                break;
296            case 2:
297                out.append( encodeTab.charAt( cout ) );
298                out.append( "=" );
299                break;
300        }
301
302        return out.toString();
303    }
304
305    /**
306     * Encodes a byte array.
307     * @param in byte array.
308     * @return encoded string or null
309     */
310    public static String encodeArray(byte[] in) {
311        if (in == null) {
312            return null;
313        }
314        if (in.length == 0) {
315            return "";
316        }
317
318        StringBuffer out = new StringBuffer( 256 );
319
320        boolean b = true;
321        int idx = 0;
322        int cin;
323        int cout = 0;
324        int mod = 0;
325
326        /*
327         * Loop.
328         */
329
330        while ( b ) {
331            if ( idx < in.length ) {
332                cin = in[ idx++ ] & 255;
333                switch ( mod ) {
334                    case 0:
335                        cout = ( cin >> 2 ) & 63;
336                        out.append( encodeTab.charAt( cout ) );
337                        cout = ( cin << 4 ) & 63;
338                        break;
339                    case 1:
340                        cout |= ( cin >> 4 ) & 63;
341                        out.append( encodeTab.charAt( cout ) );
342                        cout = ( cin << 2 ) & 63;
343                        break;
344                    case 2:
345                        cout |= ( cin >> 6 ) & 63;
346                        out.append( encodeTab.charAt( cout ) );
347                        cout = cin & 63;
348                        out.append( encodeTab.charAt( cout ) );
349                        break;
350                }
351                mod = ( mod + 1 ) % 3;
352            }
353            else {
354                b = false;
355            }
356        }
357
358        /*
359         * Padding.
360         */
361
362        switch( mod ) {
363            case 0:
364                break;
365            case 1:
366                out.append( encodeTab.charAt( cout ) );
367                out.append( "==" );
368                break;
369            case 2:
370                out.append( encodeTab.charAt( cout ) );
371                out.append( "=" );
372                break;
373        }
374
375        return out.toString();
376    }
377
378}