View Javadoc
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 &gt; 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 }