• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package org.apache.commons.codec.net;
18 
19 import java.io.ByteArrayOutputStream;
20 import java.io.UnsupportedEncodingException;
21 import java.util.BitSet;
22 import org.apache.commons.codec.BinaryDecoder;
23 import org.apache.commons.codec.BinaryEncoder;
24 import org.apache.commons.codec.DecoderException;
25 import org.apache.commons.codec.EncoderException;
26 import org.apache.commons.codec.StringDecoder;
27 import org.apache.commons.codec.StringEncoder;
28 
29 /**
30  * <p>
31  * Codec for the Quoted-Printable section of <a href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521 </a>.
32  * </p>
33  * <p>
34  * The Quoted-Printable encoding is intended to represent data that largely consists of octets that correspond to
35  * printable characters in the ASCII character set. It encodes the data in such a way that the resulting octets are
36  * unlikely to be modified by mail transport. If the data being encoded are mostly ASCII text, the encoded form of the
37  * data remains largely recognizable by humans. A body which is entirely ASCII may also be encoded in Quoted-Printable
38  * to ensure the integrity of the data should the message pass through a character- translating, and/or line-wrapping
39  * gateway.
40  * </p>
41  *
42  * <p>
43  * Note:
44  * </p>
45  * <p>
46  * Rules #3, #4, and #5 of the quoted-printable spec are not implemented yet because the complete quoted-printable spec
47  * does not lend itself well into the byte[] oriented codec framework. Complete the codec once the steamable codec
48  * framework is ready. The motivation behind providing the codec in a partial form is that it can already come in handy
49  * for those applications that do not require quoted-printable line formatting (rules #3, #4, #5), for instance Q codec.
50  * </p>
51  *
52  * @see <a href="http://www.ietf.org/rfc/rfc1521.txt"> RFC 1521 MIME (Multipurpose Internet Mail Extensions) Part One:
53  *          Mechanisms for Specifying and Describing the Format of Internet Message Bodies </a>
54  *
55  * @author Apache Software Foundation
56  * @since 1.3
57  * @version $Id: QuotedPrintableCodec.java,v 1.7 2004/04/09 22:21:07 ggregory Exp $
58  *
59  * @deprecated Please use {@link java.net.URL#openConnection} instead.
60  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
61  *     for further details.
62  */
63 @Deprecated
64 public class QuotedPrintableCodec implements BinaryEncoder, BinaryDecoder, StringEncoder, StringDecoder {
65     /**
66      * The default charset used for string decoding and encoding.
67      */
68     private String charset = StringEncodings.UTF8;
69 
70     /**
71      * BitSet of printable characters as defined in RFC 1521.
72      */
73     private static final BitSet PRINTABLE_CHARS = new BitSet(256);
74 
75     private static byte ESCAPE_CHAR = '=';
76 
77     private static byte TAB = 9;
78 
79     private static byte SPACE = 32;
80     // Static initializer for printable chars collection
81     static {
82         // alpha characters
83         for (int i = 33; i <= 60; i++) {
84             PRINTABLE_CHARS.set(i);
85         }
86         for (int i = 62; i <= 126; i++) {
87             PRINTABLE_CHARS.set(i);
88         }
89         PRINTABLE_CHARS.set(TAB);
90         PRINTABLE_CHARS.set(SPACE);
91     }
92 
93     /**
94      * Default constructor.
95      */
QuotedPrintableCodec()96     public QuotedPrintableCodec() {
97         super();
98     }
99 
100     /**
101      * Constructor which allows for the selection of a default charset
102      *
103      * @param charset
104      *                  the default string charset to use.
105      */
QuotedPrintableCodec(String charset)106     public QuotedPrintableCodec(String charset) {
107         super();
108         this.charset = charset;
109     }
110 
111     /**
112      * Encodes byte into its quoted-printable representation.
113      *
114      * @param b
115      *                  byte to encode
116      * @param buffer
117      *                  the buffer to write to
118      */
encodeQuotedPrintable(int b, ByteArrayOutputStream buffer)119     private static final void encodeQuotedPrintable(int b, ByteArrayOutputStream buffer) {
120         buffer.write(ESCAPE_CHAR);
121         char hex1 = Character.toUpperCase(Character.forDigit((b >> 4) & 0xF, 16));
122         char hex2 = Character.toUpperCase(Character.forDigit(b & 0xF, 16));
123         buffer.write(hex1);
124         buffer.write(hex2);
125     }
126 
127     /**
128      * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped.
129      *
130      * <p>
131      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
132      * RFC 1521 and is suitable for encoding binary data and unformatted text.
133      * </p>
134      *
135      * @param printable
136      *                  bitset of characters deemed quoted-printable
137      * @param bytes
138      *                  array of bytes to be encoded
139      * @return array of bytes containing quoted-printable data
140      */
encodeQuotedPrintable(BitSet printable, byte[] bytes)141     public static final byte[] encodeQuotedPrintable(BitSet printable, byte[] bytes) {
142         if (bytes == null) {
143             return null;
144         }
145         if (printable == null) {
146             printable = PRINTABLE_CHARS;
147         }
148         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
149         for (int i = 0; i < bytes.length; i++) {
150             int b = bytes[i];
151             if (b < 0) {
152                 b = 256 + b;
153             }
154             if (printable.get(b)) {
155                 buffer.write(b);
156             } else {
157                 encodeQuotedPrintable(b, buffer);
158             }
159         }
160         return buffer.toByteArray();
161     }
162 
163     /**
164      * Decodes an array quoted-printable characters into an array of original bytes. Escaped characters are converted
165      * back to their original representation.
166      *
167      * <p>
168      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
169      * RFC 1521.
170      * </p>
171      *
172      * @param bytes
173      *                  array of quoted-printable characters
174      * @return array of original bytes
175      * @throws DecoderException
176      *                  Thrown if quoted-printable decoding is unsuccessful
177      */
decodeQuotedPrintable(byte[] bytes)178     public static final byte[] decodeQuotedPrintable(byte[] bytes) throws DecoderException {
179         if (bytes == null) {
180             return null;
181         }
182         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
183         for (int i = 0; i < bytes.length; i++) {
184             int b = bytes[i];
185             if (b == ESCAPE_CHAR) {
186                 try {
187                     int u = Character.digit((char) bytes[++i], 16);
188                     int l = Character.digit((char) bytes[++i], 16);
189                     if (u == -1 || l == -1) {
190                         throw new DecoderException("Invalid quoted-printable encoding");
191                     }
192                     buffer.write((char) ((u << 4) + l));
193                 } catch (ArrayIndexOutOfBoundsException e) {
194                     throw new DecoderException("Invalid quoted-printable encoding");
195                 }
196             } else {
197                 buffer.write(b);
198             }
199         }
200         return buffer.toByteArray();
201     }
202 
203     /**
204      * Encodes an array of bytes into an array of quoted-printable 7-bit characters. Unsafe characters are escaped.
205      *
206      * <p>
207      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
208      * RFC 1521 and is suitable for encoding binary data and unformatted text.
209      * </p>
210      *
211      * @param bytes
212      *                  array of bytes to be encoded
213      * @return array of bytes containing quoted-printable data
214      */
encode(byte[] bytes)215     public byte[] encode(byte[] bytes) {
216         return encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
217     }
218 
219     /**
220      * Decodes an array of quoted-printable characters into an array of original bytes. Escaped characters are converted
221      * back to their original representation.
222      *
223      * <p>
224      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
225      * RFC 1521.
226      * </p>
227      *
228      * @param bytes
229      *                  array of quoted-printable characters
230      * @return array of original bytes
231      * @throws DecoderException
232      *                  Thrown if quoted-printable decoding is unsuccessful
233      */
decode(byte[] bytes)234     public byte[] decode(byte[] bytes) throws DecoderException {
235         return decodeQuotedPrintable(bytes);
236     }
237 
238     /**
239      * Encodes a string into its quoted-printable form using the default string charset. Unsafe characters are escaped.
240      *
241      * <p>
242      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
243      * RFC 1521 and is suitable for encoding binary data.
244      * </p>
245      *
246      * @param pString
247      *                  string to convert to quoted-printable form
248      * @return quoted-printable string
249      *
250      * @throws EncoderException
251      *                  Thrown if quoted-printable encoding is unsuccessful
252      *
253      * @see #getDefaultCharset()
254      */
encode(String pString)255     public String encode(String pString) throws EncoderException {
256         if (pString == null) {
257             return null;
258         }
259         try {
260             return encode(pString, getDefaultCharset());
261         } catch (UnsupportedEncodingException e) {
262             throw new EncoderException(e.getMessage());
263         }
264     }
265 
266     /**
267      * Decodes a quoted-printable string into its original form using the specified string charset. Escaped characters
268      * are converted back to their original representation.
269      *
270      * @param pString
271      *                  quoted-printable string to convert into its original form
272      * @param charset
273      *                  the original string charset
274      * @return original string
275      * @throws DecoderException
276      *                  Thrown if quoted-printable decoding is unsuccessful
277      * @throws UnsupportedEncodingException
278      *                  Thrown if charset is not supported
279      */
decode(String pString, String charset)280     public String decode(String pString, String charset) throws DecoderException, UnsupportedEncodingException {
281         if (pString == null) {
282             return null;
283         }
284         return new String(decode(pString.getBytes(StringEncodings.US_ASCII)), charset);
285     }
286 
287     /**
288      * Decodes a quoted-printable string into its original form using the default string charset. Escaped characters are
289      * converted back to their original representation.
290      *
291      * @param pString
292      *                  quoted-printable string to convert into its original form
293      * @return original string
294      * @throws DecoderException
295      *                  Thrown if quoted-printable decoding is unsuccessful
296      * @throws UnsupportedEncodingException
297      *                  Thrown if charset is not supported
298      * @see #getDefaultCharset()
299      */
decode(String pString)300     public String decode(String pString) throws DecoderException {
301         if (pString == null) {
302             return null;
303         }
304         try {
305             return decode(pString, getDefaultCharset());
306         } catch (UnsupportedEncodingException e) {
307             throw new DecoderException(e.getMessage());
308         }
309     }
310 
311     /**
312      * Encodes an object into its quoted-printable safe form. Unsafe characters are escaped.
313      *
314      * @param pObject
315      *                  string to convert to a quoted-printable form
316      * @return quoted-printable object
317      * @throws EncoderException
318      *                  Thrown if quoted-printable encoding is not applicable to objects of this type or if encoding is
319      *                  unsuccessful
320      */
encode(Object pObject)321     public Object encode(Object pObject) throws EncoderException {
322         if (pObject == null) {
323             return null;
324         } else if (pObject instanceof byte[]) {
325             return encode((byte[]) pObject);
326         } else if (pObject instanceof String) {
327             return encode((String) pObject);
328         } else {
329             throw new EncoderException("Objects of type "
330                 + pObject.getClass().getName()
331                 + " cannot be quoted-printable encoded");
332         }
333     }
334 
335     /**
336      * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
337      * representation.
338      *
339      * @param pObject
340      *                  quoted-printable object to convert into its original form
341      * @return original object
342      * @throws DecoderException
343      *                  Thrown if quoted-printable decoding is not applicable to objects of this type if decoding is
344      *                  unsuccessful
345      */
decode(Object pObject)346     public Object decode(Object pObject) throws DecoderException {
347         if (pObject == null) {
348             return null;
349         } else if (pObject instanceof byte[]) {
350             return decode((byte[]) pObject);
351         } else if (pObject instanceof String) {
352             return decode((String) pObject);
353         } else {
354             throw new DecoderException("Objects of type "
355                 + pObject.getClass().getName()
356                 + " cannot be quoted-printable decoded");
357         }
358     }
359 
360     /**
361      * Returns the default charset used for string decoding and encoding.
362      *
363      * @return the default string charset.
364      */
getDefaultCharset()365     public String getDefaultCharset() {
366         return this.charset;
367     }
368 
369     /**
370      * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
371      *
372      * <p>
373      * This function implements a subset of quoted-printable encoding specification (rule #1 and rule #2) as defined in
374      * RFC 1521 and is suitable for encoding binary data and unformatted text.
375      * </p>
376      *
377      * @param pString
378      *                  string to convert to quoted-printable form
379      * @param charset
380      *                  the charset for pString
381      * @return quoted-printable string
382      *
383      * @throws UnsupportedEncodingException
384      *                  Thrown if the charset is not supported
385      */
encode(String pString, String charset)386     public String encode(String pString, String charset) throws UnsupportedEncodingException {
387         if (pString == null) {
388             return null;
389         }
390         return new String(encode(pString.getBytes(charset)), StringEncodings.US_ASCII);
391     }
392 }
393