1 /* Base64.java -- Base64 encoding and decoding.
2    Copyright (C) 2006, 2007  Free Software Foundation, Inc.
3 
4 This file is a part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version.
37 
38 --
39 Base64 encoding derived from ISC's DHCP. Copyright notices from DHCP
40 follow. See http://www.isc.org/products/DHCP/.
41 
42 Copyright (c) 1996 by Internet Software Consortium.
43 
44 Permission to use, copy, modify, and distribute this software for any
45 purpose with or without fee is hereby granted, provided that the above
46 copyright notice and this permission notice appear in all copies.
47 
48 THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
49 DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
50 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
51 INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
52 INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
53 FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
54 NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
55 WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
56 
57 --
58 Portions Copyright (c) 1995 by International Business Machines, Inc.
59 
60 International Business Machines, Inc. (hereinafter called IBM) grants
61 permission under its copyrights to use, copy, modify, and distribute
62 this Software with or without fee, provided that the above copyright
63 notice and all paragraphs of this notice appear in all copies, and
64 that the name of IBM not be used in connection with the marketing of
65 any product incorporating the Software or modifications thereof,
66 without specific, written prior permission.
67 
68 To the extent it has a right to do so, IBM grants an immunity from
69 suit under its patents, if any, for the use, sale or manufacture of
70 products to the extent that such products are used for performing
71 Domain Name System dynamic updates in TCP/IP networks by means of the
72 Software.  No immunity is granted for any product per se or for any
73 other function of any product.
74 
75 THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
76 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
77 PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
78 DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
79 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
80 SOFTWARE, EVEN IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH
81 DAMAGES.  */
82 
83 
84 package gnu.java.util;
85 
86 import gnu.java.lang.CPStringBuilder;
87 
88 import java.io.ByteArrayOutputStream;
89 import java.io.IOException;
90 
91 public final class Base64
92 {
93 
94   // No constructor.
Base64()95   private Base64() { }
96 
97   // Class methods.
98   // -------------------------------------------------------------------------
99 
100   /** Base-64 characters. */
101   private static final String BASE_64 =
102     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
103 
104   /** Base-64 padding character. */
105   private static final char BASE_64_PAD = '=';
106 
107   /**
108    * Base64 encode a byte array, with no line wrapping.
109    *
110    * @param buf The byte array to encode.
111    * @return <tt>buf</tt> encoded in Base64.
112    */
encode(byte[] buf)113   public static String encode(byte[] buf)
114   {
115     return encode(buf, 0);
116   }
117 
118   /**
119    * Base64 encode a byte array, returning the returning string.
120    *
121    * @param buf The byte array to encode.
122    * @param tw  The total length of any line, 0 for unlimited.
123    * @return <tt>buf</tt> encoded in Base64.
124    */
encode(byte[] buf, int tw)125   public static String encode(byte[] buf, int tw)
126   {
127     return encode(buf, 0, buf.length, tw);
128   }
129 
130   /**
131    * Base64 encode a byte array, returning the returning string.
132    *
133    * @param buf The byte array to encode.
134    * @param offset The offset in the byte array to start.
135    * @param length The number of bytes to encode.
136    * @param tw The total length of any line, 0 for unlimited.
137    * @return <tt>buf</tt> encoded in Base64.
138    */
encode(byte[] buf, int offset, int length, int tw)139   public static String encode(byte[] buf, int offset, int length, int tw)
140   {
141     if (offset < 0 || length < 0 || offset + length > buf.length)
142       throw new ArrayIndexOutOfBoundsException(buf.length  + " "
143                                                + offset + " "
144                                                + length);
145     int srcLength = buf.length - offset;
146     byte[] input = new byte[3];
147     int[] output = new int[4];
148     CPStringBuilder out = new CPStringBuilder();
149     int i = offset;
150     int chars = 0;
151 
152     while (srcLength > 2)
153       {
154         input[0] = buf[i++];
155         input[1] = buf[i++];
156         input[2] = buf[i++];
157         srcLength -= 3;
158 
159         output[0] = (input[0] & 0xff) >>> 2;
160         output[1] = ((input[0] & 0x03) << 4) + ((input[1] & 0xff) >>> 4);
161         output[2] = ((input[1] & 0x0f) << 2) + ((input[2] & 0xff) >>> 6);
162         output[3] = input[2] & 0x3f;
163 
164         out.append(BASE_64.charAt(output[0]));
165         if (tw > 0 && ++chars % tw == 0)
166           {
167             out.append("\n");
168           }
169         out.append(BASE_64.charAt(output[1]));
170         if (tw > 0 && ++chars % tw == 0)
171           {
172             out.append("\n");
173           }
174         out.append(BASE_64.charAt(output[2]));
175         if (tw > 0 && ++chars % tw == 0)
176           {
177             out.append("\n");
178           }
179         out.append(BASE_64.charAt(output[3]));
180         if (tw > 0 && ++chars % tw == 0)
181           {
182             out.append("\n");
183           }
184       }
185 
186     if (srcLength != 0)
187       {
188         input[0] = input[1] = input[2] = 0;
189         for (int j = 0; j < srcLength; j++)
190           {
191             input[j] = buf[i+j];
192           }
193         output[0] = (input[0] & 0xff) >>> 2;
194         output[1] = ((input[0] & 0x03) << 4) + ((input[1] & 0xff) >>> 4);
195         output[2] = ((input[1] & 0x0f) << 2) + ((input[2] & 0xff) >>> 6);
196 
197         out.append(BASE_64.charAt(output[0]));
198         if (tw > 0 && ++chars % tw == 0)
199           {
200             out.append("\n");
201           }
202         out.append(BASE_64.charAt(output[1]));
203         if (tw > 0 && ++chars % tw == 0)
204           {
205             out.append("\n");
206           }
207         if (srcLength == 1)
208           {
209             out.append(BASE_64_PAD);
210           }
211         else
212           {
213             out.append(BASE_64.charAt(output[2]));
214           }
215         if (tw > 0 && ++chars % tw == 0)
216           {
217             out.append("\n");
218           }
219         out.append(BASE_64_PAD);
220         if (tw > 0 && ++chars % tw == 0)
221           {
222             out.append("\n");
223           }
224       }
225     if (tw > 0)
226       {
227         out.append("\n");
228       }
229 
230     return out.toString();
231   }
232 
233   /**
234    * Decode a Base-64 string into a byte array.
235    *
236    * @param b64 The Base-64 encoded string.
237    * @return The decoded bytes.
238    * @throws java.io.IOException If the argument is not a valid Base-64
239    *    encoding.
240    */
decode(String b64)241   public static byte[] decode(String b64) throws IOException
242   {
243     ByteArrayOutputStream result = new ByteArrayOutputStream(b64.length() / 3);
244     int state = 0, i;
245     byte temp = 0;
246 
247     for (i = 0; i < b64.length(); i++)
248       {
249         if (Character.isWhitespace(b64.charAt(i)))
250           {
251             continue;
252           }
253         if (b64.charAt(i) == BASE_64_PAD)
254           {
255             break;
256           }
257 
258         int pos = BASE_64.indexOf(b64.charAt(i));
259         if (pos < 0)
260           {
261             throw new IOException("non-Base64 character " + b64.charAt(i));
262           }
263         switch (state)
264           {
265           case 0:
266             temp = (byte) (pos - BASE_64.indexOf('A') << 2);
267             state = 1;
268             break;
269 
270           case 1:
271             temp |= (byte) (pos - BASE_64.indexOf('A') >>> 4);
272             result.write(temp);
273             temp = (byte) ((pos - BASE_64.indexOf('A') & 0x0f) << 4);
274             state = 2;
275             break;
276 
277           case 2:
278             temp |= (byte) ((pos - BASE_64.indexOf('A') & 0x7f) >>> 2);
279             result.write(temp);
280             temp = (byte) ((pos - BASE_64.indexOf('A') & 0x03) << 6);
281             state = 3;
282             break;
283 
284           case 3:
285             temp |= (byte) (pos - BASE_64.indexOf('A') & 0xff);
286             result.write(temp);
287             state = 0;
288             break;
289 
290           default:
291             throw new Error("this statement should be unreachable");
292           }
293       }
294 
295     if (i < b64.length() && b64.charAt(i) == BASE_64_PAD)
296       {
297         switch (state)
298           {
299           case 0:
300           case 1:
301             throw new IOException("malformed Base64 sequence");
302 
303           case 2:
304             i++;
305             for ( ; i < b64.length(); i++)
306               {
307                 if (!Character.isWhitespace(b64.charAt(i)))
308                   {
309                     break;
310                   }
311               }
312             // We must see a second pad character here.
313             if (b64.charAt(i) != BASE_64_PAD)
314               {
315                 throw new IOException("malformed Base64 sequence");
316               }
317             i++;
318             // Fall-through.
319 
320           case 3:
321             i++;
322             for ( ; i < b64.length(); i++)
323               {
324                 // We should only see whitespace after this.
325                 if (!Character.isWhitespace(b64.charAt(i)))
326                   {
327                     throw new IOException("malformed Base64 sequence");
328                   }
329               }
330           }
331       }
332     else
333       {
334         if (state != 0)
335           {
336             throw new IOException("malformed Base64 sequence");
337           }
338       }
339 
340     return result.toByteArray();
341   }
342 }
343