1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.foxinmy.weixin4j.base64; 19 20 import io.netty.handler.codec.DecoderException; 21 import io.netty.handler.codec.EncoderException; 22 23 import java.util.Arrays; 24 25 import com.foxinmy.weixin4j.util.ServerToolkits; 26 27 /** 28 * <p> 29 * <font color="red">reference of apache pivot</font> 30 * </p> 31 * 32 * Abstract superclass for Base-N encoders and decoders. 33 * 34 * <p> 35 * This class is thread-safe. 36 * </p> 37 * 38 * @version $Id: BaseNCodec.java 1465182 2013-04-06 04:03:12Z ggregory $ 39 */ 40 public abstract class BaseNCodec { 41 42 /** 43 * Holds thread context so classes can be thread-safe. 44 * 45 * This class is not itself thread-safe; each thread must allocate its own 46 * copy. 47 * 48 * @since 1.7 49 */ 50 static class Context { 51 52 /** 53 * Place holder for the bytes we're dealing with for our based logic. 54 * Bitwise operations store and extract the encoding or decoding from 55 * this variable. 56 */ 57 int ibitWorkArea; 58 59 /** 60 * Place holder for the bytes we're dealing with for our based logic. 61 * Bitwise operations store and extract the encoding or decoding from 62 * this variable. 63 */ 64 long lbitWorkArea; 65 66 /** 67 * Buffer for streaming. 68 */ 69 byte[] buffer; 70 71 /** 72 * Position where next character should be written in the buffer. 73 */ 74 int pos; 75 76 /** 77 * Position where next character should be read from the buffer. 78 */ 79 int readPos; 80 81 /** 82 * Boolean flag to indicate the EOF has been reached. Once EOF has been 83 * reached, this object becomes useless, and must be thrown away. 84 */ 85 boolean eof; 86 87 /** 88 * Variable tracks how many characters have been written to the current 89 * line. Only used when encoding. We use it to make sure each encoded 90 * line never goes beyond lineLength (if lineLength > 0). 91 */ 92 int currentLinePos; 93 94 /** 95 * Writes to the buffer only occur after every 3/5 reads when encoding, 96 * and every 4/8 reads when decoding. This variable helps track that. 97 */ 98 int modulus; 99 100 Context() { 101 } 102 103 /** 104 * Returns a String useful for debugging (especially within a debugger.) 105 * 106 * @return a String useful for debugging. 107 */ 108 @SuppressWarnings("boxing") 109 // OK to ignore boxing here 110 @Override 111 public String toString() { 112 return String.format( 113 "%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, " 114 + "modulus=%s, pos=%s, readPos=%s]", this 115 .getClass().getSimpleName(), Arrays 116 .toString(buffer), currentLinePos, eof, 117 ibitWorkArea, lbitWorkArea, modulus, pos, readPos); 118 } 119 } 120 121 /** 122 * EOF 123 * 124 * @since 1.7 125 */ 126 static final int EOF = -1; 127 128 /** 129 * MIME chunk size per RFC 2045 section 6.8. 130 * 131 * <p> 132 * The {@value} character limit does not count the trailing CRLF, but counts 133 * all other characters, including any equal signs. 134 * </p> 135 * 136 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 137 * 6.8</a> 138 */ 139 public static final int MIME_CHUNK_SIZE = 76; 140 141 /** 142 * PEM chunk size per RFC 1421 section 4.3.2.4. 143 * 144 * <p> 145 * The {@value} character limit does not count the trailing CRLF, but counts 146 * all other characters, including any equal signs. 147 * </p> 148 * 149 * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421 section 150 * 4.3.2.4</a> 151 */ 152 public static final int PEM_CHUNK_SIZE = 64; 153 154 private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; 155 156 /** 157 * Defines the default buffer size - currently {@value} - must be large 158 * enough for at least one encoded block+separator 159 */ 160 private static final int DEFAULT_BUFFER_SIZE = 8192; 161 162 /** Mask used to extract 8 bits, used in decoding bytes */ 163 protected static final int MASK_8BITS = 0xff; 164 165 /** 166 * Byte used to pad output. 167 */ 168 protected static final byte PAD_DEFAULT = '='; // Allow static access to 169 // default 170 171 protected final byte PAD = PAD_DEFAULT; // instance variable just in case it 172 // needs to vary later 173 174 /** 175 * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 176 * and 5 for Base32 177 */ 178 private final int unencodedBlockSize; 179 180 /** 181 * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 182 * 8 for Base32 183 */ 184 private final int encodedBlockSize; 185 186 /** 187 * Chunksize for encoding. Not used when decoding. A value of zero or less 188 * implies no chunking of the encoded data. Rounded down to nearest multiple 189 * of encodedBlockSize. 190 */ 191 protected final int lineLength; 192 193 /** 194 * Size of chunk separator. Not used unless {@link #lineLength} > 0. 195 */ 196 private final int chunkSeparatorLength; 197 198 /** 199 * Note <code>lineLength</code> is rounded down to the nearest multiple of 200 * {@link #encodedBlockSize} If <code>chunkSeparatorLength</code> is zero, 201 * then chunking is disabled. 202 * 203 * @param unencodedBlockSize 204 * the size of an unencoded block (e.g. Base64 = 3) 205 * @param encodedBlockSize 206 * the size of an encoded block (e.g. Base64 = 4) 207 * @param lineLength 208 * if > 0, use chunking with a length <code>lineLength</code> 209 * @param chunkSeparatorLength 210 * the chunk separator length, if relevant 211 */ 212 protected BaseNCodec(final int unencodedBlockSize, 213 final int encodedBlockSize, final int lineLength, 214 final int chunkSeparatorLength) { 215 this.unencodedBlockSize = unencodedBlockSize; 216 this.encodedBlockSize = encodedBlockSize; 217 final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0; 218 this.lineLength = useChunking ? (lineLength / encodedBlockSize) 219 * encodedBlockSize : 0; 220 this.chunkSeparatorLength = chunkSeparatorLength; 221 } 222 223 /** 224 * Returns true if this object has buffered data for reading. 225 * 226 * @param context 227 * the context to be used 228 * @return true if there is data still available for reading. 229 */ 230 boolean hasData(final Context context) { // package protected for access 231 // from I/O streams 232 return context.buffer != null; 233 } 234 235 /** 236 * Returns the amount of buffered data available for reading. 237 * 238 * @param context 239 * the context to be used 240 * @return The amount of buffered data available for reading. 241 */ 242 int available(final Context context) { // package protected for access from 243 // I/O streams 244 return context.buffer != null ? context.pos - context.readPos : 0; 245 } 246 247 /** 248 * Get the default buffer size. Can be overridden. 249 * 250 * @return {@link #DEFAULT_BUFFER_SIZE} 251 */ 252 protected int getDefaultBufferSize() { 253 return DEFAULT_BUFFER_SIZE; 254 } 255 256 /** 257 * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}. 258 * 259 * @param context 260 * the context to be used 261 */ 262 private byte[] resizeBuffer(final Context context) { 263 if (context.buffer == null) { 264 context.buffer = new byte[getDefaultBufferSize()]; 265 context.pos = 0; 266 context.readPos = 0; 267 } else { 268 final byte[] b = new byte[context.buffer.length 269 * DEFAULT_BUFFER_RESIZE_FACTOR]; 270 System.arraycopy(context.buffer, 0, b, 0, context.buffer.length); 271 context.buffer = b; 272 } 273 return context.buffer; 274 } 275 276 /** 277 * Ensure that the buffer has room for <code>size</code> bytes 278 * 279 * @param size 280 * minimum spare space required 281 * @param context 282 * the context to be used 283 */ 284 protected byte[] ensureBufferSize(final int size, final Context context) { 285 if ((context.buffer == null) 286 || (context.buffer.length < context.pos + size)) { 287 return resizeBuffer(context); 288 } 289 return context.buffer; 290 } 291 292 /** 293 * Extracts buffered data into the provided byte[] array, starting at 294 * position bPos, up to a maximum of bAvail bytes. Returns how many bytes 295 * were actually extracted. 296 * <p> 297 * Package protected for access from I/O streams. 298 * 299 * @param b 300 * byte[] array to extract the buffered data into. 301 * @param bPos 302 * position in byte[] array to start extraction at. 303 * @param bAvail 304 * amount of bytes we're allowed to extract. We may extract fewer 305 * (if fewer are available). 306 * @param context 307 * the context to be used 308 * @return The number of bytes successfully extracted into the provided 309 * byte[] array. 310 */ 311 int readResults(final byte[] b, final int bPos, final int bAvail, 312 final Context context) { 313 if (context.buffer != null) { 314 final int len = Math.min(available(context), bAvail); 315 System.arraycopy(context.buffer, context.readPos, b, bPos, len); 316 context.readPos += len; 317 if (context.readPos >= context.pos) { 318 context.buffer = null; // so hasData() will return false, and 319 // this method can return -1 320 } 321 return len; 322 } 323 return context.eof ? EOF : 0; 324 } 325 326 /** 327 * Checks if a byte value is whitespace or not. Whitespace is taken to mean: 328 * space, tab, CR, LF 329 * 330 * @param byteToCheck 331 * the byte to check 332 * @return true if byte is whitespace, false otherwise 333 */ 334 protected static boolean isWhiteSpace(final byte byteToCheck) { 335 switch (byteToCheck) { 336 case ' ': 337 case '\n': 338 case '\r': 339 case '\t': 340 return true; 341 default: 342 return false; 343 } 344 } 345 346 /** 347 * Encodes an Object using the Base-N algorithm. This method is provided in 348 * order to satisfy the requirements of the Encoder interface, and will 349 * throw an EncoderException if the supplied object is not of type byte[]. 350 * 351 * @param obj 352 * Object to encode 353 * @return An object (of type byte[]) containing the Base-N encoded data 354 * which corresponds to the byte[] supplied. 355 * @throws EncoderException 356 * if the parameter supplied is not of type byte[] 357 */ 358 public Object encode(final Object obj) throws EncoderException { 359 if (!(obj instanceof byte[])) { 360 throw new EncoderException( 361 "Parameter supplied to Base-N encode is not a byte[]"); 362 } 363 return encode((byte[]) obj); 364 } 365 366 /** 367 * Encodes a byte[] containing binary data, into a String containing 368 * characters in the Base-N alphabet. Uses UTF8 encoding. 369 * 370 * @param pArray 371 * a byte array containing binary data 372 * @return A String containing only Base-N character data 373 */ 374 public String encodeToString(final byte[] pArray) { 375 return ServerToolkits.newStringUtf8(encode(pArray)); 376 } 377 378 /** 379 * Encodes a byte[] containing binary data, into a String containing 380 * characters in the appropriate alphabet. Uses UTF8 encoding. 381 * 382 * @param pArray 383 * a byte array containing binary data 384 * @return String containing only character data in the appropriate 385 * alphabet. 386 */ 387 public String encodeAsString(final byte[] pArray) { 388 return ServerToolkits.newStringUtf8(encode(pArray)); 389 } 390 391 /** 392 * Decodes an Object using the Base-N algorithm. This method is provided in 393 * order to satisfy the requirements of the Decoder interface, and will 394 * throw a DecoderException if the supplied object is not of type byte[] or 395 * String. 396 * 397 * @param obj 398 * Object to decode 399 * @return An object (of type byte[]) containing the binary data which 400 * corresponds to the byte[] or String supplied. 401 * @throws DecoderException 402 * if the parameter supplied is not of type byte[] 403 */ 404 public Object decode(final Object obj) throws DecoderException { 405 if (obj instanceof byte[]) { 406 return decode((byte[]) obj); 407 } else if (obj instanceof String) { 408 return decode((String) obj); 409 } else { 410 throw new DecoderException( 411 "Parameter supplied to Base-N decode is not a byte[] or a String"); 412 } 413 } 414 415 /** 416 * Decodes a String containing characters in the Base-N alphabet. 417 * 418 * @param pArray 419 * A String containing Base-N character data 420 * @return a byte array containing binary data 421 */ 422 public byte[] decode(final String pArray) { 423 return decode(ServerToolkits.getBytesUtf8(pArray)); 424 } 425 426 /** 427 * Decodes a byte[] containing characters in the Base-N alphabet. 428 * 429 * @param pArray 430 * A byte array containing Base-N character data 431 * @return a byte array containing binary data 432 */ 433 public byte[] decode(final byte[] pArray) { 434 if (pArray == null || pArray.length == 0) { 435 return pArray; 436 } 437 final Context context = new Context(); 438 decode(pArray, 0, pArray.length, context); 439 decode(pArray, 0, EOF, context); // Notify decoder of EOF. 440 final byte[] result = new byte[context.pos]; 441 readResults(result, 0, result.length, context); 442 return result; 443 } 444 445 /** 446 * Encodes a byte[] containing binary data, into a byte[] containing 447 * characters in the alphabet. 448 * 449 * @param pArray 450 * a byte array containing binary data 451 * @return A byte array containing only the basen alphabetic character data 452 */ 453 public byte[] encode(final byte[] pArray) { 454 if (pArray == null || pArray.length == 0) { 455 return pArray; 456 } 457 final Context context = new Context(); 458 encode(pArray, 0, pArray.length, context); 459 encode(pArray, 0, EOF, context); // Notify encoder of EOF. 460 final byte[] buf = new byte[context.pos - context.readPos]; 461 readResults(buf, 0, buf.length, context); 462 return buf; 463 } 464 465 // package protected for access from I/O streams 466 abstract void encode(byte[] pArray, int i, int length, Context context); 467 468 // package protected for access from I/O streams 469 abstract void decode(byte[] pArray, int i, int length, Context context); 470 471 /** 472 * Returns whether or not the <code>octet</code> is in the current alphabet. 473 * Does not allow whitespace or pad. 474 * 475 * @param value 476 * The value to test 477 * 478 * @return {@code true} if the value is defined in the current alphabet, 479 * {@code false} otherwise. 480 */ 481 protected abstract boolean isInAlphabet(byte value); 482 483 /** 484 * Tests a given byte array to see if it contains only valid characters 485 * within the alphabet. The method optionally treats whitespace and pad as 486 * valid. 487 * 488 * @param arrayOctet 489 * byte array to test 490 * @param allowWSPad 491 * if {@code true}, then whitespace and PAD are also allowed 492 * 493 * @return {@code true} if all bytes are valid characters in the alphabet or 494 * if the byte array is empty; {@code false}, otherwise 495 */ 496 public boolean isInAlphabet(final byte[] arrayOctet, 497 final boolean allowWSPad) { 498 for (int i = 0; i < arrayOctet.length; i++) { 499 if (!isInAlphabet(arrayOctet[i]) 500 && (!allowWSPad || (arrayOctet[i] != PAD) 501 && !isWhiteSpace(arrayOctet[i]))) { 502 return false; 503 } 504 } 505 return true; 506 } 507 508 /** 509 * Tests a given String to see if it contains only valid characters within 510 * the alphabet. The method treats whitespace and PAD as valid. 511 * 512 * @param basen 513 * String to test 514 * @return {@code true} if all characters in the String are valid characters 515 * in the alphabet or if the String is empty; {@code false}, 516 * otherwise 517 * @see #isInAlphabet(byte[], boolean) 518 */ 519 public boolean isInAlphabet(final String basen) { 520 return isInAlphabet(ServerToolkits.getBytesUtf8(basen), true); 521 } 522 523 /** 524 * Tests a given byte array to see if it contains any characters within the 525 * alphabet or PAD. 526 * 527 * Intended for use in checking line-ending arrays 528 * 529 * @param arrayOctet 530 * byte array to test 531 * @return {@code true} if any byte is a valid character in the alphabet or 532 * PAD; {@code false} otherwise 533 */ 534 protected boolean containsAlphabetOrPad(final byte[] arrayOctet) { 535 if (arrayOctet == null) { 536 return false; 537 } 538 for (final byte element : arrayOctet) { 539 if (PAD == element || isInAlphabet(element)) { 540 return true; 541 } 542 } 543 return false; 544 } 545 546 /** 547 * Calculates the amount of space needed to encode the supplied array. 548 * 549 * @param pArray 550 * byte[] array which will later be encoded 551 * 552 * @return amount of space needed to encoded the supplied array. Returns a 553 * long since a max-len array will require > Integer.MAX_VALUE 554 */ 555 public long getEncodedLength(final byte[] pArray) { 556 // Calculate non-chunked size - rounded up to allow for padding 557 // cast to long is needed to avoid possibility of overflow 558 long len = ((pArray.length + unencodedBlockSize - 1) / unencodedBlockSize) 559 * (long) encodedBlockSize; 560 if (lineLength > 0) { // We're using chunking 561 // Round up to nearest multiple 562 len += ((len + lineLength - 1) / lineLength) * chunkSeparatorLength; 563 } 564 return len; 565 } 566 }