BCodec.java

  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.  *      https://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. package org.apache.commons.codec.net;

  18. import java.io.UnsupportedEncodingException;
  19. import java.nio.charset.Charset;
  20. import java.nio.charset.StandardCharsets;
  21. import java.nio.charset.UnsupportedCharsetException;

  22. import org.apache.commons.codec.CodecPolicy;
  23. import org.apache.commons.codec.DecoderException;
  24. import org.apache.commons.codec.EncoderException;
  25. import org.apache.commons.codec.StringDecoder;
  26. import org.apache.commons.codec.StringEncoder;
  27. import org.apache.commons.codec.binary.Base64;
  28. import org.apache.commons.codec.binary.BaseNCodec;

  29. /**
  30.  * Identical to the Base64 encoding defined by <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a>
  31.  * and allows a character set to be specified.
  32.  * <p>
  33.  * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
  34.  * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
  35.  * handling software.
  36.  * </p>
  37.  * <p>
  38.  * This class is immutable and thread-safe.
  39.  * </p>
  40.  *
  41.  * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
  42.  *          Header Extensions for Non-ASCII Text</a>
  43.  *
  44.  * @since 1.3
  45.  */
  46. public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder {

  47.     /**
  48.      * The default decoding policy.
  49.      */
  50.     private static final CodecPolicy DECODING_POLICY_DEFAULT = CodecPolicy.LENIENT;

  51.     /**
  52.      * If true then decoding should throw an exception for impossible combinations of bits at the
  53.      * end of the byte input. The default is to decode as much of them as possible.
  54.      */
  55.     private final CodecPolicy decodingPolicy;

  56.     /**
  57.      * Default constructor.
  58.      */
  59.     public BCodec() {
  60.         this(StandardCharsets.UTF_8);
  61.     }

  62.     /**
  63.      * Constructor which allows for the selection of a default Charset
  64.      *
  65.      * @param charset
  66.      *            the default string Charset to use.
  67.      *
  68.      * @see Charset
  69.      * @since 1.7
  70.      */
  71.     public BCodec(final Charset charset) {
  72.         this(charset, DECODING_POLICY_DEFAULT);
  73.     }

  74.     /**
  75.      * Constructor which allows for the selection of a default Charset.
  76.      *
  77.      * @param charset
  78.      *            the default string Charset to use.
  79.      * @param decodingPolicy The decoding policy.
  80.      * @see Charset
  81.      * @since 1.15
  82.      */
  83.     public BCodec(final Charset charset, final CodecPolicy decodingPolicy) {
  84.         super(charset);
  85.         this.decodingPolicy = decodingPolicy;
  86.     }

  87.     /**
  88.      * Constructor which allows for the selection of a default Charset
  89.      *
  90.      * @param charsetName
  91.      *            the default Charset to use.
  92.      * @throws java.nio.charset.UnsupportedCharsetException
  93.      *             If the named Charset is unavailable
  94.      * @since 1.7 throws UnsupportedCharsetException if the named Charset is unavailable
  95.      * @see Charset
  96.      */
  97.     public BCodec(final String charsetName) {
  98.         this(Charset.forName(charsetName));
  99.     }

  100.     /**
  101.      * Decodes a Base64 object into its original form. Escaped characters are converted back to their original
  102.      * representation.
  103.      *
  104.      * @param value
  105.      *            Base64 object to convert into its original form
  106.      * @return original object
  107.      * @throws DecoderException
  108.      *             Thrown if the argument is not a {@code String}. Thrown if a failure condition is encountered
  109.      *             during the decode process.
  110.      */
  111.     @Override
  112.     public Object decode(final Object value) throws DecoderException {
  113.         if (value == null) {
  114.             return null;
  115.         }
  116.         if (value instanceof String) {
  117.             return decode((String) value);
  118.         }
  119.         throw new DecoderException("Objects of type " + value.getClass().getName() + " cannot be decoded using BCodec");
  120.     }

  121.     /**
  122.      * Decodes a Base64 string into its original form. Escaped characters are converted back to their original
  123.      * representation.
  124.      *
  125.      * @param value
  126.      *            Base64 string to convert into its original form
  127.      * @return original string
  128.      * @throws DecoderException
  129.      *             A decoder exception is thrown if a failure condition is encountered during the decode process.
  130.      */
  131.     @Override
  132.     public String decode(final String value) throws DecoderException {
  133.         try {
  134.             return decodeText(value);
  135.         } catch (final UnsupportedEncodingException | IllegalArgumentException e) {
  136.             throw new DecoderException(e.getMessage(), e);
  137.         }
  138.     }

  139.     @Override
  140.     protected byte[] doDecoding(final byte[] bytes) {
  141.         if (bytes == null) {
  142.             return null;
  143.         }
  144.         return new Base64(0, BaseNCodec.getChunkSeparator(), false, decodingPolicy).decode(bytes);
  145.     }

  146.     @Override
  147.     protected byte[] doEncoding(final byte[] bytes) {
  148.         if (bytes == null) {
  149.             return null;
  150.         }
  151.         return Base64.encodeBase64(bytes);
  152.     }

  153.     /**
  154.      * Encodes an object into its Base64 form using the default Charset. Unsafe characters are escaped.
  155.      *
  156.      * @param value
  157.      *            object to convert to Base64 form
  158.      * @return Base64 object
  159.      * @throws EncoderException
  160.      *             thrown if a failure condition is encountered during the encoding process.
  161.      */
  162.     @Override
  163.     public Object encode(final Object value) throws EncoderException {
  164.         if (value == null) {
  165.             return null;
  166.         }
  167.         if (value instanceof String) {
  168.             return encode((String) value);
  169.         }
  170.         throw new EncoderException("Objects of type " + value.getClass().getName() + " cannot be encoded using BCodec");
  171.     }

  172.     /**
  173.      * Encodes a string into its Base64 form using the default Charset. Unsafe characters are escaped.
  174.      *
  175.      * @param strSource
  176.      *            string to convert to Base64 form
  177.      * @return Base64 string
  178.      * @throws EncoderException
  179.      *             thrown if a failure condition is encountered during the encoding process.
  180.      */
  181.     @Override
  182.     public String encode(final String strSource) throws EncoderException {
  183.         return encode(strSource, getCharset());
  184.     }

  185.     /**
  186.      * Encodes a string into its Base64 form using the specified Charset. Unsafe characters are escaped.
  187.      *
  188.      * @param strSource
  189.      *            string to convert to Base64 form
  190.      * @param sourceCharset
  191.      *            the Charset for {@code value}
  192.      * @return Base64 string
  193.      * @throws EncoderException
  194.      *             thrown if a failure condition is encountered during the encoding process.
  195.      * @since 1.7
  196.      */
  197.     public String encode(final String strSource, final Charset sourceCharset) throws EncoderException {
  198.         return encodeText(strSource, sourceCharset);
  199.     }

  200.     /**
  201.      * Encodes a string into its Base64 form using the specified Charset. Unsafe characters are escaped.
  202.      *
  203.      * @param strSource
  204.      *            string to convert to Base64 form
  205.      * @param sourceCharset
  206.      *            the Charset for {@code value}
  207.      * @return Base64 string
  208.      * @throws EncoderException
  209.      *             thrown if a failure condition is encountered during the encoding process.
  210.      */
  211.     public String encode(final String strSource, final String sourceCharset) throws EncoderException {
  212.         try {
  213.             return encodeText(strSource, sourceCharset);
  214.         } catch (final UnsupportedCharsetException e) {
  215.             throw new EncoderException(e.getMessage(), e);
  216.         }
  217.     }

  218.     @Override
  219.     protected String getEncoding() {
  220.         return "B";
  221.     }

  222.     /**
  223.      * Returns true if decoding behavior is strict. Decoding will raise a
  224.      * {@link DecoderException} if trailing bits are not part of a valid Base64 encoding.
  225.      *
  226.      * <p>The default is false for lenient encoding. Decoding will compose trailing bits
  227.      * into 8-bit bytes and discard the remainder.
  228.      *
  229.      * @return true if using strict decoding
  230.      * @since 1.15
  231.      */
  232.     public boolean isStrictDecoding() {
  233.         return decodingPolicy == CodecPolicy.STRICT;
  234.     }
  235. }