/**
* OnionCoffee - Anonymous Communication through TOR Network
* Copyright (C) 2005-2007 RWTH Aachen University, Informatik IV
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package TorJava.Common;
import java.util.Date;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import TorJava.Logger;
/**
* this class contains utility functions concerning encodings
*
* @author Lexi Pimenidis
* @author Andriy Panchenko
* @author Michael Koellejan
*/
public class Encoding {
static String[] hexChars = { "00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff" };
/**
* Converts a byte array to hex string
*/
public static String toHexString(byte[] block, int column_width, int offset,
int length) {
byte[] temp = new byte[length];
System.arraycopy(block, offset, temp, 0, length);
return toHexString(temp, column_width);
}
/**
* Converts a byte array to hex string
*/
public static String toHexString(byte[] block, int column_width) {
StringBuffer buf = new StringBuffer(4*(block.length+2));
for (int i = 0; i < block.length; i++) {
if (i > 0) {
buf.append(":");
if (i % (column_width / 3) == 0)
buf.append("\n");
}
;
buf.append(hexChars[block[i] & 0xff]);
}
return buf.toString();
}
/**
* Converts a byte array to hex string
*/
/*public static String toHexStringNoColon(byte[] block) {
StringBuffer buf = new StringBuffer(4*(block.length+2));
for (int i = 0; i < block.length; i++)
buf.append(hexChars[block[i] & 0xff]);
return buf.toString();
}*/
private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
public static final String toHexStringNoColon(byte[] buf)
{
char[] chars = new char[2 * buf.length];
for (int i = 0; i < buf.length; ++i)
{
chars[2 * i] = HEX_CHARS[(buf[i] & 0xF0) >>> 4];
chars[2 * i + 1] = HEX_CHARS[buf[i] & 0x0F];
}
return new String(chars);
}
public static String toHexString(byte[] block) {
return toHexString(block, block.length * 3 + 1);
}
/**
* Convert int to the array of bytes
*
* @param myInt
* integer to convert
* @param n
* size of the byte array
* @return byte array of size n
*
*/
public static byte[] intToNByteArray(int myInt, int n) {
byte[] myBytes = new byte[n];
for (int i = 0; i < n; ++i) {
myBytes[i] = (byte) ((myInt >> ((n - i - 1) * 8)) & 0xff);
}
return myBytes;
}
/**
* wrapper to convert int to the array of 2 bytes
*
* @param myInt
* integer to convert
* @return byte array of size two
*/
public static byte[] intTo2ByteArray(int myInt) {
return intToNByteArray(myInt, 2);
}
/**
* Convert the byte array to an int starting from the given offset.
*
* @param b
* byte array
* @param offset
* array offset
* @param length
* number of bytes to convert
* @return integer
*/
public static int byteArrayToInt(byte[] b, int offset, int length) {
int value = 0;
int numbersToConvert = b.length - offset;
int n = Math.min(length, 4); // 4 bytes is max int size (2^32)
n = Math.min(n, numbersToConvert); // make sure we are not out of array
// bounds
// if (numbersToConvert > 4)
// offset = b.length - 4; // warning: offset has been changed
// in order to convert LSB
for (int i = 0; i < n; i++) {
int shift = (n - 1 - i) * 8;
value += (b[i + offset] & 0xff) << shift;
}
return value;
}
/**
* Convert the byte array to an int
*
* @param b
* byte array
* @return the integer
*
*/
public static int byteArrayToInt(byte[] b) {
return byteArrayToInt(b, 0, b.length);
}
/**
* converts a notation like 192.168.3.101 into a binary format
*
* @param s
* a string containing the dotted notation
* @return the binary format
*/
public static long dottedNotationToBinary(String s) {
long temp = 0;
Pattern p = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)");
Matcher m = p.matcher(s);
if (m.find()) {
for (int i = 1; i <= 4; ++i) {
temp = temp << 8;
temp = temp | Integer.parseInt(m.group(i));
}
;
}
;
return temp;
}
/**
* converts netmask into int - number of significant bits
* @param netmask netmask
*
* @return number of significant bits
*/
public static int netmaskToInt(long netmask){
int result = 0;
while ( (netmask & 0xffffffffL) != 0){
netmask = netmask << 1;
result++;
}
return result;
}
/**
* converts our binary format back into dotted-decimal notation
*
* @param ip
* binary encoded ip-address.
*/
public static String binaryToDottedNotation(long ip) {
StringBuffer dottedNotation = new StringBuffer();
dottedNotation.append(((ip & 0xff000000) >> 24) + ".");
dottedNotation.append(((ip & 0x00ff0000) >> 16) + ".");
dottedNotation.append(((ip & 0x0000ff00) >> 8) + ".");
dottedNotation.append(((ip & 0x000000ff) >> 0));
return dottedNotation.toString();
}
/** creates an base64-string out of a byte[] */
public static String toBase64(byte[] data) {
return new String(org.bouncycastle.util.encoders.Base64.encode(data));
}
/**
* parses a base64-String. <br>
* <b>Q</b>: Why doesn't provide Java us with such a functionality? <br>
* <b>A</b>: Because it sucks. <br>
* <b>A</b>: RTFM e.g.
*
* @see sun.misc.BASE64Decoder <br>
*
* @param s
* a string that contains a base64 encoded array
* @return the decoded array
*/
public static byte[] parseBase64(String s) {
byte[] b = new byte[s.length() + 5]; // size is a upper approximation
// of expected length needed
int fill = 0;
char[] c = s.toCharArray();
// main loop
int temp = 0; // stores the reconstructed bitfield
int temp_fill = 0;
String base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < c.length; ++i) { // loop over the complete string
int v = base64.indexOf(c[i]);
if (v >= 0) { // base64 char found
temp = temp << 6;
temp = temp | v;
++temp_fill;
if (temp_fill >= 4) { // filled up 24bit
System.arraycopy(Encoding.intToNByteArray(temp, 3), 0, b,
fill, 3);
fill += 3;
temp_fill = 0;
temp = 0;
}
;
} else {
// the string to be parsed does obviously contain other
// characters, too.
}
;
}
;
if (temp_fill > 0) { // end of string, 24 bit buffer still filled?
if (temp_fill == 1)
temp = temp << 18;
if (temp_fill == 2)
temp = temp << 12;
if (temp_fill == 3)
temp = temp << 6;
System.arraycopy(Encoding.intToNByteArray(temp, 3), 0, b, fill, 3);
if (temp_fill == 1)
fill += 2;
if (temp_fill == 2)
fill += 2;
if (temp_fill == 3)
fill += 2;
}
;
// copy from temp array to return-array
byte[] ret = new byte[fill];
for (int i = 0; i < fill; ++i)
ret[i] = b[i];
return ret;
}
/**
* parses a hex-string into a byte-array.<br>
* <b>Q</b>: Why doesn't provide Java us with such a functionality? <br>
* <b>A</b>: Because it sucks.
*
* @param s
* a string that contains a hex-encoded array
* @return the decodeed array
*/
public static byte[] parseHex(String s) {
byte[] b = new byte[s.length()]; // size is a worst-case
// approximation of needed length
int p = 0;
char[] c = s.toCharArray();
String hex = "0123456789ABCDEF";
s = s.toUpperCase();
// loop over complete string
boolean first = true;
for (int i = 0; i < c.length; ++i) {
byte temp = (byte) hex.indexOf(c[i]);
if (temp >= 0) {
b[p] = (byte) (b[p] | temp);
first = !first;
if (first) { // put together two nibbles
++p;
if (p >= b.length)
return b;
} else {
b[p] = (byte) (b[p] << 4);
}
;
}
;
}
;
// copy to return array
byte[] ret = new byte[p];
for (int i = 0; i < p; ++i)
ret[i] = b[i];
return ret;
}
/**
* do a base32-enconding from a binary field
*/
public static String toBase32(byte[] data) {
String base32 = "abcdefghijklmnopqrstuvwxyz234567";
StringBuffer sb = new StringBuffer();
int b32 = 0;
int b32_filled = 0;
for (int pos = 0; pos < data.length; ++pos)
for (int bitmask = 128; bitmask > 0; bitmask /= 2) {
b32 = (b32 << 1);
if (((int) data[pos] & bitmask) != 0)
b32 = b32 | 1;
++b32_filled;
if (b32_filled == 5) {
sb.append(base32.charAt(b32)); // transform to
// base32-encoding
b32 = 0;
b32_filled = 0;
}
}
// check if bits were left unencoded
if (b32_filled != 0)
Logger.logGeneral(Logger.WARNING,
"Common.toBase32: received array with unsupported number of bits "
+ Encoding.toHexString(data));
// return result
sb.append(".onion");
return sb.toString();
}
/**
* returns a string with a hex-representation of the provided long
*/
public static String toHex(long n) {
String hex = "0123456789abcdef";
int[] octet = new int[4];
octet[0] = (int) ((n >> 24) & 0xff);
octet[1] = (int) ((n >> 16) & 0xff);
octet[2] = (int) ((n >> 8) & 0xff);
octet[3] = (int) ((n) & 0xff);
StringBuffer buf = new StringBuffer();
for (int i = 0; i < 4; ++i) {
buf.append(hex.substring(octet[i] >> 4, (octet[i] >> 4) + 1));
buf.append(hex.substring(octet[i] & 0xf, (octet[i] & 0xf) + 1));
buf.append(" ");
}
;
return buf.toString();
}
/**
* compares two arrays.
*
* @return true, if the two arrays are equal
*/
public static boolean arraysEqual(byte[] one, byte[] two) {
if ((one == null) && (two == null))
return true;
if ((one != null) && (two == null)) {
// System.err.println("first array contains data, second doesn't");
return false;
}
if ((one == null) && (two != null)) {
// System.err.println("seconds array contains data, first doesn't");
return false;
}
if (one.length != two.length) {
// System.err.println("Different size: "+one.length+" !=
// "+two.length);
return false;
}
for (int i = 0; i < one.length; ++i)
if (one[i] != two[i]) {
// System.err.println("byte "+i+" of "+one.length+" differs:
// "+one[i]+" != "+two[i]);
return false;
}
;
return true;
}
/**
* makes hashmap with x,y,z parts of the hidden service address
*
* @param hostname
* hostname of the hidden service
* @return hashmap with keys x,y,z
*/
public static HashMap<String,String> parseHiddenAddress(String hostname) {
String x, y, z;
HashMap<String,String> result = new HashMap<String,String>(3);
z = hostname;
z = z.replaceFirst(".onion", "");
x = Parsing.parseStringByRE(z, "(.*?)\\.", "");
z = z.replaceFirst(x + "\\.", "");
y = Parsing.parseStringByRE(z, "(.*?)\\.", "");
z = z.replaceFirst(y + "\\.", "");
if (y == "") {
y = x;
x = "";
}
result.put("x", x);
result.put("y", y);
result.put("z", z);
return result;
}
/**
* takes a string and returns it urlencoded
*/
public static String UrlEncode(String input) {
StringBuffer output = new StringBuffer();
char[] c = input.toCharArray();
for(int i=0;i<c.length;++i) {
if ( Character.isDigit(c[i]) || Character.isLetter(c[i]) ) {
output.append(c[i]);
} else {
output.append("%"+hexChars[c[i] & 0xff]);
}
}
return output.toString();
}
/**
* Parses a date from the provided string. Faster than
* using built-in date parser.
* @param date date to be parsed
* @return the date object
*/
public final static Date parseDate(String date) {
int year = Integer.parseInt(date.substring(0, 4));
int month = Integer.parseInt(date.substring(5, 7));
int day = Integer.parseInt(date.substring(8, 10));
int hour = Integer.parseInt(date.substring(11, 13));
int min = Integer.parseInt(date.substring(14, 16));
int sec = Integer.parseInt(date.substring(17, 19));
return new Date(year, month, day, hour, min, sec);
}
}