001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.geronimo.util.encoders; 019 020 import java.io.IOException; 021 import java.io.OutputStream; 022 023 public class Base64Encoder 024 implements Encoder 025 { 026 protected final byte[] encodingTable = 027 { 028 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', 029 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', 030 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', 031 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', 032 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', 033 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', 034 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', 035 (byte)'v', 036 (byte)'w', (byte)'x', (byte)'y', (byte)'z', 037 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', 038 (byte)'7', (byte)'8', (byte)'9', 039 (byte)'+', (byte)'/' 040 }; 041 042 protected byte padding = (byte)'='; 043 044 /* 045 * set up the decoding table. 046 */ 047 protected final byte[] decodingTable = new byte[128]; 048 049 protected void initialiseDecodingTable() 050 { 051 for (int i = 0; i < encodingTable.length; i++) 052 { 053 decodingTable[encodingTable[i]] = (byte)i; 054 } 055 } 056 057 public Base64Encoder() 058 { 059 initialiseDecodingTable(); 060 } 061 062 /** 063 * encode the input data producing a base 64 output stream. 064 * 065 * @return the number of bytes produced. 066 */ 067 public int encode( 068 byte[] data, 069 int off, 070 int length, 071 OutputStream out) 072 throws IOException 073 { 074 int modulus = length % 3; 075 int dataLength = (length - modulus); 076 int a1, a2, a3; 077 078 for (int i = off; i < off + dataLength; i += 3) 079 { 080 a1 = data[i] & 0xff; 081 a2 = data[i + 1] & 0xff; 082 a3 = data[i + 2] & 0xff; 083 084 out.write(encodingTable[(a1 >>> 2) & 0x3f]); 085 out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]); 086 out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]); 087 out.write(encodingTable[a3 & 0x3f]); 088 } 089 090 /* 091 * process the tail end. 092 */ 093 int b1, b2, b3; 094 int d1, d2; 095 096 switch (modulus) 097 { 098 case 0: /* nothing left to do */ 099 break; 100 case 1: 101 d1 = data[off + dataLength] & 0xff; 102 b1 = (d1 >>> 2) & 0x3f; 103 b2 = (d1 << 4) & 0x3f; 104 105 out.write(encodingTable[b1]); 106 out.write(encodingTable[b2]); 107 out.write(padding); 108 out.write(padding); 109 break; 110 case 2: 111 d1 = data[off + dataLength] & 0xff; 112 d2 = data[off + dataLength + 1] & 0xff; 113 114 b1 = (d1 >>> 2) & 0x3f; 115 b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f; 116 b3 = (d2 << 2) & 0x3f; 117 118 out.write(encodingTable[b1]); 119 out.write(encodingTable[b2]); 120 out.write(encodingTable[b3]); 121 out.write(padding); 122 break; 123 } 124 125 return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4); 126 } 127 128 private boolean ignore( 129 char c) 130 { 131 return (c == '\n' || c =='\r' || c == '\t' || c == ' '); 132 } 133 134 /** 135 * decode the base 64 encoded byte data writing it to the given output stream, 136 * whitespace characters will be ignored. 137 * 138 * @return the number of bytes produced. 139 */ 140 public int decode( 141 byte[] data, 142 int off, 143 int length, 144 OutputStream out) 145 throws IOException 146 { 147 byte[] bytes; 148 byte b1, b2, b3, b4; 149 int outLen = 0; 150 151 int end = off + length; 152 153 while (end > 0) 154 { 155 if (!ignore((char)data[end - 1])) 156 { 157 break; 158 } 159 160 end--; 161 } 162 163 int i = off; 164 int finish = end - 4; 165 166 while (i < finish) 167 { 168 while ((i < finish) && ignore((char)data[i])) 169 { 170 i++; 171 } 172 173 b1 = decodingTable[data[i++]]; 174 175 while ((i < finish) && ignore((char)data[i])) 176 { 177 i++; 178 } 179 180 b2 = decodingTable[data[i++]]; 181 182 while ((i < finish) && ignore((char)data[i])) 183 { 184 i++; 185 } 186 187 b3 = decodingTable[data[i++]]; 188 189 while ((i < finish) && ignore((char)data[i])) 190 { 191 i++; 192 } 193 194 b4 = decodingTable[data[i++]]; 195 196 out.write((b1 << 2) | (b2 >> 4)); 197 out.write((b2 << 4) | (b3 >> 2)); 198 out.write((b3 << 6) | b4); 199 200 outLen += 3; 201 } 202 203 if (data[end - 2] == padding) 204 { 205 b1 = decodingTable[data[end - 4]]; 206 b2 = decodingTable[data[end - 3]]; 207 208 out.write((b1 << 2) | (b2 >> 4)); 209 210 outLen += 1; 211 } 212 else if (data[end - 1] == padding) 213 { 214 b1 = decodingTable[data[end - 4]]; 215 b2 = decodingTable[data[end - 3]]; 216 b3 = decodingTable[data[end - 2]]; 217 218 out.write((b1 << 2) | (b2 >> 4)); 219 out.write((b2 << 4) | (b3 >> 2)); 220 221 outLen += 2; 222 } 223 else 224 { 225 b1 = decodingTable[data[end - 4]]; 226 b2 = decodingTable[data[end - 3]]; 227 b3 = decodingTable[data[end - 2]]; 228 b4 = decodingTable[data[end - 1]]; 229 230 out.write((b1 << 2) | (b2 >> 4)); 231 out.write((b2 << 4) | (b3 >> 2)); 232 out.write((b3 << 6) | b4); 233 234 outLen += 3; 235 } 236 237 return outLen; 238 } 239 240 /** 241 * decode the base 64 encoded String data writing it to the given output stream, 242 * whitespace characters will be ignored. 243 * 244 * @return the number of bytes produced. 245 */ 246 public int decode( 247 String data, 248 OutputStream out) 249 throws IOException 250 { 251 byte[] bytes; 252 byte b1, b2, b3, b4; 253 int length = 0; 254 255 int end = data.length(); 256 257 while (end > 0) 258 { 259 if (!ignore(data.charAt(end - 1))) 260 { 261 break; 262 } 263 264 end--; 265 } 266 267 int i = 0; 268 int finish = end - 4; 269 270 while (i < finish) 271 { 272 while ((i < finish) && ignore(data.charAt(i))) 273 { 274 i++; 275 } 276 277 b1 = decodingTable[data.charAt(i++)]; 278 279 while ((i < finish) && ignore(data.charAt(i))) 280 { 281 i++; 282 } 283 b2 = decodingTable[data.charAt(i++)]; 284 285 while ((i < finish) && ignore(data.charAt(i))) 286 { 287 i++; 288 } 289 b3 = decodingTable[data.charAt(i++)]; 290 291 while ((i < finish) && ignore(data.charAt(i))) 292 { 293 i++; 294 } 295 b4 = decodingTable[data.charAt(i++)]; 296 297 out.write((b1 << 2) | (b2 >> 4)); 298 out.write((b2 << 4) | (b3 >> 2)); 299 out.write((b3 << 6) | b4); 300 301 length += 3; 302 } 303 304 if (data.charAt(end - 2) == padding) 305 { 306 b1 = decodingTable[data.charAt(end - 4)]; 307 b2 = decodingTable[data.charAt(end - 3)]; 308 309 out.write((b1 << 2) | (b2 >> 4)); 310 311 length += 1; 312 } 313 else if (data.charAt(end - 1) == padding) 314 { 315 b1 = decodingTable[data.charAt(end - 4)]; 316 b2 = decodingTable[data.charAt(end - 3)]; 317 b3 = decodingTable[data.charAt(end - 2)]; 318 319 out.write((b1 << 2) | (b2 >> 4)); 320 out.write((b2 << 4) | (b3 >> 2)); 321 322 length += 2; 323 } 324 else 325 { 326 b1 = decodingTable[data.charAt(end - 4)]; 327 b2 = decodingTable[data.charAt(end - 3)]; 328 b3 = decodingTable[data.charAt(end - 2)]; 329 b4 = decodingTable[data.charAt(end - 1)]; 330 331 out.write((b1 << 2) | (b2 >> 4)); 332 out.write((b2 << 4) | (b3 >> 2)); 333 out.write((b3 << 6) | b4); 334 335 length += 3; 336 } 337 338 return length; 339 } 340 }