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}