1/*
2 * Copyright 2001-2004 The Apache Software Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.apache.commons.codec.net;
18
19import java.io.UnsupportedEncodingException;
20import java.util.BitSet;
21
22import org.apache.commons.codec.DecoderException;
23import org.apache.commons.codec.EncoderException;
24import org.apache.commons.codec.StringDecoder;
25import org.apache.commons.codec.StringEncoder;
26
27/**
28 * <p>
29 * Similar to the Quoted-Printable content-transfer-encoding defined in <a
30 * href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a> and designed to allow text containing mostly ASCII
31 * characters to be decipherable on an ASCII terminal without decoding.
32 * </p>
33 *
34 * <p>
35 * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
36 * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
37 * handling software.
38 * </p>
39 *
40 * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
41 *          Header Extensions for Non-ASCII Text</a>
42 *
43 * @author Apache Software Foundation
44 * @since 1.3
45 * @version $Id: QCodec.java,v 1.6 2004/05/24 00:24:32 ggregory Exp $
46 *
47 * @deprecated Please use {@link java.net.URL#openConnection} instead.
48 *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
49 *     for further details.
50 */
51@Deprecated
52public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
53    /**
54     * The default charset used for string decoding and encoding.
55     */
56    private String charset = StringEncodings.UTF8;
57
58    /**
59     * BitSet of printable characters as defined in RFC 1522.
60     */
61    private static final BitSet PRINTABLE_CHARS = new BitSet(256);
62    // Static initializer for printable chars collection
63    static {
64        // alpha characters
65        PRINTABLE_CHARS.set(' ');
66        PRINTABLE_CHARS.set('!');
67        PRINTABLE_CHARS.set('"');
68        PRINTABLE_CHARS.set('#');
69        PRINTABLE_CHARS.set('$');
70        PRINTABLE_CHARS.set('%');
71        PRINTABLE_CHARS.set('&');
72        PRINTABLE_CHARS.set('\'');
73        PRINTABLE_CHARS.set('(');
74        PRINTABLE_CHARS.set(')');
75        PRINTABLE_CHARS.set('*');
76        PRINTABLE_CHARS.set('+');
77        PRINTABLE_CHARS.set(',');
78        PRINTABLE_CHARS.set('-');
79        PRINTABLE_CHARS.set('.');
80        PRINTABLE_CHARS.set('/');
81        for (int i = '0'; i <= '9'; i++) {
82            PRINTABLE_CHARS.set(i);
83        }
84        PRINTABLE_CHARS.set(':');
85        PRINTABLE_CHARS.set(';');
86        PRINTABLE_CHARS.set('<');
87        PRINTABLE_CHARS.set('>');
88        PRINTABLE_CHARS.set('@');
89        for (int i = 'A'; i <= 'Z'; i++) {
90            PRINTABLE_CHARS.set(i);
91        }
92        PRINTABLE_CHARS.set('[');
93        PRINTABLE_CHARS.set('\\');
94        PRINTABLE_CHARS.set(']');
95        PRINTABLE_CHARS.set('^');
96        PRINTABLE_CHARS.set('`');
97        for (int i = 'a'; i <= 'z'; i++) {
98            PRINTABLE_CHARS.set(i);
99        }
100        PRINTABLE_CHARS.set('{');
101        PRINTABLE_CHARS.set('|');
102        PRINTABLE_CHARS.set('}');
103        PRINTABLE_CHARS.set('~');
104    }
105
106    private static byte BLANK = 32;
107
108    private static byte UNDERSCORE = 95;
109
110    private boolean encodeBlanks = false;
111
112    /**
113     * Default constructor.
114     */
115    public QCodec() {
116        super();
117    }
118
119    /**
120     * Constructor which allows for the selection of a default charset
121     *
122     * @param charset
123     *                  the default string charset to use.
124     *
125     * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
126     *          encoding names</a>
127     */
128    public QCodec(final String charset) {
129        super();
130        this.charset = charset;
131    }
132
133    protected String getEncoding() {
134        return "Q";
135    }
136
137    protected byte[] doEncoding(byte[] bytes) throws EncoderException {
138        if (bytes == null) {
139            return null;
140        }
141        byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
142        if (this.encodeBlanks) {
143            for (int i = 0; i < data.length; i++) {
144                if (data[i] == BLANK) {
145                    data[i] = UNDERSCORE;
146                }
147            }
148        }
149        return data;
150    }
151
152    protected byte[] doDecoding(byte[] bytes) throws DecoderException {
153        if (bytes == null) {
154            return null;
155        }
156        boolean hasUnderscores = false;
157        for (int i = 0; i < bytes.length; i++) {
158            if (bytes[i] == UNDERSCORE) {
159                hasUnderscores = true;
160                break;
161            }
162        }
163        if (hasUnderscores) {
164            byte[] tmp = new byte[bytes.length];
165            for (int i = 0; i < bytes.length; i++) {
166                byte b = bytes[i];
167                if (b != UNDERSCORE) {
168                    tmp[i] = b;
169                } else {
170                    tmp[i] = BLANK;
171                }
172            }
173            return QuotedPrintableCodec.decodeQuotedPrintable(tmp);
174        }
175        return QuotedPrintableCodec.decodeQuotedPrintable(bytes);
176    }
177
178    /**
179     * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
180     *
181     * @param pString
182     *                  string to convert to quoted-printable form
183     * @param charset
184     *                  the charset for pString
185     * @return quoted-printable string
186     *
187     * @throws EncoderException
188     *                  thrown if a failure condition is encountered during the encoding process.
189     */
190    public String encode(final String pString, final String charset) throws EncoderException {
191        if (pString == null) {
192            return null;
193        }
194        try {
195            return encodeText(pString, charset);
196        } catch (UnsupportedEncodingException e) {
197            throw new EncoderException(e.getMessage());
198        }
199    }
200
201    /**
202     * Encodes a string into its quoted-printable form using the default charset. Unsafe characters are escaped.
203     *
204     * @param pString
205     *                  string to convert to quoted-printable form
206     * @return quoted-printable string
207     *
208     * @throws EncoderException
209     *                  thrown if a failure condition is encountered during the encoding process.
210     */
211    public String encode(String pString) throws EncoderException {
212        if (pString == null) {
213            return null;
214        }
215        return encode(pString, getDefaultCharset());
216    }
217
218    /**
219     * Decodes a quoted-printable string into its original form. Escaped characters are converted back to their original
220     * representation.
221     *
222     * @param pString
223     *                  quoted-printable string to convert into its original form
224     *
225     * @return original string
226     *
227     * @throws DecoderException
228     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
229     */
230    public String decode(String pString) throws DecoderException {
231        if (pString == null) {
232            return null;
233        }
234        try {
235            return decodeText(pString);
236        } catch (UnsupportedEncodingException e) {
237            throw new DecoderException(e.getMessage());
238        }
239    }
240
241    /**
242     * Encodes an object into its quoted-printable form using the default charset. Unsafe characters are escaped.
243     *
244     * @param pObject
245     *                  object to convert to quoted-printable form
246     * @return quoted-printable object
247     *
248     * @throws EncoderException
249     *                  thrown if a failure condition is encountered during the encoding process.
250     */
251    public Object encode(Object pObject) throws EncoderException {
252        if (pObject == null) {
253            return null;
254        } else if (pObject instanceof String) {
255            return encode((String) pObject);
256        } else {
257            throw new EncoderException("Objects of type "
258                + pObject.getClass().getName()
259                + " cannot be encoded using Q codec");
260        }
261    }
262
263    /**
264     * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
265     * representation.
266     *
267     * @param pObject
268     *                  quoted-printable object to convert into its original form
269     *
270     * @return original object
271     *
272     * @throws DecoderException
273     *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
274     */
275    public Object decode(Object pObject) throws DecoderException {
276        if (pObject == null) {
277            return null;
278        } else if (pObject instanceof String) {
279            return decode((String) pObject);
280        } else {
281            throw new DecoderException("Objects of type "
282                + pObject.getClass().getName()
283                + " cannot be decoded using Q codec");
284        }
285    }
286
287    /**
288     * The default charset used for string decoding and encoding.
289     *
290     * @return the default string charset.
291     */
292    public String getDefaultCharset() {
293        return this.charset;
294    }
295
296    /**
297     * Tests if optional tranformation of SPACE characters is to be used
298     *
299     * @return <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
300     */
301    public boolean isEncodeBlanks() {
302        return this.encodeBlanks;
303    }
304
305    /**
306     * Defines whether optional tranformation of SPACE characters is to be used
307     *
308     * @param b
309     *                  <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
310     */
311    public void setEncodeBlanks(boolean b) {
312        this.encodeBlanks = b;
313    }
314}
315