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);
+ }
+ }
+}