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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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