tracker

Change-Id: I8f8ac81f9c4d7c7650cd64d2dade701dc6c11dce
diff --git a/ttorrent-master/bencoding/pom.xml b/ttorrent-master/bencoding/pom.xml
new file mode 100644
index 0000000..95e628a
--- /dev/null
+++ b/ttorrent-master/bencoding/pom.xml
@@ -0,0 +1,17 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>com.turn</groupId>
+        <artifactId>ttorrent</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <name>ttorrent/bencoding</name>
+    <url>http://turn.github.com/ttorrent/</url>
+    <artifactId>ttorrent-bencoding</artifactId>
+    <version>1.3.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+</project>
\ No newline at end of file
diff --git a/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java
new file mode 100644
index 0000000..8a2b3ab
--- /dev/null
+++ b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BDecoder.java
@@ -0,0 +1,320 @@
+/**
+ * Copyright (C) 2011-2012 Turn, Inc.
+ *
+ * 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 com.turn.ttorrent.bcodec;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * B-encoding decoder.
+ *
+ * <p>
+ * A b-encoded byte stream can represent byte arrays, numbers, lists and maps
+ * (dictionaries). This class implements a decoder of such streams into
+ * {@link BEValue}s.
+ * </p>
+ *
+ * <p>
+ * Inspired by Snark's implementation.
+ * </p>
+ *
+ * @author mpetazzoni
+ * @see <a href="http://en.wikipedia.org/wiki/Bencode">B-encoding specification</a>
+ */
+public class BDecoder {
+
+  // The InputStream to BDecode.
+  private final InputStream in;
+
+  // The last indicator read.
+  // Zero if unknown.
+  // '0'..'9' indicates a byte[].
+  // 'i' indicates an Number.
+  // 'l' indicates a List.
+  // 'd' indicates a Map.
+  // 'e' indicates end of Number, List or Map (only used internally).
+  // -1 indicates end of stream.
+  // Call getNextIndicator to get the current value (will never return zero).
+  private int indicator = 0;
+
+  /**
+   * Initializes a new BDecoder.
+   *
+   * <p>
+   * Nothing is read from the given <code>InputStream</code> yet.
+   * </p>
+   *
+   * @param in The input stream to read from.
+   */
+  public BDecoder(InputStream in) {
+    this.in = in;
+  }
+
+  /**
+   * Decode a B-encoded stream.
+   *
+   * <p>
+   * Automatically instantiates a new BDecoder for the provided input stream
+   * and decodes its root member.
+   * </p>
+   *
+   * @param in The input stream to read from.
+   */
+  public static BEValue bdecode(InputStream in) throws IOException {
+    return new BDecoder(in).bdecode();
+  }
+
+  /**
+   * Decode a B-encoded byte buffer.
+   *
+   * <p>
+   * Automatically instantiates a new BDecoder for the provided buffer and
+   * decodes its root member.
+   * </p>
+   *
+   * @param data The {@link ByteBuffer} to read from.
+   */
+  public static BEValue bdecode(ByteBuffer data) throws IOException {
+    return BDecoder.bdecode(new ByteArrayInputStream(data.array()));
+  }
+
+  /**
+   * Returns what the next b-encoded object will be on the stream or -1
+   * when the end of stream has been reached.
+   *
+   * <p>
+   * Can return something unexpected (not '0' .. '9', 'i', 'l' or 'd') when
+   * the stream isn't b-encoded.
+   * </p>
+   *
+   * This might or might not read one extra byte from the stream.
+   */
+  private int getNextIndicator() throws IOException {
+    if (this.indicator == 0) {
+      this.indicator = in.read();
+    }
+    return this.indicator;
+  }
+
+  /**
+   * Gets the next indicator and returns either null when the stream
+   * has ended or b-decodes the rest of the stream and returns the
+   * appropriate BEValue encoded object.
+   */
+  public BEValue bdecode() throws IOException {
+    if (this.getNextIndicator() == -1)
+      return null;
+
+    if (this.indicator >= '0' && this.indicator <= '9')
+      return this.bdecodeBytes();
+    else if (this.indicator == 'i')
+      return this.bdecodeNumber();
+    else if (this.indicator == 'l')
+      return this.bdecodeList();
+    else if (this.indicator == 'd')
+      return this.bdecodeMap();
+    else
+      throw new InvalidBEncodingException
+              ("Unknown indicator '" + this.indicator + "'");
+  }
+
+  /**
+   * Returns the next b-encoded value on the stream and makes sure it is a
+   * byte array.
+   *
+   * @throws InvalidBEncodingException If it is not a b-encoded byte array.
+   */
+  public BEValue bdecodeBytes() throws IOException {
+    int c = this.getNextIndicator();
+    int num = c - '0';
+    if (num < 0 || num > 9)
+      throw new InvalidBEncodingException("Number expected, not '"
+              + (char) c + "'");
+    this.indicator = 0;
+
+    c = this.read();
+    int i = c - '0';
+    while (i >= 0 && i <= 9) {
+      // This can overflow!
+      num = num * 10 + i;
+      c = this.read();
+      i = c - '0';
+    }
+
+    if (c != ':') {
+      throw new InvalidBEncodingException("Colon expected, not '" +
+              (char) c + "'");
+    }
+
+    return new BEValue(read(num));
+  }
+
+  /**
+   * Returns the next b-encoded value on the stream and makes sure it is a
+   * number.
+   *
+   * @throws InvalidBEncodingException If it is not a number.
+   */
+  public BEValue bdecodeNumber() throws IOException {
+    int c = this.getNextIndicator();
+    if (c != 'i') {
+      throw new InvalidBEncodingException("Expected 'i', not '" +
+              (char) c + "'");
+    }
+    this.indicator = 0;
+
+    c = this.read();
+    if (c == '0') {
+      c = this.read();
+      if (c == 'e')
+        return new BEValue(BigInteger.ZERO);
+      else
+        throw new InvalidBEncodingException("'e' expected after zero," +
+                " not '" + (char) c + "'");
+    }
+
+    // We don't support more the 255 char big integers
+    char[] chars = new char[256];
+    int off = 0;
+
+    if (c == '-') {
+      c = this.read();
+      if (c == '0')
+        throw new InvalidBEncodingException("Negative zero not allowed");
+      chars[off] = '-';
+      off++;
+    }
+
+    if (c < '1' || c > '9')
+      throw new InvalidBEncodingException("Invalid Integer start '"
+              + (char) c + "'");
+    chars[off] = (char) c;
+    off++;
+
+    c = this.read();
+    int i = c - '0';
+    while (i >= 0 && i <= 9) {
+      chars[off] = (char) c;
+      off++;
+      c = read();
+      i = c - '0';
+    }
+
+    if (c != 'e')
+      throw new InvalidBEncodingException("Integer should end with 'e'");
+
+    String s = new String(chars, 0, off);
+    return new BEValue(new BigInteger(s));
+  }
+
+  /**
+   * Returns the next b-encoded value on the stream and makes sure it is a
+   * list.
+   *
+   * @throws InvalidBEncodingException If it is not a list.
+   */
+  public BEValue bdecodeList() throws IOException {
+    int c = this.getNextIndicator();
+    if (c != 'l') {
+      throw new InvalidBEncodingException("Expected 'l', not '" +
+              (char) c + "'");
+    }
+    this.indicator = 0;
+
+    List<BEValue> result = new ArrayList<BEValue>();
+    c = this.getNextIndicator();
+    while (c != 'e') {
+      result.add(this.bdecode());
+      c = this.getNextIndicator();
+    }
+    this.indicator = 0;
+
+    return new BEValue(result);
+  }
+
+  /**
+   * Returns the next b-encoded value on the stream and makes sure it is a
+   * map (dictionary).
+   *
+   * @throws InvalidBEncodingException If it is not a map.
+   */
+  public BEValue bdecodeMap() throws IOException {
+    int c = this.getNextIndicator();
+    if (c != 'd') {
+      throw new InvalidBEncodingException("Expected 'd', not '" +
+              (char) c + "'");
+    }
+    this.indicator = 0;
+
+    Map<String, BEValue> result = new HashMap<String, BEValue>();
+    c = this.getNextIndicator();
+    while (c != 'e') {
+      // Dictionary keys are always strings.
+      String key = this.bdecode().getString();
+
+      BEValue value = this.bdecode();
+      result.put(key, value);
+
+      c = this.getNextIndicator();
+    }
+    this.indicator = 0;
+
+    return new BEValue(result);
+  }
+
+  /**
+   * Returns the next byte read from the InputStream (as int).
+   *
+   * @throws EOFException If InputStream.read() returned -1.
+   */
+  private int read() throws IOException {
+    int c = this.in.read();
+    if (c == -1)
+      throw new EOFException();
+    return c;
+  }
+
+  /**
+   * Returns a byte[] containing length valid bytes starting at offset zero.
+   *
+   * @throws EOFException If InputStream.read() returned -1 before all
+   *                      requested bytes could be read.  Note that the byte[] returned might be
+   *                      bigger then requested but will only contain length valid bytes.  The
+   *                      returned byte[] will be reused when this method is called again.
+   */
+  private byte[] read(int length) throws IOException {
+    byte[] result = new byte[length];
+
+    int read = 0;
+    while (read < length) {
+      int i = this.in.read(result, read, length - read);
+      if (i == -1)
+        throw new EOFException();
+      read += i;
+    }
+
+    return result;
+  }
+}
diff --git a/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEValue.java b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEValue.java
new file mode 100644
index 0000000..875bcf1
--- /dev/null
+++ b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEValue.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (C) 2011-2012 Turn, Inc.
+ *
+ * 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 com.turn.ttorrent.bcodec;
+
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A type-agnostic container for B-encoded values.
+ *
+ * b加密方法,bt种子文件中存储数据的规则
+ * @author mpetazzoni
+ */
+public class BEValue {
+
+  /**
+   * The B-encoded value can be a byte array, a Number, a List or a Map.
+   * Lists and Maps contains BEValues too.
+   */
+  private final Object value;
+
+  public BEValue(byte[] value) {
+    this.value = value;
+  }
+
+  public BEValue(String value) throws UnsupportedEncodingException {
+    this.value = value.getBytes("UTF-8");
+  }
+
+  public BEValue(String value, String enc)
+          throws UnsupportedEncodingException {
+    this.value = value.getBytes(enc);
+  }
+
+  public BEValue(int value) {
+    this.value = new Integer(value);
+  }
+
+  public BEValue(long value) {
+    this.value = new Long(value);
+  }
+
+  public BEValue(Number value) {
+    this.value = value;
+  }
+
+  public BEValue(List<BEValue> value) {
+    this.value = value;
+  }
+
+  public BEValue(Map<String, BEValue> value) {
+    this.value = value;
+  }
+
+  public Object getValue() {
+    return this.value;
+  }
+
+  /**
+   * Returns this BEValue as a String, interpreted as UTF-8.
+   *
+   * @throws InvalidBEncodingException If the value is not a byte[].
+   */
+  public String getString() throws InvalidBEncodingException {
+    return this.getString("UTF-8");
+  }
+
+  /**
+   * Returns this BEValue as a String, interpreted with the specified
+   * encoding.
+   *
+   * @param encoding The encoding to interpret the bytes as when converting
+   *                 them into a {@link String}.
+   * @throws InvalidBEncodingException If the value is not a byte[].
+   */
+  public String getString(String encoding) throws InvalidBEncodingException {
+    try {
+      return new String(this.getBytes(), encoding);
+    } catch (ClassCastException cce) {
+      throw new InvalidBEncodingException(cce.toString());
+    } catch (UnsupportedEncodingException uee) {
+      throw new InternalError(uee.toString());
+    }
+  }
+
+  /**
+   * Returns this BEValue as a byte[].
+   *
+   * @throws InvalidBEncodingException If the value is not a byte[].
+   */
+  public byte[] getBytes() throws InvalidBEncodingException {
+    try {
+      return (byte[]) this.value;
+    } catch (ClassCastException cce) {
+      throw new InvalidBEncodingException(cce.toString());
+    }
+  }
+
+  /**
+   * Returns this BEValue as a Number.
+   *
+   * @throws InvalidBEncodingException If the value is not a {@link Number}.
+   */
+  public Number getNumber() throws InvalidBEncodingException {
+    try {
+      return (Number) this.value;
+    } catch (ClassCastException cce) {
+      throw new InvalidBEncodingException(cce.toString());
+    }
+  }
+
+  /**
+   * Returns this BEValue as short.
+   *
+   * @throws InvalidBEncodingException If the value is not a {@link Number}.
+   */
+  public short getShort() throws InvalidBEncodingException {
+    return this.getNumber().shortValue();
+  }
+
+  /**
+   * Returns this BEValue as int.
+   *
+   * @throws InvalidBEncodingException If the value is not a {@link Number}.
+   */
+  public int getInt() throws InvalidBEncodingException {
+    return this.getNumber().intValue();
+  }
+
+  /**
+   * Returns this BEValue as long.
+   *
+   * @throws InvalidBEncodingException If the value is not a {@link Number}.
+   */
+  public long getLong() throws InvalidBEncodingException {
+    return this.getNumber().longValue();
+  }
+
+  /**
+   * Returns this BEValue as a List of BEValues.
+   *
+   * @throws InvalidBEncodingException If the value is not an
+   *                                   {@link ArrayList}.
+   */
+  @SuppressWarnings("unchecked")
+  public List<BEValue> getList() throws InvalidBEncodingException {
+    if (this.value instanceof ArrayList) {
+      return (ArrayList<BEValue>) this.value;
+    } else {
+      throw new InvalidBEncodingException("Excepted List<BEvalue> !");
+    }
+  }
+
+  /**
+   * Returns this BEValue as a Map of String keys and BEValue values.
+   *
+   * @throws InvalidBEncodingException If the value is not a {@link HashMap}.
+   */
+  @SuppressWarnings("unchecked")
+  public Map<String, BEValue> getMap() throws InvalidBEncodingException {
+    if (this.value instanceof HashMap) {
+      return (Map<String, BEValue>) this.value;
+    } else {
+      throw new InvalidBEncodingException("Expected Map<String, BEValue> !");
+    }
+  }
+}
diff --git a/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java
new file mode 100644
index 0000000..0619720
--- /dev/null
+++ b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/BEncoder.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright (C) 2011-2012 Turn, Inc.
+ *
+ * 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 com.turn.ttorrent.bcodec;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.*;
+
+/**
+ * B-encoding encoder.
+ *
+ * <p>
+ * This class provides utility methods to encode objects and
+ * {@link BEValue}s to B-encoding into a provided output stream.
+ * </p>
+ *
+ * <p>
+ * Inspired by Snark's implementation.
+ * </p>
+ *
+ * @author mpetazzoni
+ * @see <a href="http://en.wikipedia.org/wiki/Bencode">B-encoding specification</a>
+ */
+public class BEncoder {
+
+  @SuppressWarnings("unchecked")
+  public static void bencode(Object o, OutputStream out)
+          throws IOException, IllegalArgumentException {
+    if (o instanceof BEValue) {
+      o = ((BEValue) o).getValue();
+    }
+
+    if (o instanceof String) {
+      bencode((String) o, out);
+    } else if (o instanceof byte[]) {
+      bencode((byte[]) o, out);
+    } else if (o instanceof Number) {
+      bencode((Number) o, out);
+    } else if (o instanceof List) {
+      bencode((List<BEValue>) o, out);
+    } else if (o instanceof Map) {
+      bencode((Map<String, BEValue>) o, out);
+    } else {
+      throw new IllegalArgumentException("Cannot bencode: " +
+              o.getClass());
+    }
+  }
+
+  public static void bencode(String s, OutputStream out) throws IOException {
+    byte[] bs = s.getBytes("UTF-8");
+    bencode(bs, out);
+  }
+
+  public static void bencode(Number n, OutputStream out) throws IOException {
+    out.write('i');
+    String s = n.toString();
+    out.write(s.getBytes("UTF-8"));
+    out.write('e');
+  }
+
+  public static void bencode(List<BEValue> l, OutputStream out)
+          throws IOException {
+    out.write('l');
+    for (BEValue value : l) {
+      bencode(value, out);
+    }
+    out.write('e');
+  }
+
+  public static void bencode(byte[] bs, OutputStream out) throws IOException {
+    String l = Integer.toString(bs.length);
+    out.write(l.getBytes("UTF-8"));
+    out.write(':');
+    out.write(bs);
+  }
+
+  public static void bencode(Map<String, BEValue> m, OutputStream out)
+          throws IOException {
+    out.write('d');
+
+    // Keys must be sorted.
+    Set<String> s = m.keySet();
+    List<String> l = new ArrayList<String>(s);
+    Collections.sort(l);
+
+    for (String key : l) {
+      Object value = m.get(key);
+      bencode(key, out);
+      bencode(value, out);
+    }
+
+    out.write('e');
+  }
+
+  public static ByteBuffer bencode(Map<String, BEValue> m)
+          throws IOException {
+    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+    BEncoder.bencode(m, baos);
+    baos.close();
+    return ByteBuffer.wrap(baos.toByteArray());
+  }
+}
diff --git a/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java
new file mode 100644
index 0000000..8a0d1dd
--- /dev/null
+++ b/ttorrent-master/bencoding/src/main/java/com/turn/ttorrent/bcodec/InvalidBEncodingException.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (C) 2011-2012 Turn, Inc.
+ *
+ * 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 com.turn.ttorrent.bcodec;
+
+import java.io.IOException;
+
+
+/**
+ * Exception thrown when a B-encoded stream cannot be decoded.
+ *
+ * @author mpetazzoni
+ */
+public class InvalidBEncodingException extends IOException {
+
+  public static final long serialVersionUID = -1;
+
+  public InvalidBEncodingException(String message) {
+    super(message);
+  }
+}
diff --git a/ttorrent-master/bencoding/src/test/java/BDecoderTest.java b/ttorrent-master/bencoding/src/test/java/BDecoderTest.java
new file mode 100644
index 0000000..b48ef69
--- /dev/null
+++ b/ttorrent-master/bencoding/src/test/java/BDecoderTest.java
@@ -0,0 +1,56 @@
+import com.turn.ttorrent.bcodec.BDecoder;
+import com.turn.ttorrent.bcodec.InvalidBEncodingException;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+public class BDecoderTest {
+
+  @Test
+  public void testDecodeNumbers() throws IOException {
+
+    testNumber(0);
+    testNumber(1);
+    testNumber(Integer.MAX_VALUE);
+    testNumber(1234567);
+    testNumber(-1);
+    testNumber(-100);
+    testNumber(Integer.MIN_VALUE);
+    testNumber(Long.MAX_VALUE);
+    testNumber(Long.MIN_VALUE);
+
+    //by specification number with lead zero it's incorrect value
+    testBadNumber("00");
+    testBadNumber("01234");
+    testBadNumber("000");
+    testBadNumber("0001");
+
+  }
+
+  private void testBadNumber(String number) throws IOException {
+    try {
+      BDecoder.bdecode(numberToBEPBytes(number));
+    } catch (InvalidBEncodingException e) {
+      return;
+    }
+    fail("Value " + number + " is incorrect by BEP specification but is was parsed correctly");
+  }
+
+  private void testNumber(long value) throws IOException {
+    assertEquals(BDecoder.bdecode(numberToBEPBytes(value)).getLong(), value);
+  }
+
+  private ByteBuffer numberToBEPBytes(long value) throws UnsupportedEncodingException {
+    return ByteBuffer.wrap(("i" + value + "e").getBytes("ASCII"));
+  }
+
+  private ByteBuffer numberToBEPBytes(String value) throws UnsupportedEncodingException {
+    return ByteBuffer.wrap(("i" + value + "e").getBytes("ASCII"));
+  }
+
+}