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 }