package org.subethamail.core.util;
import java.util.logging.Level;
import lombok.extern.java.Log;
import org.apache.commons.codec.binary.Base64;
/**
* Static methods for "Base62" encoding and decoding. A Base62
* encoded string is very similar to Base64, but eliminates the
* '/', '+', and '='. Having only the characters A-Za-z0-9
* helps prevent MUAs from putting linebreaks in the middle
* of longish encryption tokens.
*
* @author Jeff Schnitzer
*/
@Log
public class Base62
{
/** default constructor prevents util class from being created. */
private Base62() {}
/**
* Encodes bytes as a Base62 string.
*
* Having tokens that are a seamless string of letters and numbers
* means that MUAs are less likely to linebreak a long token.
*/
public static String encode(byte[] data)
{
String base64 = new String(Base64.encodeBase64(data));
return base64ToBase62(base64);
}
/**
* Returns a Base62 encoded string to it's original state.
*/
public static byte[] decode(String base62)
{
String base64 = base62ToBase64(base62);
return Base64.decodeBase64(base64.getBytes());
}
/**
* Takes a base64 encoded string and eliminates the '+' and '/'.
* Also eliminates any CRs.
*
* Having tokens that are a seamless string of letters and numbers
* means that MUAs are less likely to linebreak a long token.
*/
protected static String base64ToBase62(String base64)
{
StringBuffer buf = new StringBuffer(base64.length() * 2);
for (int i=0; i<base64.length(); i++)
{
char ch = base64.charAt(i);
switch (ch)
{
case 'i':
buf.append("ii");
break;
case '+':
buf.append("ip");
break;
case '/':
buf.append("is");
break;
case '=':
buf.append("ie");
break;
case '\n':
// Strip out
break;
default:
buf.append(ch);
}
}
if (log.isLoggable(Level.FINE))
log.log(Level.FINE,"encodeBase62 from {0} to {1}", new Object[]{base64, buf});
return buf.toString();
}
/**
* Returns a string encoded with encodeBase62 to its original
* (base64 encoded) state.
*/
protected static String base62ToBase64(String base62)
{
StringBuffer buf = new StringBuffer(base62.length());
int i = 0;
while (i < base62.length())
{
char ch = base62.charAt(i);
if (ch == 'i')
{
i++;
char code = base62.charAt(i);
switch (code)
{
case 'i':
buf.append('i');
break;
case 'p':
buf.append('+');
break;
case 's':
buf.append('/');
break;
case 'e':
buf.append('=');
break;
default:
throw new IllegalStateException("Illegal code in base62 encoding");
}
}
else
{
buf.append(ch);
}
i++;
}
if (log.isLoggable(Level.FINE))
log.log(Level.FINE,"decodeBase62 from {0} to {1}", new Object[]{base62,buf});
return buf.toString();
}
}