• 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.UnsupportedEncodingException;
20 import java.util.BitSet;
21 
22 import org.apache.commons.codec.DecoderException;
23 import org.apache.commons.codec.EncoderException;
24 import org.apache.commons.codec.StringDecoder;
25 import 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 public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
48     /**
49      * The default charset used for string decoding and encoding.
50      */
51     private String charset = StringEncodings.UTF8;
52 
53     /**
54      * BitSet of printable characters as defined in RFC 1522.
55      */
56     private static final BitSet PRINTABLE_CHARS = new BitSet(256);
57     // Static initializer for printable chars collection
58     static {
59         // alpha characters
60         PRINTABLE_CHARS.set(' ');
61         PRINTABLE_CHARS.set('!');
62         PRINTABLE_CHARS.set('"');
63         PRINTABLE_CHARS.set('#');
64         PRINTABLE_CHARS.set('$');
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         for (int i = '0'; i <= '9'; i++) {
77             PRINTABLE_CHARS.set(i);
78         }
79         PRINTABLE_CHARS.set(':');
80         PRINTABLE_CHARS.set(';');
81         PRINTABLE_CHARS.set('<');
82         PRINTABLE_CHARS.set('>');
83         PRINTABLE_CHARS.set('@');
84         for (int i = 'A'; i <= 'Z'; i++) {
85             PRINTABLE_CHARS.set(i);
86         }
87         PRINTABLE_CHARS.set('[');
88         PRINTABLE_CHARS.set('\\');
89         PRINTABLE_CHARS.set(']');
90         PRINTABLE_CHARS.set('^');
91         PRINTABLE_CHARS.set('`');
92         for (int i = 'a'; i <= 'z'; i++) {
93             PRINTABLE_CHARS.set(i);
94         }
95         PRINTABLE_CHARS.set('{');
96         PRINTABLE_CHARS.set('|');
97         PRINTABLE_CHARS.set('}');
98         PRINTABLE_CHARS.set('~');
99     }
100 
101     private static byte BLANK = 32;
102 
103     private static byte UNDERSCORE = 95;
104 
105     private boolean encodeBlanks = false;
106 
107     /**
108      * Default constructor.
109      */
QCodec()110     public QCodec() {
111         super();
112     }
113 
114     /**
115      * Constructor which allows for the selection of a default charset
116      *
117      * @param charset
118      *                  the default string charset to use.
119      *
120      * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
121      *          encoding names</a>
122      */
QCodec(final String charset)123     public QCodec(final String charset) {
124         super();
125         this.charset = charset;
126     }
127 
getEncoding()128     protected String getEncoding() {
129         return "Q";
130     }
131 
doEncoding(byte[] bytes)132     protected byte[] doEncoding(byte[] bytes) throws EncoderException {
133         if (bytes == null) {
134             return null;
135         }
136         byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
137         if (this.encodeBlanks) {
138             for (int i = 0; i < data.length; i++) {
139                 if (data[i] == BLANK) {
140                     data[i] = UNDERSCORE;
141                 }
142             }
143         }
144         return data;
145     }
146 
doDecoding(byte[] bytes)147     protected byte[] doDecoding(byte[] bytes) throws DecoderException {
148         if (bytes == null) {
149             return null;
150         }
151         boolean hasUnderscores = false;
152         for (int i = 0; i < bytes.length; i++) {
153             if (bytes[i] == UNDERSCORE) {
154                 hasUnderscores = true;
155                 break;
156             }
157         }
158         if (hasUnderscores) {
159             byte[] tmp = new byte[bytes.length];
160             for (int i = 0; i < bytes.length; i++) {
161                 byte b = bytes[i];
162                 if (b != UNDERSCORE) {
163                     tmp[i] = b;
164                 } else {
165                     tmp[i] = BLANK;
166                 }
167             }
168             return QuotedPrintableCodec.decodeQuotedPrintable(tmp);
169         }
170         return QuotedPrintableCodec.decodeQuotedPrintable(bytes);
171     }
172 
173     /**
174      * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
175      *
176      * @param pString
177      *                  string to convert to quoted-printable form
178      * @param charset
179      *                  the charset for pString
180      * @return quoted-printable string
181      *
182      * @throws EncoderException
183      *                  thrown if a failure condition is encountered during the encoding process.
184      */
encode(final String pString, final String charset)185     public String encode(final String pString, final String charset) throws EncoderException {
186         if (pString == null) {
187             return null;
188         }
189         try {
190             return encodeText(pString, charset);
191         } catch (UnsupportedEncodingException e) {
192             throw new EncoderException(e.getMessage());
193         }
194     }
195 
196     /**
197      * Encodes a string into its quoted-printable form using the default charset. Unsafe characters are escaped.
198      *
199      * @param pString
200      *                  string to convert to quoted-printable form
201      * @return quoted-printable string
202      *
203      * @throws EncoderException
204      *                  thrown if a failure condition is encountered during the encoding process.
205      */
encode(String pString)206     public String encode(String pString) throws EncoderException {
207         if (pString == null) {
208             return null;
209         }
210         return encode(pString, getDefaultCharset());
211     }
212 
213     /**
214      * Decodes a quoted-printable string into its original form. Escaped characters are converted back to their original
215      * representation.
216      *
217      * @param pString
218      *                  quoted-printable string to convert into its original form
219      *
220      * @return original string
221      *
222      * @throws DecoderException
223      *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
224      */
decode(String pString)225     public String decode(String pString) throws DecoderException {
226         if (pString == null) {
227             return null;
228         }
229         try {
230             return decodeText(pString);
231         } catch (UnsupportedEncodingException e) {
232             throw new DecoderException(e.getMessage());
233         }
234     }
235 
236     /**
237      * Encodes an object into its quoted-printable form using the default charset. Unsafe characters are escaped.
238      *
239      * @param pObject
240      *                  object to convert to quoted-printable form
241      * @return quoted-printable object
242      *
243      * @throws EncoderException
244      *                  thrown if a failure condition is encountered during the encoding process.
245      */
encode(Object pObject)246     public Object encode(Object pObject) throws EncoderException {
247         if (pObject == null) {
248             return null;
249         } else if (pObject instanceof String) {
250             return encode((String) pObject);
251         } else {
252             throw new EncoderException("Objects of type "
253                 + pObject.getClass().getName()
254                 + " cannot be encoded using Q codec");
255         }
256     }
257 
258     /**
259      * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
260      * representation.
261      *
262      * @param pObject
263      *                  quoted-printable object to convert into its original form
264      *
265      * @return original object
266      *
267      * @throws DecoderException
268      *                  A decoder exception is thrown if a failure condition is encountered during the decode process.
269      */
decode(Object pObject)270     public Object decode(Object pObject) throws DecoderException {
271         if (pObject == null) {
272             return null;
273         } else if (pObject instanceof String) {
274             return decode((String) pObject);
275         } else {
276             throw new DecoderException("Objects of type "
277                 + pObject.getClass().getName()
278                 + " cannot be decoded using Q codec");
279         }
280     }
281 
282     /**
283      * The default charset used for string decoding and encoding.
284      *
285      * @return the default string charset.
286      */
getDefaultCharset()287     public String getDefaultCharset() {
288         return this.charset;
289     }
290 
291     /**
292      * Tests if optional tranformation of SPACE characters is to be used
293      *
294      * @return <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
295      */
isEncodeBlanks()296     public boolean isEncodeBlanks() {
297         return this.encodeBlanks;
298     }
299 
300     /**
301      * Defines whether optional tranformation of SPACE characters is to be used
302      *
303      * @param b
304      *                  <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
305      */
setEncodeBlanks(boolean b)306     public void setEncodeBlanks(boolean b) {
307         this.encodeBlanks = b;
308     }
309 }
310