tracker

Change-Id: I8f8ac81f9c4d7c7650cd64d2dade701dc6c11dce
diff --git a/ttorrent-master/cli/pom.xml b/ttorrent-master/cli/pom.xml
new file mode 100644
index 0000000..0e0b6c9
--- /dev/null
+++ b/ttorrent-master/cli/pom.xml
@@ -0,0 +1,71 @@
+<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>
+	</parent>
+
+	<name>Java BitTorrent library CLI</name>
+	<artifactId>ttorrent-cli</artifactId>
+	<packaging>jar</packaging>
+
+	<dependencies>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-client</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.turn</groupId>
+            <artifactId>ttorrent-tracker</artifactId>
+            <version>1.3.0-SNAPSHOT</version>
+        </dependency>
+	</dependencies>
+
+	<build>
+		<defaultGoal>package</defaultGoal>
+
+		<plugins>
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-jar-plugin</artifactId>
+				<version>2.4</version>
+				<configuration>
+					<archive>
+						<manifest>
+							<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+						</manifest>
+					</archive>
+					<includes>
+						<include>**</include>
+					</includes>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<artifactId>maven-shade-plugin</artifactId>
+				<version>2.1</version>
+				<executions>
+					<execution>
+						<phase>package</phase>
+						<goals>
+							<goal>shade</goal>
+						</goals>
+						<configuration>
+							<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-shaded.jar</outputFile>
+							<transformers>
+								<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+									<manifestEntries>
+										<Main-Class>com.turn.ttorrent.cli.ClientMain</Main-Class>
+									</manifestEntries>
+								</transformer>
+							</transformers>
+						</configuration>
+					</execution>
+				</executions>
+			</plugin>
+		</plugins>
+	</build>
+</project>
diff --git a/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/ClientMain.java b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/ClientMain.java
new file mode 100644
index 0000000..e7a68d1
--- /dev/null
+++ b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/ClientMain.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (C) 2011-2013 Turn, Inc.
+ * <p>
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.cli;
+
+import com.turn.ttorrent.client.CommunicationManager;
+import com.turn.ttorrent.client.SimpleClient;
+import jargs.gnu.CmdLineParser;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.PatternLayout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.net.*;
+import java.nio.channels.UnsupportedAddressTypeException;
+import java.util.Enumeration;
+
+/**
+ * Command-line entry-point for starting a {@link CommunicationManager}
+ */
+public class ClientMain {
+
+  private static final Logger logger =
+          LoggerFactory.getLogger(ClientMain.class);
+
+  /**
+   * Default data output directory.
+   */
+  private static final String DEFAULT_OUTPUT_DIRECTORY = "/tmp";
+
+  /**
+   * Returns a usable {@link Inet4Address} for the given interface name.
+   *
+   * <p>
+   * If an interface name is given, return the first usable IPv4 address for
+   * that interface. If no interface name is given or if that interface
+   * doesn't have an IPv4 address, return's localhost address (if IPv4).
+   * </p>
+   *
+   * <p>
+   * It is understood this makes the client IPv4 only, but it is important to
+   * remember that most BitTorrent extensions (like compact peer lists from
+   * trackers and UDP tracker support) are IPv4-only anyway.
+   * </p>
+   *
+   * @param iface The network interface name.
+   * @return A usable IPv4 address as a {@link Inet4Address}.
+   * @throws UnsupportedAddressTypeException If no IPv4 address was available
+   * to bind on.
+   */
+  private static Inet4Address getIPv4Address(String iface)
+          throws SocketException, UnsupportedAddressTypeException,
+          UnknownHostException {
+    if (iface != null) {
+      Enumeration<InetAddress> addresses =
+              NetworkInterface.getByName(iface).getInetAddresses();
+      while (addresses.hasMoreElements()) {
+        InetAddress addr = addresses.nextElement();
+        if (addr instanceof Inet4Address) {
+          return (Inet4Address) addr;
+        }
+      }
+    }
+
+    InetAddress localhost = InetAddress.getLocalHost();
+    if (localhost instanceof Inet4Address) {
+      return (Inet4Address) localhost;
+    }
+
+    throw new UnsupportedAddressTypeException();
+  }
+
+  /**
+   * Display program usage on the given {@link PrintStream}.
+   */
+  private static void usage(PrintStream s) {
+    s.println("usage: Client [options] <torrent>");
+    s.println();
+    s.println("Available options:");
+    s.println("  -h,--help                  Show this help and exit.");
+    s.println("  -o,--output DIR            Read/write data to directory DIR.");
+    s.println("  -i,--iface IFACE           Bind to interface IFACE.");
+    s.println("  -s,--seed SECONDS          Time to seed after downloading (default: infinitely).");
+    s.println("  -d,--max-download KB/SEC   Max download rate (default: unlimited).");
+    s.println("  -u,--max-upload KB/SEC     Max upload rate (default: unlimited).");
+    s.println();
+  }
+
+  /**
+   * Main client entry point for stand-alone operation.
+   */
+  public static void main(String[] args) {
+    BasicConfigurator.configure(new ConsoleAppender(
+            new PatternLayout("%d [%-25t] %-5p: %m%n")));
+
+    CmdLineParser parser = new CmdLineParser();
+    CmdLineParser.Option help = parser.addBooleanOption('h', "help");
+    CmdLineParser.Option output = parser.addStringOption('o', "output");
+    CmdLineParser.Option iface = parser.addStringOption('i', "iface");
+    CmdLineParser.Option seedTime = parser.addIntegerOption('s', "seed");
+    CmdLineParser.Option maxUpload = parser.addDoubleOption('u', "max-upload");
+    CmdLineParser.Option maxDownload = parser.addDoubleOption('d', "max-download");
+
+    try {
+      parser.parse(args);
+    } catch (CmdLineParser.OptionException oe) {
+      System.err.println(oe.getMessage());
+      usage(System.err);
+      System.exit(1);
+    }
+
+    // Display help and exit if requested
+    if (Boolean.TRUE.equals((Boolean) parser.getOptionValue(help))) {
+      usage(System.out);
+      System.exit(0);
+    }
+
+    String outputValue = (String) parser.getOptionValue(output,
+            DEFAULT_OUTPUT_DIRECTORY);
+    String ifaceValue = (String) parser.getOptionValue(iface);
+    int seedTimeValue = (Integer) parser.getOptionValue(seedTime, -1);
+
+    String[] otherArgs = parser.getRemainingArgs();
+    if (otherArgs.length != 1) {
+      usage(System.err);
+      System.exit(1);
+    }
+
+    SimpleClient client = new SimpleClient();
+    try {
+      Inet4Address iPv4Address = getIPv4Address(ifaceValue);
+      File torrentFile = new File(otherArgs[0]);
+      File outputFile = new File(outputValue);
+
+      client.downloadTorrent(
+              torrentFile.getAbsolutePath(),
+              outputFile.getAbsolutePath(),
+              iPv4Address);
+      if (seedTimeValue > 0) {
+        Thread.sleep(seedTimeValue * 1000);
+      }
+
+    } catch (Exception e) {
+      logger.error("Fatal error: {}", e.getMessage(), e);
+      System.exit(2);
+    } finally {
+      client.stop();
+    }
+  }
+}
diff --git a/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TorrentMain.java b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TorrentMain.java
new file mode 100644
index 0000000..4ab6353
--- /dev/null
+++ b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TorrentMain.java
@@ -0,0 +1,190 @@
+/*
+  Copyright (C) 2011-2013 Turn, Inc.
+  <p>
+  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
+  <p>
+  http://www.apache.org/licenses/LICENSE-2.0
+  <p>
+  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.cli;
+
+
+import com.turn.ttorrent.common.TorrentCreator;
+import com.turn.ttorrent.common.TorrentMetadata;
+import com.turn.ttorrent.common.TorrentParser;
+import com.turn.ttorrent.common.TorrentSerializer;
+import jargs.gnu.CmdLineParser;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.filefilter.TrueFileFilter;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.PatternLayout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Command-line entry-point for reading and writing {@link TorrentMetadata}.
+ */
+public class TorrentMain {
+
+  private static final Logger logger =
+          LoggerFactory.getLogger(TorrentMain.class);
+
+  /**
+   * Display program usage on the given {@link PrintStream}.
+   */
+  private static void usage(PrintStream s) {
+    usage(s, null);
+  }
+
+  /**
+   * Display a message and program usage on the given {@link PrintStream}.
+   */
+  private static void usage(PrintStream s, String msg) {
+    if (msg != null) {
+      s.println(msg);
+      s.println();
+    }
+
+    s.println("usage: Torrent [options] [file|directory]");
+    s.println();
+    s.println("Available options:");
+    s.println("  -h,--help             Show this help and exit.");
+    s.println("  -t,--torrent FILE     Use FILE to read/write torrent file.");
+    s.println();
+    s.println("  -c,--create           Create a new torrent file using " +
+            "the given announce URL and data.");
+    s.println("  -l,--length           Define the piece length for hashing data");
+    s.println("  -a,--announce         Tracker URL (can be repeated).");
+    s.println();
+  }
+
+  /**
+   * Torrent reader and creator.
+   *
+   * <p>
+   * You can use the {@code main()} function of this class to read or create
+   * torrent files. See usage for details.
+   * </p>
+   */
+  public static void main(String[] args) {
+    BasicConfigurator.configure(new ConsoleAppender(
+            new PatternLayout("%-5p: %m%n")));
+
+    CmdLineParser parser = new CmdLineParser();
+    CmdLineParser.Option help = parser.addBooleanOption('h', "help");
+    CmdLineParser.Option filename = parser.addStringOption('t', "torrent");
+    CmdLineParser.Option create = parser.addBooleanOption('c', "create");
+    CmdLineParser.Option pieceLength = parser.addIntegerOption('l', "length");
+    CmdLineParser.Option announce = parser.addStringOption('a', "announce");
+
+    try {
+      parser.parse(args);
+    } catch (CmdLineParser.OptionException oe) {
+      System.err.println(oe.getMessage());
+      usage(System.err);
+      System.exit(1);
+    }
+
+    // Display help and exit if requested
+    if (Boolean.TRUE.equals(parser.getOptionValue(help))) {
+      usage(System.out);
+      System.exit(0);
+    }
+
+    String filenameValue = (String) parser.getOptionValue(filename);
+    if (filenameValue == null) {
+      usage(System.err, "Torrent file must be provided!");
+      System.exit(1);
+    }
+
+    Integer pieceLengthVal = (Integer) parser.getOptionValue(pieceLength);
+    if (pieceLengthVal == null) {
+      pieceLengthVal = TorrentCreator.DEFAULT_PIECE_LENGTH;
+    } else {
+      pieceLengthVal = pieceLengthVal * 1024;
+    }
+    logger.info("Using piece length of {} bytes.", pieceLengthVal);
+
+    Boolean createFlag = (Boolean) parser.getOptionValue(create);
+
+    //For repeated announce urls
+    @SuppressWarnings("unchecked")
+    Vector<String> announceURLs = (Vector<String>) parser.getOptionValues(announce);
+
+    String[] otherArgs = parser.getRemainingArgs();
+
+    if (Boolean.TRUE.equals(createFlag) &&
+            (otherArgs.length != 1 || announceURLs.isEmpty())) {
+      usage(System.err, "Announce URL and a file or directory must be " +
+              "provided to create a torrent file!");
+      System.exit(1);
+    }
+
+    OutputStream fos = null;
+    try {
+      if (Boolean.TRUE.equals(createFlag)) {
+        fos = new FileOutputStream(filenameValue);
+
+        //Process the announce URLs into URIs
+        List<URI> announceURIs = new ArrayList<URI>();
+        for (String url : announceURLs) {
+          announceURIs.add(new URI(url));
+        }
+
+        //Create the announce-list as a list of lists of URIs
+        //Assume all the URI's are first tier trackers
+        List<List<URI>> announceList = new ArrayList<List<URI>>();
+        announceList.add(announceURIs);
+
+        File source = new File(otherArgs[0]);
+        if (!source.exists() || !source.canRead()) {
+          throw new IllegalArgumentException(
+                  "Cannot access source file or directory " +
+                          source.getName());
+        }
+
+        String creator = String.format("%s (ttorrent)",
+                System.getProperty("user.name"));
+
+        TorrentMetadata torrent;
+        if (source.isDirectory()) {
+          List<File> files = new ArrayList<File>(FileUtils.listFiles(source, TrueFileFilter.TRUE, TrueFileFilter.TRUE));
+          Collections.sort(files);
+          torrent = TorrentCreator.create(source, files, announceList.get(0).get(0), announceList, creator, pieceLengthVal);
+        } else {
+          torrent = TorrentCreator.create(source, null, announceList.get(0).get(0), announceList, creator, pieceLengthVal);
+        }
+
+        fos.write(new TorrentSerializer().serialize(torrent));
+      } else {
+        new TorrentParser().parseFromFile(new File(filenameValue));
+      }
+    } catch (Exception e) {
+      logger.error("{}", e.getMessage(), e);
+      System.exit(2);
+    } finally {
+      if (fos != System.out) {
+        IOUtils.closeQuietly(fos);
+      }
+    }
+  }
+}
diff --git a/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TrackerMain.java b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TrackerMain.java
new file mode 100644
index 0000000..0556e94
--- /dev/null
+++ b/ttorrent-master/cli/src/main/java/com/turn/ttorrent/cli/TrackerMain.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (C) 2011-2013 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.cli;
+
+import com.turn.ttorrent.tracker.TrackedTorrent;
+import com.turn.ttorrent.tracker.Tracker;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+
+import jargs.gnu.CmdLineParser;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.ConsoleAppender;
+import org.apache.log4j.PatternLayout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Command-line entry-point for starting a {@link Tracker}
+ */
+public class TrackerMain {
+
+	private static final Logger logger =
+		LoggerFactory.getLogger(TrackerMain.class);
+
+	/**
+	 * Display program usage on the given {@link PrintStream}.
+	 */
+	private static void usage(PrintStream s) {
+		s.println("usage: Tracker [options] [directory]");
+		s.println();
+		s.println("Available options:");
+		s.println("  -h,--help             Show this help and exit.");
+		s.println("  -p,--port PORT        Bind to port PORT.");
+		s.println();
+	}
+
+	/**
+	 * Main function to start a tracker.
+	 */
+	public static void main(String[] args) {
+		BasicConfigurator.configure(new ConsoleAppender(
+			new PatternLayout("%d [%-25t] %-5p: %m%n")));
+
+		CmdLineParser parser = new CmdLineParser();
+		CmdLineParser.Option help = parser.addBooleanOption('h', "help");
+		CmdLineParser.Option port = parser.addIntegerOption('p', "port");
+
+		try {
+			parser.parse(args);
+		} catch (CmdLineParser.OptionException oe) {
+			System.err.println(oe.getMessage());
+			usage(System.err);
+			System.exit(1);
+		}
+
+		// Display help and exit if requested
+		if (Boolean.TRUE.equals((Boolean)parser.getOptionValue(help))) {
+			usage(System.out);
+			System.exit(0);
+		}
+
+		Integer portValue = (Integer)parser.getOptionValue(port,
+			Integer.valueOf(Tracker.DEFAULT_TRACKER_PORT));
+
+		String[] otherArgs = parser.getRemainingArgs();
+
+		if (otherArgs.length > 1) {
+			usage(System.err);
+			System.exit(1);
+		}
+
+		// Get directory from command-line argument or default to current
+		// directory
+		String directory = otherArgs.length > 0
+			? otherArgs[0]
+			: ".";
+
+		FilenameFilter filter = new FilenameFilter() {
+			@Override
+			public boolean accept(File dir, String name) {
+				return name.endsWith(".torrent");
+			}
+		};
+
+		try {
+			Tracker t = new Tracker(portValue);
+
+			File parent = new File(directory);
+			for (File f : parent.listFiles(filter)) {
+				logger.info("Loading torrent from " + f.getName());
+				t.announce(TrackedTorrent.load(f));
+			}
+
+			logger.info("Starting tracker with {} announced torrents...",
+				t.getTrackedTorrents().size());
+			t.start(true);
+		} catch (Exception e) {
+			logger.error("{}", e.getMessage(), e);
+			System.exit(2);
+		}
+	}
+}