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