+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+
+/** General <tt>String</tt> and <tt>byte[]</tt> processing functions,
+ * including Base64 and a safe filename transform.
+ *
+ * @author adam@ibex.org
+ */
+public final class Encode {
+
+ public static class QuotedPrintable {
+ public static String decode(String s, boolean lax) {
+ //
+ // =XX -> hex representation, must be uppercase
+ // 9, 32, 33-60, 62-126 can be literal
+ // 9, 32 at end-of-line must get encoded
+ // trailing whitespace must be deleted when decoding
+ // =\n = soft line break
+ // lines cannot be more than 76 chars long
+ //
+
+ // lax is used for RFC2047 headers; removes restrictions on which chars you can encode
+ return s;
+ }
+ }
+
+
+ public static class RFC2047 {
+ public static String decode(String s) {
+ /*
+ try { while (s.indexOf("=?") != -1) {
+ String pre = s.substring(0, s.indexOf("=?"));
+ s = s.substring(s.indexOf("=?") + 2);
+
+ // MIME charset; FIXME use this
+ String charset = s.substring(0, s.indexOf('?')).toLowerCase();
+ s = s.substring(s.indexOf('?') + 1);
+
+ String encoding = s.substring(0, s.indexOf('?')).toLowerCase();
+ s = s.substring(s.indexOf('?') + 1);
+
+ String encodedText = s.substring(0, s.indexOf("?="));
+
+ if (encoding.equals("b")) encodedText = new String(Base64.decode(encodedText));
+
+ // except that ANY char can be endoed (unlike real qp)
+ else if (encoding.equals("q")) encodedText = MIME.QuotedPrintable.decode(encodedText, true);
+ else Log.warn(MIME.class, "unknown RFC2047 encoding \""+encoding+"\"");
+
+ String post = s.substring(s.indexOf("?=") + 2);
+ s = pre + encodedText + post;
+
+ // FIXME re-encode when transmitting
+
+ } } catch (Exception e) {
+ Log.warn(MIME.class, "error trying to decode RFC2047 encoded-word: \""+s+"\"");
+ Log.warn(MIME.class, e);
+ }
+ */
+ return s;
+ }
+ }
+
+
+ public static long twoFloatsToLong(float a, float b) {
+ return ((Float.floatToIntBits(a) & 0xffffffffL) << 32) | (Float.floatToIntBits(b) & 0xffffffffL); }
+ public static float longToFloat1(long l) { return Float.intBitsToFloat((int)((l >> 32) & 0xffffffff)); }
+ public static float longToFloat2(long l) { return Float.intBitsToFloat((int)(l & 0xffffffff)); }
+
+ private static final char[] fn =
+ new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+ public static String toFilename(String s) {
+ StringBuffer sb = new StringBuffer();
+ try {
+ byte[] b = s.getBytes("UTF-8");
+ for(int i=0; i<b.length; i++) {
+ char c = (char)(b[i] & 0xff);
+ if (c == File.separatorChar || c < 32 || c > 126 || c == '%' || (i == 0 && c == '.'))
+ sb.append("%" + fn[(b[i] & 0xf0) >> 8] + fn[b[i] & 0xf]);
+ else sb.append(c);
+ }
+ return sb.toString();
+ } catch (UnsupportedEncodingException uee) {
+ throw new Error("this should never happen; Java spec mandates UTF-8 support");
+ }
+ }
+
+ public static String fromFilename(String s) {
+ StringBuffer sb = new StringBuffer();
+ byte[] b = new byte[s.length() * 2];
+ int bytes = 0;
+ for(int i=0; i<s.length(); i++) {
+ char c = s.charAt(i);
+ if (c == '%') b[bytes++] = (byte)Integer.parseInt(("" + s.charAt(++i) + s.charAt(++i)), 16);
+ else b[bytes++] = (byte)c;
+ }
+ try {
+ return new String(b, 0, bytes, "UTF-8");
+ } catch (UnsupportedEncodingException uee) {
+ throw new Error("this should never happen; Java spec mandates UTF-8 support");
+ }
+ }
+
+
+ private static final byte[] encB64 = {
+ (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
+ (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
+ (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
+ (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
+ (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
+ (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
+ (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
+ (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', (byte)'0', (byte)'1',
+ (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8',
+ (byte)'9', (byte)'+', (byte)'/'
+ };
+
+ // FIXME could be far more efficient
+ public static class Base64InputStream extends ByteArrayInputStream {
+ public Base64InputStream(String s) { super(fromBase64(s.getBytes())); }
+ }
+
+ public static byte[] toBase64(String data) { return toBase64(data.getBytes()); }
+
+ public static String toBase64String(byte[] data) { return new String(toBase64(data)); }
+
+ /** Encode the input data producong a base 64 encoded byte array.
+ * @return A byte array containing the base 64 encoded data. */
+ public static byte[] toBase64(byte[] data) {
+ byte[] bytes;
+
+ int modulus = data.length % 3;
+ if (modulus == 0) {
+ bytes = new byte[4 * data.length / 3];
+ } else {
+ bytes = new byte[4 * ((data.length / 3) + 1)];
+ }