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 *      https://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
018package org.apache.commons.codec.binary;
019
020import java.io.InputStream;
021
022import org.apache.commons.codec.CodecPolicy;
023import org.apache.commons.codec.binary.BaseNCodecInputStream.AbstracBuilder; // NOPMD: Required by ECJ (Eclipse)
024
025/**
026 * Provides Base64 decoding in a streaming fashion (unlimited size). When encoding the default lineLength is 76 characters and the default lineEnding is CRLF,
027 * but these can be overridden by using the appropriate constructor.
028 * <p>
029 * The default behavior of the Base64InputStream is to DECODE, whereas the default behavior of the Base64OutputStream is to ENCODE, but this behavior can be
030 * overridden by using a different constructor.
031 * </p>
032 * <p>
033 * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
034 * Format of Internet Message Bodies</cite> by Freed and Borenstein.
035 * </p>
036 * <p>
037 * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode character encodings which are
038 * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).
039 * </p>
040 * <p>
041 * You can set the decoding behavior when the input bytes contain leftover trailing bits that cannot be created by a valid encoding. These can be bits that are
042 * unused from the final character or entire characters. The default mode is lenient decoding.
043 * </p>
044 * <ul>
045 * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible. The remainder are discarded.
046 * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits are not part of a valid encoding. Any unused bits from the final
047 * character must be zero. Impossible counts of entire final characters are not allowed.
048 * </ul>
049 * <p>
050 * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded to a byte array that matches the original, i.e. no changes occur on
051 * the final character. This requires that the input bytes use the same padding and alphabet as the encoder.
052 * </p>
053 *
054 * @see Base64
055 * @see <a href="https://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
056 * @since 1.4
057 */
058public class Base64InputStream extends BaseNCodecInputStream<Base64, Base64InputStream, Base64InputStream.Builder> {
059
060    /**
061     * Builds instances of Base64InputStream.
062     */
063    public static class Builder extends AbstracBuilder<Base64InputStream, Base64, Builder> {
064
065        /**
066         * Constructs a new instance.
067         */
068        public Builder() {
069            // empty
070        }
071
072        @Override
073        public Base64InputStream get() {
074            return new Base64InputStream(this);
075        }
076
077        @Override
078        protected Base64 newBaseNCodec() {
079            return new Base64();
080        }
081    }
082
083    /**
084     * Constructs a new Builder.
085     *
086     * @return a new Builder.
087     */
088    public static Builder builder() {
089        return new Builder();
090    }
091
092    private Base64InputStream(final Builder builder) {
093        super(builder);
094    }
095
096    /**
097     * Constructs a Base64InputStream such that all data read is Base64-decoded from the original provided InputStream.
098     *
099     * @param inputStream InputStream to wrap.
100     */
101    public Base64InputStream(final InputStream inputStream) {
102        super(builder().setInputStream(inputStream));
103    }
104
105    /**
106     * Constructs a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original provided InputStream.
107     *
108     * @param inputStream InputStream to wrap.
109     * @param encode    true if we should encode all data read from us, false if we should decode.
110     * @deprecated Use {@link #builder()} and {@link Builder}.
111     */
112    @Deprecated
113    public Base64InputStream(final InputStream inputStream, final boolean encode) {
114        super(builder().setInputStream(inputStream).setEncode(encode));
115    }
116
117    /**
118     * Constructs a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original provided InputStream.
119     *
120     * @param inputStream   InputStream to wrap.
121     * @param encode      true if we should encode all data read from us, false if we should decode.
122     * @param lineLength    If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to the nearest multiple of 4). If
123     *                      lineLength &lt;= 0, the encoded data is not divided into lines. If doEncode is false, lineLength is ignored.
124     * @param lineSeparator If doEncode is true, each line of encoded data will be terminated with this byte sequence (for example \r\n). If lineLength &lt;= 0,
125     *                      the lineSeparator is not used. If doEncode is false lineSeparator is ignored.
126     * @deprecated Use {@link #builder()} and {@link Builder}.
127     */
128    @Deprecated
129    public Base64InputStream(final InputStream inputStream, final boolean encode, final int lineLength, final byte[] lineSeparator) {
130        super(builder().setInputStream(inputStream).setEncode(encode)
131                .setBaseNCodec(Base64.builder().setLineLength(lineLength).setLineSeparator(lineSeparator).get()));
132    }
133
134    /**
135     * Constructs a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original provided InputStream.
136     *
137     * @param inputStream    InputStream to wrap.
138     * @param encode       true if we should encode all data read from us, false if we should decode.
139     * @param lineLength     If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to the nearest multiple of 4). If
140     *                       lineLength &lt;= 0, the encoded data is not divided into lines. If doEncode is false, lineLength is ignored.
141     * @param lineSeparator  If doEncode is true, each line of encoded data will be terminated with this byte sequence (for example \r\n). If lineLength &lt;=
142     *                       0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.
143     * @param decodingPolicy The decoding policy.
144     * @since 1.15
145     * @deprecated Use {@link #builder()} and {@link Builder}.
146     */
147    @Deprecated
148    public Base64InputStream(final InputStream inputStream, final boolean encode, final int lineLength, final byte[] lineSeparator,
149            final CodecPolicy decodingPolicy) {
150        super(builder().setInputStream(inputStream).setEncode(encode)
151                .setBaseNCodec(Base64.builder().setLineLength(lineLength).setLineSeparator(lineSeparator).setDecodingPolicy(decodingPolicy).get()));
152    }
153}