MoparScape/MoparScape/src/main/java/org/moparscape/res/MakeTorrent.java

332 lines
14 KiB
Java

/*
* Copyright (C) 2009-2013 moparisthebest
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Official forums are http://www.moparscape.org/smf/
* Email me at admin@moparisthebest.com.
*/
package org.moparscape.res;
import org.moparscape.Debug;
import org.moparscape.classloader.CRCClassLoader;
import org.moparscape.res.impl.Downloader;
import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.net.URLEncoder;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
public class MakeTorrent {
private long crc = 0;
private File torrentFile = null;
private File sharedFile = null;
//private String[][] announceList = new String[][]{new String[]{"udp://tracker.moparisthebest.com:2710/announce"}, new String[]{"http://tracker.moparisthebest.com/announce"}, new String[]{"udp://exodus.desync.com:6969", "http://exodus.desync.com:6969/announce"}};
//private String[][] announceList = new String[][]{new String[]{"udp://tracker.moparisthebest.com:2710/announce"}};
private String[][] announceList = new String[][]{new String[]{"http://tracker.moparisthebest.com:2710/announce"}, new String[]{"http://tracker.moparisthebest.com/announce"}, new String[]{"udp://exodus.desync.com:6969", "http://exodus.desync.com:6969/announce"}};
private String[] urlList = null;
private String sha1InfoHash = null;
private String base32InfoHash = null;
private String[] magnetLinks = null;
public MakeTorrent() {
this((Component) null);
}
public MakeTorrent(Component parent) {
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (fc.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION) {
System.out.println("Make Torrent command cancelled by user.");
return;
}
String[] trackers = askForList(parent, "Enter a tracker URL (http/https/udp), otherwise a default list will be used.");
String[][] announceList = null;
if (trackers != null) {
announceList = new String[trackers.length][1];
for (int i = 0; i < trackers.length; ++i)
announceList[i][0] = trackers[i];
}
this.setup(fc.getSelectedFile(), announceList, askForList(parent, "Enter a webseed URL (http:// or https://)"));
}
private String[] askForList(Component parent, String question) {
question += " (Press Cancel if done)";
java.util.List<String> list = new ArrayList<String>();
String newItem = null;
do {
if (newItem != null)
list.add(newItem);
newItem = JOptionPane.showInputDialog(parent, question);
} while (newItem != null && !newItem.isEmpty());
return list.size() == 0 ? null : list.toArray(new String[list.size()]);
}
public MakeTorrent(String sharedFile) {
this(sharedFile, (String[]) null);
}
public MakeTorrent(String sharedFile, String... urlList) {
this(new File(sharedFile), urlList);
}
public MakeTorrent(File sharedFile) {
this(sharedFile, (String[]) null);
}
public MakeTorrent(File sharedFile, String... urlList) {
this(sharedFile, null, urlList);
//this(sharedFile, new String[][]{new String[]{"udp://tracker.moparisthebest.com:2710/announce", "http://tracker.moparisthebest.com/announce"}}, urlList);
}
public MakeTorrent(File sharedFile, String[][] announceList, String[] urlList) {
this.setup(sharedFile, announceList, urlList);
}
private void setup(File sharedFile, String[][] announceList, String[] urlList) {
this.sharedFile = sharedFile;
if (announceList != null)
this.announceList = announceList;
this.urlList = urlList;
String absolutePath = sharedFile.getAbsolutePath();
System.out.println("Creating torrent from: " + absolutePath);
torrentFile = new File(absolutePath + ".torrent");
try {
this.createTorrent();
} catch (Exception e) {
System.out.println("Creating torrent failed: " + e.getMessage());
Debug.debug(e);
return;
}
// now calculate and print info about the torrent, like the CRC
try {
if (absolutePath.toLowerCase().endsWith(".jar") || absolutePath.toLowerCase().endsWith(".jar.gz")) {
System.out.println("Detected jar file, calculating CRC with CRCClassLoader");
crc = new CRCClassLoader(absolutePath).getCRC();
} else if (Downloader.supportsExtraction(absolutePath)) {
System.out.println("Detected file to extract, calculating CRC of extracted files only.");
crc = Downloader.crcExtractFile(absolutePath);
// debugging stuff below, it should (and currently does) produce the same CRC
/*
System.out.println("initial crc: "+crc);
File tmpDir = Downloader.createTempDir();
if(Downloader.extractFile(absolutePath, tmpDir.getAbsolutePath()))
crc = crcFile(tmpDir.getAbsolutePath());
System.out.println("later crc: "+crc + " temp dir: "+tmpDir.getAbsolutePath());
*/
}
// if crc is still 0, just calculate the CRC of the sharedFile itself
if (crc == 0)
crc = ChecksumInfo.crcFile(absolutePath);
} catch (Exception e) {
System.out.println("Calculating CRC for torrent failed: " + e.getMessage());
Debug.debug(e);
return;
}
System.out.println("info hash of torrent: " + sha1InfoHash);
//System.out.println("info hash of torrent: " + new Base32().toSha1(base32InfoHash));
System.out.println("CRC of torrent: " + crc);
String magTrackers = "";
if (this.announceList != null)
for (String[] trackerList : this.announceList)
for (String tracker : trackerList)
magTrackers += "&tr=" + urlEncode(tracker);
String[][] magnetTypes = new String[][]{new String[]{"btih", base32InfoHash}, new String[]{"sha1", sha1InfoHash}};
this.magnetLinks = new String[magnetTypes.length];
for (int i = 0; i < this.magnetLinks.length; ++i) {
this.magnetLinks[i] = String.format("magnet:?xt=urn:%s:%s&dn=%s%s", magnetTypes[i][0], magnetTypes[i][1], urlEncode(sharedFile.getName()), magTrackers);
System.out.println("magnet link: " + this.magnetLinks[i]);
}
for(String url: urlList)
System.out.println("webseed: "+url);
}
public String urlEncode(String url) {
try {
return URLEncoder.encode(url, "UTF-8");
} catch (Exception e) {
return url;
}
}
@SuppressWarnings("unchecked")
private void bencode(Object o, OutputStream out) throws IOException {
try {
if (o instanceof String)
bencode((String) o, out);
else if (o instanceof Map)
bencode((Map<String, Object>) o, out);
else if (o instanceof Number)
bencode(((Number) o).longValue(), out);
else if (o instanceof byte[])
bencode((byte[]) o, out);
else if (o instanceof Object[])
bencode((Object[]) o, out);
else
throw new Error("Unencodable type");
} catch (ClassCastException e) {
Debug.debug(e);
throw new Error("Unencodable type (ClassCastException)");
}
}
private void bencode(Object[] list, OutputStream out) throws IOException {
out.write('l');
for (Object str : list)
bencode(str, out);
out.write('e');
}
private void bencode(long value, OutputStream out) throws IOException {
out.write('i');
out.write(Long.toString(value).getBytes("US-ASCII"));
out.write('e');
}
private void bencode(String str, OutputStream out) throws IOException {
bencode(str.getBytes("UTF-8"), out);
}
private void bencode(byte[] bytes, OutputStream out) throws IOException {
out.write(Integer.toString(bytes.length).getBytes("US-ASCII"));
out.write(':');
out.write(bytes);
}
private void bencode(Map<String, Object> map, OutputStream out) throws IOException {
// Sort the map. A generic encoder should sort by key bytes
SortedMap<String, Object> sortedMap = new TreeMap<String, Object>(map);
out.write('d');
for (String key : sortedMap.keySet()) {
bencode(key, out);
bencode(sortedMap.get(key), out);
}
out.write('e');
}
private byte[] hashPieces(File file, int pieceLength) throws IOException {
MessageDigest sha1;
try {
sha1 = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
throw new Error("SHA1 not supported");
}
InputStream in = new FileInputStream(file);
ByteArrayOutputStream pieces = new ByteArrayOutputStream();
byte[] bytes = new byte[pieceLength];
int pieceByteCount = 0, readCount = in.read(bytes, 0, pieceLength);
while (readCount != -1) {
pieceByteCount += readCount;
sha1.update(bytes, 0, readCount);
if (pieceByteCount == pieceLength) {
pieceByteCount = 0;
pieces.write(sha1.digest());
}
readCount = in.read(bytes, 0, pieceLength - pieceByteCount);
}
in.close();
if (pieceByteCount > 0)
pieces.write(sha1.digest());
return pieces.toByteArray();
}
public void createTorrent() throws IOException {
final int pieceLength = 512 * 1024;
Map<String, Object> info = new HashMap<String, Object>();
info.put("name", sharedFile.getName());
info.put("length", sharedFile.length());
info.put("piece length", pieceLength);
info.put("pieces", hashPieces(sharedFile, pieceLength));
hashInfo(info);
Map<String, Object> metainfo = new HashMap<String, Object>();
if (announceList != null && announceList.length > 0 && announceList[0] != null && announceList[0].length > 0) {
metainfo.put("announce", announceList[0][0]);
if (announceList.length > 1 || announceList[0].length > 1)
metainfo.put("announce-list", announceList);
}
if (urlList != null && urlList.length > 0)
metainfo.put("url-list", (urlList.length == 1) ? urlList[0] : urlList);
//metainfo.put("created by", "libtorrent");
metainfo.put("created by", "ResourceGrabber");
//metainfo.put("creation date", 1325820676);
//metainfo.put("creation date", 1325821361);
metainfo.put("creation date", System.currentTimeMillis() / 1000L);
metainfo.put("info", info);
OutputStream out = new FileOutputStream(torrentFile);
bencode(metainfo, out);
out.close();
}
public void hashInfo(Object o) throws IOException {
MessageDigest sha1;
try {
sha1 = MessageDigest.getInstance("SHA");
} catch (NoSuchAlgorithmException e) {
throw new Error("SHA1 not supported");
}
bencode(o, new DigestOutputStream(new NullOutputStream(), sha1));
byte[] hash = sha1.digest();
this.base32InfoHash = new Base32().encode(hash);
this.sha1InfoHash = new java.math.BigInteger(1, hash).toString(16);
}
public static void main(String[] args) throws Exception {
/*
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/dist/server317.zip", "http://cache.hybridscape.com/minimal317.9.zip", "http://bob.com/tom");
System.out.println("----------------------------------------------");
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/dist/torrents/server317.zip", "http://cache.hybridscape.com/minimal317.9.zip", "http://bob.com/tom");
System.out.println("----------------------------------------------");
new MakeTorrent("/home/mopar/.moparscape4/servers/default/317/server317.zip", "http://cache.hybridscape.com/minimal317.9.zip", "http://bob.com/tom");
System.exit(0);
*/
Debug.debug = true;
if (args.length < 1) {
System.out.println("Usage: MakeTorrent file [webseed...]");
new MakeTorrent();
return;
}
String[] webseeds = new String[args.length - 1];
System.arraycopy(args, 1, webseeds, 0, webseeds.length);
new MakeTorrent(args[0], webseeds);
/*
//new MakeTorrent();
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/cachedump/minimal317.9.zip", "http://cache.hybridscape.com/minimal317.9.zip", "http://bob.com/tom");
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/cachedump/minimal317.9.zip.gz");
System.out.println("my crc: " + ChecksumInfo.crcFile("/home/mopar/onefifty/cachetest/minimal317"));
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/dist/client317.jar");
System.out.println("old CRC of jar: 48487200");
new MakeTorrent("/home/mopar/IdeaProjects/MoparScape4/dist/client317.jar.gz");
*/
}
}