001/**
002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC
003 * and GZip files. (http://jwat.org/)
004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.jwat.common;
019
020import java.io.ByteArrayOutputStream;
021
022/**
023 * Class used to decode and validate a Quoted-Printable string as specified
024 * in RFC 2047 (Section 4.2).
025 *
026 * @author nicl
027 */
028public class QuotedPrintable {
029
030    /**
031     * Static class.
032     */
033    protected QuotedPrintable() {
034    }
035
036    /**
037     * Returns a decoded Quoted-Printable string or null if it is not valid.
038     * @param encoded_text Quoted-Printable string
039     * @return decoded string or null
040     */
041    public static byte[] decode(String encoded_text) {
042        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
043        int idx = 0;
044        int c;
045        int cout;
046        while (idx < encoded_text.length()) {
047            c = encoded_text.charAt(idx++);
048            if (c == '_') {
049                // Underscore is shorthand for <space>.
050                bytesOut.write(' ');
051            } else if (c == '=') {
052                // "=" followed by two hexadecimal digits.
053                if ((idx + 2) <= encoded_text.length()) {
054                    c = encoded_text.charAt(idx++);
055                    c = Base16.decodeTab[c];
056                    if (c != -1) {
057                        cout = c << 4;
058                        c = encoded_text.charAt(idx++);
059                        c = Base16.decodeTab[c];
060                        if (c != -1) {
061                            cout |= c;
062                            bytesOut.write(cout);
063                        } else {
064                            return null;
065                        }
066                    } else {
067                        return null;
068                    }
069                } else {
070                    return null;
071                }
072            } else if (c > 32 && c < 127) {
073                bytesOut.write(c);
074            } else {
075                return null;
076            }
077        }
078        return bytesOut.toByteArray();
079    }
080
081}