/* * Copyright 2013 cruxframework.org. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.cruxframework.crux.core.client.encoder; import com.google.gwt.core.client.GWT; /** * Cross browser Base64 encoder / decoder. * This class consider a "string" where each character represents an 8-bit byte. * If you pass a string containing characters that can't be represented in 8 bits, it will probably break. * @author Thiago da Rosa de Bustamante * */ public class Base64 { private static Impl instance; private Base64(){} /** * Encode the given input into a base64 output. * Since DOMStrings are 16-bit-encoded strings, in most browsers calling this on a Unicode string will cause a Character * Out Of Range exception if a character exceeds the range of a 8-bit ASCII-encoded character. There are two possible methods to solve this problem: * <p>the first one is to escape the whole string and then encode it;</p> * <p>the second one is to convert the UTF-16 DOMString to an UTF-8 array of characters and then encode it.</p> * * @param input * @return */ public static String encode(String input) { return getImplementation().encode(input); } /** * Decode the given base64 input. * This method returns a "string" where each character represents an 8-bit byte. * * @param input * @return */ public static String decode(String input) { return getImplementation().decode(input); } /** * Gets the encoder implementation * @return */ private static Impl getImplementation() { if (instance == null) { instance = GWT.create(Impl.class); } return instance; } /** * Implementation contract * @author Thiago da Rosa de Bustamante * */ static interface Impl { String encode(String input); String decode(String input); } /** * Implementation used by browsers that support atob and btoa functions. * @author Thiago da Rosa de Bustamante */ static class NativeImpl implements Impl { @Override public native String encode(String input)/*-{ return btoa(input); }-*/; @Override public native String decode(String input)/*-{ return atob(input); }-*/; } /** * Implementation used by browsers that does not support atob and btoa functions. * @author Thiago da Rosa de Bustamante */ static class EmulatedImpl implements Impl { @Override public native String encode(String input)/*-{ var o1, o2, o3, bits, h1, h2, h3, h4, e=[], pad = '', c, plain, coded; var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; plain = input; c = plain.length % 3; // pad string to length of multiple of 3 if (c > 0) { while (c++ < 3) { pad += '='; plain += '\0'; } } // note: doing padding here saves us doing special-case packing for trailing 1 or 2 chars for (c=0; c<plain.length; c+=3) { // pack three octets into four hexets o1 = plain.charCodeAt(c); o2 = plain.charCodeAt(c+1); o3 = plain.charCodeAt(c+2); bits = o1<<16 | o2<<8 | o3; h1 = bits>>18 & 0x3f; h2 = bits>>12 & 0x3f; h3 = bits>>6 & 0x3f; h4 = bits & 0x3f; // use hextets to index into code string e[c/3] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); } coded = e.join(''); // replace 'A's from padded nulls with '='s coded = coded.slice(0, coded.length-pad.length) + pad; return coded; }-*/; @Override public native String decode(String input)/*-{ var o1, o2, o3, h1, h2, h3, h4, bits, d=[], plain, coded; var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; coded = input; for (var c=0; c<coded.length; c+=4) { // unpack four hexets into three octets h1 = b64.indexOf(coded.charAt(c)); h2 = b64.indexOf(coded.charAt(c+1)); h3 = b64.indexOf(coded.charAt(c+2)); h4 = b64.indexOf(coded.charAt(c+3)); bits = h1<<18 | h2<<12 | h3<<6 | h4; o1 = bits>>>16 & 0xff; o2 = bits>>>8 & 0xff; o3 = bits & 0xff; d[c/4] = String.fromCharCode(o1, o2, o3); // check for padding if (h4 == 0x40) d[c/4] = String.fromCharCode(o1, o2); if (h3 == 0x40) d[c/4] = String.fromCharCode(o1); } plain = d.join(''); return plain; }-*/; } }