/*
* Created on 7 Jun 2007
* Created by Allan Crooks
* Copyright (C) 2007 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*/
package com.aelitis.azureus.core.peermanager.utils;
/**
* @author Allan Crooks
*/
class BTPeerIDByteDecoderUtils {
public static String decodeMnemonic(char c) {
switch (c) {
case 'b':
case 'B':
return "Beta";
case 'Z':
return "(Dev)"; // Just for Transmission at the moment.
}
return null;
}
public static String decodeNumericValueOfByte(byte b) {
return String.valueOf((int)b & 0xFF);
}
public static String decodeNumericValueOfByte(byte b, int min_digits) {
String result = decodeNumericValueOfByte(b);
while (result.length() < min_digits) {result = "0" + result;}
return result;
}
public static String decodeNumericChar(char c) {
String result = decodeAlphaNumericChar(c);
if (result == null || result.length() == 1) {return result;}
return null;
}
public static String intchar(char c) {
String result = decodeNumericChar(c);
if (result == null) {throw new IllegalArgumentException("not an integer character: " + c);}
return result;
}
public static String decodeAlphaNumericChar(char c) {
switch (c) {
case '0':
return "0";
case '1':
return "1";
case '2':
return "2";
case '3':
return "3";
case '4':
return "4";
case '5':
return "5";
case '6':
return "6";
case '7':
return "7";
case '8':
return "8";
case '9':
return "9";
case 'A':
return "10";
case 'B':
return "11";
case 'C':
return "12";
case 'D':
return "13";
case 'E':
return "14";
case 'F':
return "15";
case 'G':
return "16";
case 'H':
return "17";
case 'I':
return "18";
case 'J':
return "19";
case 'K':
return "20";
case 'L':
return "21";
case 'M':
return "22";
case 'N':
return "23";
case 'O':
return "24";
case 'P':
return "25";
case 'Q':
return "26";
case 'R':
return "27";
case 'S':
return "28";
case 'T':
return "29";
case 'U':
return "30";
case 'V':
return "31";
case 'W':
return "32";
case 'X':
return "33";
case 'Y':
return "34";
case 'Z':
return "35";
case 'a':
return "36";
case 'b':
return "37";
case 'c':
return "38";
case 'd':
return "39";
case 'e':
return "40";
case 'f':
return "41";
case 'g':
return "42";
case 'h':
return "43";
case 'i':
return "44";
case 'j':
return "45";
case 'k':
return "46";
case 'l':
return "47";
case 'm':
return "48";
case 'n':
return "49";
case 'o':
return "50";
case 'p':
return "51";
case 'q':
return "52";
case 'r':
return "53";
case 's':
return "54";
case 't':
return "55";
case 'u':
return "56";
case 'v':
return "57";
case 'w':
return "58";
case 'x':
return "59";
case 'y':
return "60";
case 'z':
return "61";
case '.':
return "62";
//case '-':
// return "63";
}
return null;
}
public static boolean isAzStyle(String peer_id) {
if (peer_id.charAt(0) != '-') {return false;}
if (peer_id.charAt(7) == '-') {return true;}
/**
* Hack for FlashGet - it doesn't use the trailing dash.
* Also, LH-ABC has strayed into "forgetting about the delimiter" territory.
*
* In fact, the code to generate a peer ID for LH-ABC is based on BitTornado's,
* yet tries to give an Az style peer ID... oh dear.
*/
if (peer_id.substring(1, 3).equals("FG")) {return true;}
if (peer_id.substring(1, 3).equals("LH")) {return true;}
return false;
}
/**
* Checking whether a peer ID is Shadow style or not is a bit tricky.
*
* The BitTornado peer ID convention code is explained here:
* http://forums.degreez.net/viewtopic.php?t=7070
*
* The main thing we are interested in is the first six characters.
* Although the other characters are base64 characters, there's no
* guarantee that other clients which follow that style will follow
* that convention (though the fact that some of these clients use
* BitTornado in the core does blur the lines a bit between what is
* "style" and what is just common across clients).
*
* So if we base it on the version number information, there's another
* problem - there isn't the use of absolute delimiters (no fixed dash
* character, for example).
*
* There are various things we can do to determine how likely the peer
* ID is to be of that style, but for now, I'll keep it to a relatively
* simple check.
*
* We'll assume that no client uses the fifth version digit, so we'll
* expect a dash. We'll also assume that no client has reached version 10
* yet, so we expect the first two characters to be "letter,digit".
*
* We've seen some clients which don't appear to contain any version
* information, so we need to allow for that.
*/
public static boolean isShadowStyle(String peer_id) {
if (peer_id.charAt(5) != '-') {return false;}
if (!Character.isLetter(peer_id.charAt(0))) {return false;}
if (!(Character.isDigit(peer_id.charAt(1)) || peer_id.charAt(1) == '-')) {return false;}
// Find where the version number string ends.
int last_ver_num_index = 4;
for (; last_ver_num_index>0; last_ver_num_index--) {
if (peer_id.charAt(last_ver_num_index) != '-') {break;}
}
// For each digit in the version string, check it is a valid version identifier.
char c;
for (int i=1; i<=last_ver_num_index; i++) {
c = peer_id.charAt(i);
if (c == '-') {return false;}
if (decodeAlphaNumericChar(c) == null) {return false;}
}
return true;
}
public static boolean isMainlineStyle(String peer_id) {
/**
* One of the following styles will be used:
* Mx-y-z--
* Mx-yy-z-
*/
return peer_id.charAt(2) == '-' && peer_id.charAt(7) == '-' && (
peer_id.charAt(4) == '-' || peer_id.charAt(5) == '-');
}
public static boolean isPossibleSpoofClient(String peer_id) {
return peer_id.endsWith("UDP0") || peer_id.endsWith("HTTPBT");
}
public static String getMainlineStyleVersionNumber(String peer_id) {
boolean two_digit_in_middle = peer_id.charAt(5) == '-';
String middle_part = decodeNumericChar(peer_id.charAt(3));
if (two_digit_in_middle) {
middle_part = join(middle_part, decodeNumericChar(peer_id.charAt(4)));
}
return joinAsDotted(
decodeNumericChar(peer_id.charAt(1)), middle_part,
decodeNumericChar(peer_id.charAt(two_digit_in_middle ? 6 : 5))
);
}
public static String getShadowStyleVersionNumber(String peer_id) {
String ver_number = decodeAlphaNumericChar(peer_id.charAt(1));
if (ver_number == null) {return null;}
for (int i=2; i<6 && ver_number != null; i++) {
char c = peer_id.charAt(i);
if (c == '-') {break;}
ver_number = joinAsDotted(ver_number, decodeAlphaNumericChar(peer_id.charAt(i)));
}
// We'll strip off trailing redundant zeroes.
while (ver_number.endsWith(".0")) {ver_number = ver_number.substring(0, ver_number.length()-2);}
return ver_number;
}
public static String decodeAzStyleVersionNumber(String version_data, String version_scheme) {
char a = version_data.charAt(0);
char b = version_data.charAt(1);
char c = version_data.charAt(2);
char d = version_data.charAt(3);
/**
* Specialness for Transmission - thanks to BentMyWookie for the information,
* I'm sure he'll be reading this comment anyway... ;)
*/
if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_TRANSMISSION_STYLE) {
// Very old client style: -TR0006- is 0.6.
if (version_data.startsWith("000")) {
version_scheme = "3.4";
}
// Previous client style: -TR0072- is 0.72.
else if (version_data.startsWith("00")) {
version_scheme = BTPeerIDByteDecoderDefinitions.VER_AZ_SKIP_FIRST_ONE_MAJ_TWO_MIN;
}
// Current client style: -TR072Z- is 0.72 (Dev).
else {
version_scheme = BTPeerIDByteDecoderDefinitions.VER_AZ_ONE_MAJ_TWO_MIN_PLUS_MNEMONIC;
}
}
if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_FOUR_DIGITS) {
return intchar(a) + "." + intchar(b) + "." + intchar(c) + "." + intchar(d);
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_THREE_DIGITS ||
version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_THREE_DIGITS_PLUS_MNEMONIC ||
version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_ONE_MAJ_TWO_MIN_PLUS_MNEMONIC) {
String result;
if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_ONE_MAJ_TWO_MIN_PLUS_MNEMONIC) {
result = intchar(a) + "." + intchar(b) + intchar(c);
}
else {
result = intchar(a) + "." + intchar(b) + "." + intchar(c);
}
if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_THREE_DIGITS_PLUS_MNEMONIC ||
version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_ONE_MAJ_TWO_MIN_PLUS_MNEMONIC) {
String mnemonic = decodeMnemonic(d);
if (mnemonic != null) {result += " " + mnemonic;}
}
return result;
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_TWO_MAJ_TWO_MIN) {
return (a == '0' ? "" : intchar(a)) + intchar(b) + "." + intchar(c) + intchar(d);
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_LAST_THREE_DIGITS) {
return intchar(b) + "." + intchar(c) + "." + intchar(d);
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_THREE_ALPHANUMERIC_DIGITS) {
return decodeAlphaNumericChar(a) + "." + decodeAlphaNumericChar(b) + "." + decodeAlphaNumericChar(c);
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_SKIP_FIRST_ONE_MAJ_TWO_MIN) {
return intchar(b) + "." + intchar(c) + intchar(d);
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_AZ_KTORRENT_STYLE) {
// Either something like this:
// 1.2 RC 4 [where 3 == 'R')
// 1.2 Dev [where 3 == 'D')
// 1.2 [where 3 doesn't equal the above]
switch (c) {
case 'R':
return intchar(a) + "." + intchar(b) + " RC" + intchar(d);
case 'D':
return intchar(a) + "." + intchar(b) + " Dev";
default:
return intchar(a) + "." + intchar(b);
}
}
else if (version_scheme.equals("1.234")) {
return intchar(a) + "." + intchar(b) + intchar(c) + intchar(d);
}
else if (version_scheme.equals("1.2(34)")) {
return intchar(a) + "." + intchar(b) + "(" + intchar(c) + intchar(d) + ")";
}
else if (version_scheme.equals("1.2.34")) {
return intchar(a) + "." + intchar(b) + "." + intchar(c) + intchar(d);
}
else if (version_scheme.equals("v1234")) {
return "v" + intchar(a) + intchar(b) + intchar(c) + intchar(d);
}
else if (version_scheme.equals("1.2")) {
return intchar(a) + "." + intchar(b);
}
else if (version_scheme.equals("3.4")) {
return intchar(c) + "." + intchar(d);
}
else {
throw new RuntimeException("unknown AZ style version number scheme - " + version_scheme);
}
}
public static String getTwoByteThreePartVersion(byte b1, byte b2) {
String min_part = decodeNumericValueOfByte(b2, 2);
return joinAsDotted(decodeNumericValueOfByte(b1), min_part.substring(0, 1), min_part.substring(1, 2));
}
/**
* Look at the peer ID and just grab as many readable characters to form the version
* substring as possible.
*/
public static String extractReadableVersionSubstringFromPeerID(String peer_id) {
for (int i=0; i<peer_id.length(); i++) {
char c = peer_id.charAt(i);
// This is based on All Peers peer ID at the time of writing, e.g:
// AP0.70rc30->>...
if (Character.isLetter(c)) continue;
if (Character.isDigit(c)) continue;
if (c == '.') continue;
// Must be delimiter character.
return peer_id.substring(0, i);
}
return peer_id;
}
public static String decodeCustomVersionNumber(String version_data, String version_scheme) {
if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_BLOCK) {
return version_data;
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_DOTTED_BLOCK ||
version_scheme == BTPeerIDByteDecoderDefinitions.VER_BYTE_BLOCK_DOTTED_CHAR) {
int inc_size = (version_scheme == BTPeerIDByteDecoderDefinitions.VER_DOTTED_BLOCK) ? 2 : 1;
String result = version_data.substring(0, 1);
for (int i=0+inc_size; i<version_data.length(); i+=inc_size) {
result = joinAsDotted(result, String.valueOf(version_data.charAt(i)));
}
return result;
}
else if (version_scheme == BTPeerIDByteDecoderDefinitions.VER_BITS_ON_WHEELS) {
if (version_data.equals("A0C")) {return "1.0.6";}
else if (version_data.equals("A0B")) {return "1.0.5";}
throw new RuntimeException("Unknown BitsOnWheels version number - " + version_data);
}
else {
throw new RuntimeException("unknown custom version number scheme - " + version_scheme);
}
}
private static String join(String a, String b) {
if (a == null) {return null;}
if (b == null) {return null;}
return a + b;
}
private static String joinAsDotted(String a, String b) {
if (a == null) {return null;}
if (b == null) {return null;}
return a + "." + b;
}
private static String joinAsDotted(String a, String b, String c) {
if (a == null) {return null;}
if (b == null) {return null;}
if (c == null) {return null;}
return a + "." + b + "." + c;
}
}
|