Home | History | Annotate | Download | only in net
      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      */
    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      */
    123     public QCodec(final String charset) {
    124         super();
    125         this.charset = charset;
    126     }
    127 
    128     protected String getEncoding() {
    129         return "Q";
    130     }
    131 
    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 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    306     public void setEncodeBlanks(boolean b) {
    307         this.encodeBlanks = b;
    308     }
    309 }
    310