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 *      http://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.net;
019
020import java.io.UnsupportedEncodingException;
021import java.nio.charset.Charset;
022
023import org.apache.commons.codec.Charsets;
024import org.apache.commons.codec.DecoderException;
025import org.apache.commons.codec.EncoderException;
026import org.apache.commons.codec.StringDecoder;
027import org.apache.commons.codec.StringEncoder;
028import org.apache.commons.codec.binary.Base64;
029
030/**
031 * Identical to the Base64 encoding defined by <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a>
032 * and allows a character set to be specified.
033 * <p>
034 * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
035 * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
036 * handling software.
037 * <p>
038 * This class is immutable and thread-safe.
039 *
040 * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
041 *          Header Extensions for Non-ASCII Text</a>
042 *
043 * @since 1.3
044 */
045public class BCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
046    /**
047     * The default Charset used for string decoding and encoding.
048     */
049    private final Charset charset;
050
051    /**
052     * Default constructor.
053     */
054    public BCodec() {
055        this(Charsets.UTF_8);
056    }
057
058    /**
059     * Constructor which allows for the selection of a default Charset
060     *
061     * @param charset
062     *            the default string Charset to use.
063     *
064     * @see <a href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
065     * @since 1.7
066     */
067    public BCodec(final Charset charset) {
068        this.charset = charset;
069    }
070
071    /**
072     * Constructor which allows for the selection of a default Charset
073     *
074     * @param charsetName
075     *            the default Charset to use.
076     * @throws java.nio.charset.UnsupportedCharsetException
077     *             If the named Charset is unavailable
078     * @since 1.7 throws UnsupportedCharsetException if the named Charset is unavailable
079     * @see <a href="http://download.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html">Standard charsets</a>
080     */
081    public BCodec(final String charsetName) {
082        this(Charset.forName(charsetName));
083    }
084
085    @Override
086    protected String getEncoding() {
087        return "B";
088    }
089
090    @Override
091    protected byte[] doEncoding(final byte[] bytes) {
092        if (bytes == null) {
093            return null;
094        }
095        return Base64.encodeBase64(bytes);
096    }
097
098    @Override
099    protected byte[] doDecoding(final byte[] bytes) {
100        if (bytes == null) {
101            return null;
102        }
103        return Base64.decodeBase64(bytes);
104    }
105
106    /**
107     * Encodes a string into its Base64 form using the specified Charset. Unsafe characters are escaped.
108     *
109     * @param strSource
110     *            string to convert to Base64 form
111     * @param sourceCharset
112     *            the Charset for <code>value</code>
113     * @return Base64 string
114     * @throws EncoderException
115     *             thrown if a failure condition is encountered during the encoding process.
116     * @since 1.7
117     */
118    public String encode(final String strSource, final Charset sourceCharset) throws EncoderException {
119        if (strSource == null) {
120            return null;
121        }
122        return encodeText(strSource, sourceCharset);
123    }
124
125    /**
126     * Encodes a string into its Base64 form using the specified Charset. Unsafe characters are escaped.
127     *
128     * @param strSource
129     *            string to convert to Base64 form
130     * @param sourceCharset
131     *            the Charset for <code>value</code>
132     * @return Base64 string
133     * @throws EncoderException
134     *             thrown if a failure condition is encountered during the encoding process.
135     */
136    public String encode(final String strSource, final String sourceCharset) throws EncoderException {
137        if (strSource == null) {
138            return null;
139        }
140        try {
141            return this.encodeText(strSource, sourceCharset);
142        } catch (final UnsupportedEncodingException e) {
143            throw new EncoderException(e.getMessage(), e);
144        }
145    }
146
147    /**
148     * Encodes a string into its Base64 form using the default Charset. Unsafe characters are escaped.
149     *
150     * @param strSource
151     *            string to convert to Base64 form
152     * @return Base64 string
153     * @throws EncoderException
154     *             thrown if a failure condition is encountered during the encoding process.
155     */
156    @Override
157    public String encode(final String strSource) throws EncoderException {
158        if (strSource == null) {
159            return null;
160        }
161        return encode(strSource, this.getCharset());
162    }
163
164    /**
165     * Decodes a Base64 string into its original form. Escaped characters are converted back to their original
166     * representation.
167     *
168     * @param value
169     *            Base64 string to convert into its original form
170     * @return original string
171     * @throws DecoderException
172     *             A decoder exception is thrown if a failure condition is encountered during the decode process.
173     */
174    @Override
175    public String decode(final String value) throws DecoderException {
176        if (value == null) {
177            return null;
178        }
179        try {
180            return this.decodeText(value);
181        } catch (final UnsupportedEncodingException | IllegalArgumentException e) {
182            throw new DecoderException(e.getMessage(), e);
183        }
184    }
185
186    /**
187     * Encodes an object into its Base64 form using the default Charset. Unsafe characters are escaped.
188     *
189     * @param value
190     *            object to convert to Base64 form
191     * @return Base64 object
192     * @throws EncoderException
193     *             thrown if a failure condition is encountered during the encoding process.
194     */
195    @Override
196    public Object encode(final Object value) throws EncoderException {
197        if (value == null) {
198            return null;
199        } else if (value instanceof String) {
200            return encode((String) value);
201        } else {
202            throw new EncoderException("Objects of type " +
203                  value.getClass().getName() +
204                  " cannot be encoded using BCodec");
205        }
206    }
207
208    /**
209     * Decodes a Base64 object into its original form. Escaped characters are converted back to their original
210     * representation.
211     *
212     * @param value
213     *            Base64 object to convert into its original form
214     * @return original object
215     * @throws DecoderException
216     *             Thrown if the argument is not a <code>String</code>. Thrown if a failure condition is encountered
217     *             during the decode process.
218     */
219    @Override
220    public Object decode(final Object value) throws DecoderException {
221        if (value == null) {
222            return null;
223        } else if (value instanceof String) {
224            return decode((String) value);
225        } else {
226            throw new DecoderException("Objects of type " +
227                  value.getClass().getName() +
228                  " cannot be decoded using BCodec");
229        }
230    }
231
232    /**
233     * Gets the default Charset name used for string decoding and encoding.
234     *
235     * @return the default Charset name
236     * @since 1.7
237     */
238    public Charset getCharset() {
239        return this.charset;
240    }
241
242    /**
243     * Gets the default Charset name used for string decoding and encoding.
244     *
245     * @return the default Charset name
246     */
247    public String getDefaultCharset() {
248        return this.charset.name();
249    }
250}