immf.Util.java Source code

Java tutorial

Introduction

Here is the source code for immf.Util.java

Source

/*
 * imoten - i mode.net mail tensou(forward)
 *
 * Copyright (C) 2010 shoozhoo (http://code.google.com/p/imoten/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */

package immf;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.ContentDisposition;
import javax.mail.internet.ContentType;
import javax.mail.internet.HeaderTokenizer;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeUtility;
import javax.mail.internet.ParseException;

import net.htmlparser.jericho.Source;

import org.apache.commons.lang.StringUtils;

public class Util {
    public static void safeclose(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (Exception e) {
            }
        }
    }

    /**
     * ????????
     * @param s
     * @return
     */
    public static String replaceUnicodeMapping(String s) {
        if (StringUtils.isEmpty(System.getProperty("sun.nio.cs.map"))) {
            s = StringUtils.replace(s, "\uff5e", "\u301c");
            s = StringUtils.replace(s, "\u2225", "\u2016");
            s = StringUtils.replace(s, "\uff0d", "\u2212");
            s = StringUtils.replace(s, "\uffe0", "\u00a2");
            s = StringUtils.replace(s, "\uffe1", "\u00a3");
            s = StringUtils.replace(s, "\uffe2", "\u00ac");
            s = StringUtils.replace(s, "\u2015", "\u2014");
        }
        // sun.nio.cs.map ??????iso-2022-jp???
        // x-windows-iso2022jp?????????????

        return s;
    }

    public static String reverseReplaceUnicodeMapping(String s) {
        s = StringUtils.replace(s, "\u301c", "\uff5e");
        s = StringUtils.replace(s, "\u2016", "\u2225");
        s = StringUtils.replace(s, "\u2212", "\uff0d");
        s = StringUtils.replace(s, "\u00a2", "\uffe0");
        s = StringUtils.replace(s, "\u00a3", "\uffe1");
        s = StringUtils.replace(s, "\u00ac", "\uffe2");
        s = StringUtils.replace(s, "\u2014", "\u2015");

        // UTF-8? ?(1)?Shift_JIS?????????(2)??
        s = StringUtils.replace(s, "\u3094", "?");

        // ??????????
        s = StringUtils.replace(s, "\u00ab", "\u226a"); // ??
        s = StringUtils.replace(s, "\u00af", "\uffe3"); // ??
        s = StringUtils.replace(s, "\u00b5", "\u03bc"); // ??
        s = StringUtils.replace(s, "\u00b7", "\u30fb"); // ??
        s = StringUtils.replace(s, "\u00b8", "\uff0c"); // ??
        s = StringUtils.replace(s, "\u00bb", "\u226b"); // ??
        s = StringUtils.replace(s, "\u203e", "\u007e"); // ?~?
        return s;
    }

    public static String easyEscapeHtml(String s) {
        StringBuilder buf = new StringBuilder();
        for (char c : s.toCharArray()) {
            if (c == '>') {
                buf.append("&gt;");
            } else if (c == '<') {
                buf.append("&lt;");
            } else if (c == '&') {
                buf.append("&amp;");
            } else if (c == '"') {
                buf.append("&quot;");
            } else {
                buf.append(c);
            }
        }
        return buf.toString();
    }

    /*
     * JavaMail ?????????????
     * http://www.sk-jp.com/book/javamail/contents/
     */
    public static void setFileName(Part part, String filename, String charset, String lang)
            throws MessagingException {

        ContentDisposition disposition;
        String[] strings = part.getHeader("Content-Disposition");
        if (strings == null || strings.length < 1) {
            disposition = new ContentDisposition(Part.ATTACHMENT);
        } else {
            disposition = new ContentDisposition(strings[0]);
            disposition.getParameterList().remove("filename");
        }

        part.setHeader("Content-Disposition",
                disposition.toString() + encodeParameter("filename", filename, charset, lang));

        ContentType cType;
        strings = part.getHeader("Content-Type");
        if (strings == null || strings.length < 1) {
            cType = new ContentType(part.getDataHandler().getContentType());
        } else {
            cType = new ContentType(strings[0]);
        }

        try {
            // I want to public the MimeUtility#doEncode()!!!
            String mimeString = MimeUtility.encodeWord(filename, charset, "B");
            // cut <CRLF>...
            StringBuffer sb = new StringBuffer();
            int i;
            while ((i = mimeString.indexOf('\r')) != -1) {
                sb.append(mimeString.substring(0, i));
                mimeString = mimeString.substring(i + 2);
            }
            sb.append(mimeString);

            cType.setParameter("name", new String(sb));
        } catch (UnsupportedEncodingException e) {
            throw new MessagingException("Encoding error", e);
        }
        part.setHeader("Content-Type", cType.toString());
    }

    public static String encodeParameter(String name, String value, String encoding, String lang) {
        StringBuffer result = new StringBuffer();
        StringBuffer encodedPart = new StringBuffer();

        boolean needWriteCES = !isAllAscii(value);
        boolean CESWasWritten = false;
        boolean encoded;
        boolean needFolding = false;
        int sequenceNo = 0;
        int column;

        while (value.length() > 0) {
            // index of boundary of ascii/non ascii
            int lastIndex;
            boolean isAscii = value.charAt(0) < 0x80;
            for (lastIndex = 1; lastIndex < value.length(); lastIndex++) {
                if (value.charAt(lastIndex) < 0x80) {
                    if (!isAscii)
                        break;
                } else {
                    if (isAscii)
                        break;
                }
            }
            if (lastIndex != value.length())
                needFolding = true;

            RETRY: while (true) {
                encodedPart.delete(0, encodedPart.length());
                String target = value.substring(0, lastIndex);

                byte[] bytes;
                try {
                    if (isAscii) {
                        bytes = target.getBytes("us-ascii");
                    } else {
                        bytes = target.getBytes(encoding);
                    }
                } catch (UnsupportedEncodingException e) {
                    bytes = target.getBytes(); // use default encoding
                    encoding = MimeUtility.mimeCharset(MimeUtility.getDefaultJavaCharset());
                }

                encoded = false;
                // It is not strict.
                column = name.length() + 7; // size of " " and "*nn*=" and ";"

                for (int i = 0; i < bytes.length; i++) {
                    if (bytes[i] > ' ' && bytes[i] < 'z' && HeaderTokenizer.MIME.indexOf((char) bytes[i]) < 0) {
                        encodedPart.append((char) bytes[i]);
                        column++;
                    } else {
                        encoded = true;
                        encodedPart.append('%');
                        String hex = Integer.toString(bytes[i] & 0xff, 16);
                        if (hex.length() == 1) {
                            encodedPart.append('0');
                        }
                        encodedPart.append(hex);
                        column += 3;
                    }
                    if (column > 76) {
                        needFolding = true;
                        lastIndex /= 2;
                        continue RETRY;
                    }
                }

                result.append(";\r\n ").append(name);
                if (needFolding) {
                    result.append('*').append(sequenceNo);
                    sequenceNo++;
                }
                if (!CESWasWritten && needWriteCES) {
                    result.append("*=");
                    CESWasWritten = true;
                    result.append(encoding).append('\'');
                    if (lang != null)
                        result.append(lang);
                    result.append('\'');
                } else if (encoded) {
                    result.append("*=");
                } else {
                    result.append('=');
                }
                result.append(new String(encodedPart));
                value = value.substring(lastIndex);
                break;
            }
        }
        return new String(result);
    }

    public static String getFileName(Part part) throws MessagingException {
        String[] disposition = part.getHeader("Content-Disposition");
        if (disposition == null || disposition.length < 1) {
            return part.getFileName();
        }
        // ??????????????
        return decodeParameterSpciallyJapanese(getParameter(disposition[0], "filename"));
    }

    static class Encoding {
        String encoding = "us-ascii";
        String lang = "";
    }

    public static String getParameter(String header, String name) {

        HeaderTokenizer tokenizer = new HeaderTokenizer(header, HeaderTokenizer.MIME, true);
        HeaderTokenizer.Token token;
        StringBuffer sb = new StringBuffer();
        // It is specified in first encoded-part.
        Encoding encoding = new Encoding();

        String n;
        String v;

        try {
            while (true) {
                token = tokenizer.next();
                if (token.getType() == HeaderTokenizer.Token.EOF)
                    break;
                if (token.getType() != ';')
                    continue;

                token = tokenizer.next();
                checkType(token);
                n = token.getValue();

                token = tokenizer.next();
                if (token.getType() != '=') {
                    throw new ParseException("Illegal token : " + token.getValue());
                }

                token = tokenizer.next();
                checkType(token);
                v = token.getValue();

                if (n.equalsIgnoreCase(name)) {
                    // It is not divided and is not encoded.
                    return v;
                }

                int index = name.length();

                if (!n.startsWith(name) || n.charAt(index) != '*') {
                    // another parameter
                    continue;
                }
                // be folded, or be encoded
                int lastIndex = n.length() - 1;
                if (n.charAt(lastIndex) == '*') {
                    sb.append(decodeRFC2231(v, encoding));
                } else {
                    sb.append(v);
                }
                if (index == lastIndex) {
                    // not folding
                    break;
                }
            }
            return new String(sb);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        throw new InternalError();
    }

    private static void checkType(HeaderTokenizer.Token token) throws ParseException {
        int t = token.getType();
        if (t != HeaderTokenizer.Token.ATOM && t != HeaderTokenizer.Token.QUOTEDSTRING) {
            throw new ParseException("Illegal token : " + token.getValue());
        }
    }

    // "lang" tag is ignored...
    private static String decodeRFC2231(String s, Encoding encoding)
            throws ParseException, UnsupportedEncodingException {
        StringBuffer sb = new StringBuffer();
        int i = 0;

        int work = s.indexOf('\'');
        if (work > 0) {
            encoding.encoding = s.substring(0, work);
            work++;
            i = s.indexOf('\'', work);
            encoding.lang = s.substring(work, i);
            i++;
        }

        try {
            for (; i < s.length(); i++) {
                if (s.charAt(i) == '%') {
                    sb.append((char) Integer.parseInt(s.substring(i + 1, i + 3), 16));
                    i += 2;
                    continue;
                }
                sb.append(s.charAt(i));
            }
            return new String(new String(sb).getBytes("ISO-8859-1"), encoding.encoding);
        } catch (IndexOutOfBoundsException e) {
            throw new ParseException(s + " :: this string were not decoded.");
        }
    }

    public static String decodeParameterSpciallyJapanese(String s) throws ParseException {
        try {
            boolean unicode = false;
            for (int i = 0; i < s.length(); i++) {
                if (s.charAt(i) > 0xff) { // Unicode
                    unicode = true;
                    break;
                }
            }
            if (!unicode) {
                // decode by character encoding.
                s = new String(s.getBytes("ISO-8859-1"), "JISAutoDetect");
            }
            // decode by RFC2047.
            // if variable s isn't encoded-word, it's ignored.
            return MimeUtility.decodeText(s);
        } catch (UnsupportedEncodingException e) {
        }
        throw new ParseException("Unsupported Encoding");
    }

    public static String html2text(String html) {
        Source src = new Source(html);
        return src.getRenderer().toString();
    }

    /*
     * From,CC???Body?????
     */
    public static String getHeaderInfo(ImodeMail imm, boolean isHtml, boolean subjectEmojiReplace, Config conf) {
        StringBuilder buf = new StringBuilder();
        StringBuilder header = new StringBuilder();

        header.append(" From:    ").append(imm.getFromAddr().toUnicodeString()).append("\r\n");

        String label = " To:";
        if (imm.getRecvType() == ImodeMail.RECV_TYPE_TO && imm.getFolderId() != ImodeNetClient.FolderIdSent) {
            header.append(label + "      ").append(imm.getMyMailAddr()).append("\r\n");
            label = "    ";
        }
        for (InternetAddress addr : imm.getToAddrList()) {
            header.append(label + "      ").append(addr.toUnicodeString()).append("\r\n");
            label = "    ";
        }
        String prefix = " Cc:";
        if (imm.getRecvType() == ImodeMail.RECV_TYPE_CC && imm.getFolderId() != ImodeNetClient.FolderIdSent) {
            header.append(prefix + "      ").append(imm.getMyMailAddr()).append("\r\n");
            label = "    ";
        }
        for (InternetAddress addr : imm.getCcAddrList()) {
            header.append(prefix + "      ").append(addr.toUnicodeString()).append("\r\n");
            prefix = "    ";
        }
        prefix = " Bcc:";
        if (imm.getRecvType() == ImodeMail.RECV_TYPE_BCC && imm.getFolderId() != ImodeNetClient.FolderIdSent) {
            header.append(" Bcc:     ").append(imm.getMyMailAddr()).append("\r\n");
        }

        SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd (EEE) HH:mm:ss");
        header.append(" Date:    ").append(df.format(imm.getTimeDate())).append("\r\n");
        String subject = imm.getSubject();
        if (subjectEmojiReplace) {
            subject = EmojiUtil.replaceToLabel(subject);
        }
        header.append(" Subject: ").append(subject).append("\r\n");

        for (String s : imm.getOtherInfoList()) {
            header.append(" :").append(s).append("\r\n");
        }

        if (isHtml) {
            String fontfamily = conf.getMailFontFamily();
            if (fontfamily != null) {
                buf.append("<pre style=\"white-space:pre-wrap;word-wrap:break-word;font-family:\'" + fontfamily
                        + "\';\">");
            } else {
                buf.append("<pre style=\"white-space:pre-wrap;word-wrap:break-word;\">");
            }
        }
        buf.append("----").append("\r\n");
        if (isHtml) {
            buf.append(Util.easyEscapeHtml(header.toString()));
        } else {
            buf.append(header.toString());
        }
        buf.append("----").append("\r\n");
        if (isHtml) {
            buf.append("</pre>");
        }
        buf.append("\r\n");
        return buf.toString();
    }

    /** check if contains only ascii characters in text. */
    public static boolean isAllAscii(String text) {
        for (int i = 0; i < text.length(); i++) {
            if (text.charAt(i) > 0x7f) { // non-ascii
                return false;
            }
        }
        return true;
    }

    /* MimeUtility.encodeText?????????????encoder? */
    public static String encodeGoomojiSubject(String subject) throws UnsupportedEncodingException {
        final int maxlen = 75 - ("=?UTF-8?B?".length() + "?=".length());
        StringBuilder sb = new StringBuilder();

        int mark = 0;
        int utf8len = "X-Goomoji-Subject: ".length();
        for (int i = 0; i < subject.length();) {
            int cp = subject.codePointAt(i);
            int len;
            if (cp < 0x7f)
                len = 1;
            else if (cp <= 0x7ff)
                len = 2;
            else if (cp <= 0xffff)
                len = 3;
            else
                len = 4;

            if (4 * ((utf8len + len - 1) / 3 + 1) >= maxlen) {
                if (mark > 0)
                    sb.append("\r\n ");
                sb.append(MimeUtility.encodeWord(subject.substring(mark, i), "UTF-8", "B"));
                mark = i;
                utf8len = 0;
            }

            utf8len += len;
            i += Character.charCount(cp);
        }
        if (mark > 0)
            sb.append("\r\n ");
        sb.append(MimeUtility.encodeWord(subject.substring(mark), "UTF-8", "B"));

        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    public static InputStream png2gif(InputStream is) throws IOException {
        InputStream gis = null;
        BufferedInputStream pis = new BufferedInputStream(is);

        try {
            // iPhone iOS??iOS 4.3???????
            pis.mark(0);
            ImageInputStream iis = ImageIO.createImageInputStream(pis);
            Iterator i = ImageIO.getImageReaders(iis);
            if (i.hasNext() && ((ImageReader) i.next()).getFormatName().equals("gif")) {
                // ???gif?????????????
                pis.reset();
                gis = pis;
            }
        } catch (Exception e) {
        } finally {
            pis.reset();
        }
        if (gis != null) {
            return gis;
        }

        // PNG -> GIF?
        try {
            BufferedImage png = ImageIO.read(pis);
            ByteArrayOutputStream converted = new ByteArrayOutputStream();
            if (ImageIO.write(png, "gif", converted)) {
                gis = new ByteArrayInputStream(converted.toByteArray());
            }
        } catch (Exception e) {
        }
        if (gis != null) {
            return gis;
        } else {
            pis.reset();
            return pis;
        }
    }

    /*
     * ??
     */
    public static void stripLastEmptyLines(SenderMail sendMail) {
        String plainText = sendMail.getPlainTextContent();
        if (plainText != null) {
            plainText = HtmlConvert.replaceAllCaseInsenstive(plainText, "[\r\n]*\\z", "");
            sendMail.setPlainTextContent(plainText);
            //log.info("Stripped text: " + sendMail.getPlainTextContent());
        }

        String html = sendMail.getHtmlContent();
        if (html != null) {
            html = HtmlConvert.replaceAllCaseInsenstive(html, "(?:<br>|<div>(?:<br>)*</div>)*</body>", "</body>");
            sendMail.setHtmlContent(html);
            //log.info("Stripped html: " + sendMail.getHtmlContent());
        }
    }

    /*
     * iPhone?MobileMail.app???????
     */
    public static void stripAppleQuotedLines(SenderMail sendMail) {
        /*
         * <Region Format>
         * Japan
         *  On yyyy/mm/dd, at HH:MM,  wrote:
         * U.S.
         *  On (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) dd, yyyy, at HH:MM (PM|AM), address wrote:
         */
        /*
         * TEXT - ??
         *
         * | :
         * |
         * |On //, at :,  wrote:
         * |
         * |>
         */
        String plainText = sendMail.getPlainTextContent();
        if (plainText != null) {
            plainText = HtmlConvert.replaceAllCaseInsenstive(plainText,
                    "[\r\n]*On \\d+/\\d+/\\d+, at \\d+:\\d+, [^\n]* wrote:.*", "");
            plainText = HtmlConvert.replaceAllCaseInsenstive(plainText,
                    "[\r\n]*On \\w\\w\\w \\d+, \\d+, at \\d+:\\d+ \\wM, [^\n]* wrote:.*", "");
            sendMail.setPlainTextContent(plainText);
            //log.info("Stripped text: " + sendMail.getPlainTextContent());
        }

        /*
         * HTML - ??
         *
         * | :
         * | <div><br></div>
         * |</div>
         * |<div><br>
         * |On //, at :,  &lt;<a href="mailto:...></a>&gt; wrote:<br>
         * |<br><br></div>
         * |<div></div>
         * |<blockquote type="cite">
         */
        String html = sendMail.getHtmlContent();
        if (html != null) {
            // 
            html = HtmlConvert.replaceAllCaseInsenstive(html,
                    "(?:<div><br></div>)*</div><div><br>On \\d+/\\d+/\\d+, at \\d+:\\d+, [^<>]*(?:(?:<a href=[^<>]*>)+[^<>]*(?:</a>)+)+[^<>]* wrote:(?:<br>)*(?:</?[^<>]+>)+<blockquote type=.*</blockquote>",
                    "</div>");
            html = HtmlConvert.replaceAllCaseInsenstive(html,
                    "(?:<div><br></div>)*</div><div><br>On \\w\\w\\w \\d+, \\d+, at \\d+:\\d+ \\wM, [^<>]*(?:(?:<a href=[^<>]*>)+[^<>]*(?:</a>)+)+[^<>]* wrote:(?:<br>)*(?:</?[^<>]+>)+<blockquote type=.*</blockquote>",
                    "</div>");
            // htmlWorkingContent?
            html = HtmlConvert.replaceAllCaseInsenstive(html,
                    "(?:<br>)*On \\d+/\\d+/\\d+, at \\d+:\\d+, [^<>]* wrote:(?:<br>)*&gt;.*</body>", "</body>");
            html = HtmlConvert.replaceAllCaseInsenstive(html,
                    "(?:<br>)*On \\w\\w\\w \\d+, \\d+, at \\d+:\\d+ \\wM, [^<>]* wrote:(?:<br>)*&gt;.*</body>",
                    "</body>");
            sendMail.setHtmlContent(html);
            //log.info("Stripped html: " + sendMail.getHtmlContent());
        }
    }
}