/* * Funambol is a mobile platform developed by Funambol, Inc. * Copyright (C) 2008 Funambol, Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by * the Free Software Foundation with the addition of the following permission * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. * * 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 Affero General Public License * along with this program; if not, see http://www.gnu.org/licenses or write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA. * * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License * version 3, these Appropriate Legal Notices must retain the display of the * "Powered by Funambol" logo. If the display of the logo is not reasonably * feasible for technical reasons, the Appropriate Legal Notices must display * the words "Powered by Funambol". */ package com.funambol.common.pim; import java.io.UnsupportedEncodingException; import java.io.OutputStream; import java.io.IOException; import com.funambol.util.QuotedPrintable; import com.funambol.util.Log; import com.funambol.util.StringUtil; /** * This class contains utilities for the PIM module. In particular it provides * methods to fold, unfold, escape, unescape, encode e decode. All these tasks * are required by most PIM formats (vCard, vCal and so on). */ public class Utils { private final int MAX_FOLDING_LINE_LENGHT = 75; public static final char FOLDING_INDENT_CHAR = ' '; private String defaultCharset = null; public Utils(String defaultCharset) { this.defaultCharset = defaultCharset; } /** * Folds a string (i.e. folds a text field in order to not exceed 75 chars * for each string line, by inserting a CLRF line break and a tabulation as a * prefix for each line) */ public String fold(String str) { StringBuffer result = new StringBuffer(); for(int i=0;i<str.length();i+=MAX_FOLDING_LINE_LENGHT) { if (i + MAX_FOLDING_LINE_LENGHT < str.length()) { result.append(str.substring(i, i + MAX_FOLDING_LINE_LENGHT)); result.append("\r\n" + FOLDING_INDENT_CHAR); } else { result.append(str.substring(i)); } } return result.toString(); } /** * Unfolds a string (i.e. removes all the CRLF characters) */ public String unfold (String str) { int ind = str.indexOf("\r\n"); if (ind == -1) { return unfoldNewline(str); } else { String tmpString1 = str.substring(0,ind); String tmpString2 = str.substring(ind+2); return unfoldNewline(unfold(tmpString1+tmpString2)); } } /** * Unfolds a string (i.e. removes all the line break characters). * This function is meant to ensure compatibility with vCard documents * that adhere loosely to the specification */ public String unfoldNewline (String str) { int ind = str.indexOf("\n"); if (ind == -1) { return str; } else { String tmpString1 = str.substring(0,ind); String tmpString2 = str.substring(ind+1); return unfoldNewline(tmpString1+tmpString2); } } /** * Decode the given text according to the given encoding and charset * * @param text the text to decode * @param encoding the encoding * @param propertyCharset the charset * * @return the text decoded */ public String decode(String text, String encoding, String propertyCharset) { if (text == null) { return null; } // // If input charset is null then set it with default charset // if (propertyCharset == null) { propertyCharset = defaultCharset; // we use the default charset } if (encoding != null) { if ("QUOTED-PRINTABLE".equals(encoding)) { try { byte textBytes[] = text.getBytes(propertyCharset); int len = QuotedPrintable.decode(textBytes); String res = new String(textBytes, 0, len, propertyCharset); return res; } catch (UnsupportedEncodingException ue) { Log.error("Cannot decode quoted printable: " + text); // In this case we keep this value return text; } } } else { try { return new String(text.getBytes(propertyCharset), propertyCharset); } catch (UnsupportedEncodingException ue) { // In this case we keep this value return text; } } return text; } /** * Removes the last equals from the end of the given String */ private String removeLastEquals(String data) { if (data == null) { return data; } data = data.trim(); while (data.endsWith("=")) { data = data.substring(0, data.length() - 1); } return data; } /** * Unescape '\' ',' ';' '\n' '\N' chars * * @param text the text to unescape * @return String the unescaped text */ public String unescape(String text) { if (text == null) { return text; } StringBuffer value = new StringBuffer(); int length = text.length(); boolean foundSlash = false; for (int i=0; i<length; i++) { char ch = text.charAt(i); switch (ch) { case '\\': if (foundSlash) { foundSlash = false; value.append('\\'); } else { foundSlash = true; } break; case ';': value.append(';'); foundSlash = false; break; case ',': value.append(','); foundSlash = false; break; case 'n': if(foundSlash) { value.append('\n'); } else { value.append('n'); } foundSlash = false; break; case 'N': if(foundSlash) { value.append('\n'); } else { value.append('N'); } foundSlash = false; break; default: if (foundSlash) { foundSlash = false; value.append('\\'); } value.append(ch); break; } } return value.toString(); } /** * Escape special chars: '\' ';' ',' '\n' * @param msg message to escape, * @param escapeComma boolean to escape or not the comma character. In some cases * for Vcard is not necessary escape commas. * @return the escaped message */ public String escape(String msg, boolean escapeComma) { return escape(msg, escapeComma, true); } /** * Escape special chars: '\' ';' ',' '\n' * @param msg message to escape, * @param escapeComma boolean to escape or not the comma character. In some cases * for Vcard is not necessary escape commas. * @param escapeLF boolean to escape LF or not * @return the escaped message */ public String escape(String msg, boolean escapeComma, boolean escapeLF) { if (msg == null) { return null; } StringBuffer res = new StringBuffer(); for(int i=0;i<msg.length();++i) { char ch = msg.charAt(i); if (ch == '\\') { res.append("\\\\"); } else if (ch == ';') { res.append("\\;"); } else if (ch == ',' && escapeComma) { res.append("\\,"); } else if (ch == '\n' && escapeLF) { res.append("\\n"); } else { res.append(ch); } } try { String enc = new String(res.toString().getBytes(defaultCharset), defaultCharset); return enc; } catch (UnsupportedEncodingException e) { Log.error("[Utils.escape] Cannot convert string " + e.toString()); } return res.toString(); } }