package edu.washington.cs.oneswarm.ui.gwt.server;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.gudy.azureus2.core3.internat.LocaleTorrentUtil;
import org.gudy.azureus2.core3.internat.LocaleUtilEncodingException;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.torrent.TOTorrentFile;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloader;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloaderCallBackInterface;
import org.gudy.azureus2.core3.torrentdownloader.TorrentDownloaderFactory;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.TorrentUtils;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreException;
import com.aelitis.azureus.core.impl.AzureusCoreImpl;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import edu.washington.cs.oneswarm.f2f.FileListFile;
import edu.washington.cs.oneswarm.f2f.permissions.GroupBean;
import edu.washington.cs.oneswarm.f2f.permissions.PermissionsDAO;
import edu.washington.cs.oneswarm.f2f.share.ShareManagerTools;
import edu.washington.cs.oneswarm.plugins.PluginCallback;
import edu.washington.cs.oneswarm.ui.gwt.CoreInterface;
import edu.washington.cs.oneswarm.ui.gwt.RequiresShutdown;
import edu.washington.cs.oneswarm.ui.gwt.rpc.FileListLite;
import edu.washington.cs.oneswarm.ui.gwt.rpc.OneSwarmConstants;
import edu.washington.cs.oneswarm.ui.gwt.rpc.PermissionsGroup;
public class AddTorrentManager implements RequiresShutdown {
private static Logger logger = Logger.getLogger(AddTorrentManager.class.getName());
private Random mRandom = new Random();
private class TorrentDownloaderLite {
private int downloadState;
private int totalRead;
private int percentDone;
private String status;
private File file;
public File getFile() {
return file;
}
public void setFile(File file) {
this.file = file;
}
public int getDownloadState() {
return downloadState;
}
public void setDownloadState(int downloadState) {
this.downloadState = downloadState;
}
public int getTotalRead() {
return totalRead;
}
public void setTotalRead(int totalRead) {
this.totalRead = totalRead;
}
public int getPercentDone() {
return percentDone;
}
public void setPercentDone(int percentDone) {
this.percentDone = percentDone;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
private final ConcurrentHashMap<Integer, TorrentDownloaderLite> downloaderMap;
// private final ConcurrentHashMap<Integer, Torrent>
private final CoreInterface coreInterface;
private final AzureusCore core;
private boolean quit = false;
public AddTorrentManager(CoreInterface coreInterface) {
this.downloaderMap = new ConcurrentHashMap<Integer, TorrentDownloaderLite>();
this.coreInterface = coreInterface;
this.core = AzureusCoreImpl.getSingleton();
coreInterface.addShutdownObject(this);
}
public int downloadTorrent(int friendConnection, int channelId, String torrentId, int lengthHint) {
int rand = mRandom.nextInt();
final TorrentDownloaderLite downloader = new TorrentDownloaderLite();
downloaderMap.put(rand, downloader);
coreInterface.getF2FInterface().getMetaInfo(friendConnection, channelId, torrentId,
lengthHint, new PluginCallback<byte[]>() {
public void dataRecieved(long bytes) {
downloader.setTotalRead(((int) (downloader.getTotalRead() + bytes)));
downloader.setDownloadState(TorrentDownloader.STATE_DOWNLOADING);
}
public void errorOccured(String string) {
downloader.setStatus("Error: " + string);
downloader.setDownloadState(TorrentDownloader.STATE_ERROR);
}
public void progressUpdate(int progress) {
downloader.setStatus("Downloading: " + progress + "%");
downloader.setPercentDone(progress);
}
public void requestCompleted(byte[] data) {
try {
File f = File.createTempFile("oneswarm_", ".torrent");
f.deleteOnExit();
logger.fine("writing torrent to file: " + f.getCanonicalPath());
/**
* We've modified the TOTorrentImpl to preserve the
* required OneSwarm attributes while dumping the
* rest (this include no text search, tags, album,
* etc.)
*/
TOTorrent torrent = TorrentUtils
.readFromBEncodedInputStream(new ByteArrayInputStream(data));
torrent.setAdditionalStringProperty("encoding", "UTF-8");
TorrentUtils.writeToFile(torrent, f);
downloader.setFile(f);
downloader.setDownloadState(TorrentDownloader.STATE_FINISHED);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
errorOccured(e.getMessage());
} catch (TOTorrentException e) {
System.err.println(e);
e.printStackTrace();
errorOccured(e.getMessage());
}
}
});
return rand;
}
public int downloadTorrent(String url) {
// url decode the string
try {
logger.finer("decoding url");
url = URLDecoder.decode(url, "ISO-8859-1");
logger.finer("new url is: " + url);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return -1;
}
if (url == null) {
return -1;
}
if (url.startsWith(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX)) {
logger.finer("modifying torrent url from: " + url);
url = url.substring(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX.length());
url = OneSwarmConstants.BITTORRENT_MAGNET_PREFIX_REAL + url;
logger.finer("new torrent url: " + url);
}
int torrentId = -1;
try {
File tempFile = File.createTempFile("OneSwam_tmp_torrent_", ".torrent");
tempFile.deleteOnExit();
logger.finest("creating temp file: " + tempFile.getCanonicalPath());
final TorrentDownloaderLite downloader = new TorrentDownloaderLite();
TorrentDownloader torrentDownloader = TorrentDownloaderFactory.create(
new TorrentDownloaderCallBackInterface() {
public void TorrentDownloaderEvent(int state, TorrentDownloader inf) {
try {
logger.finest("got torrent download event: state=" + state
+ " done=" + inf.getPercentDone() + " URL='" + inf.getURL()
+ "'");
try {
downloader.setTotalRead(inf.getTotalRead());
downloader.setDownloadState(state);
downloader.setPercentDone(inf.getPercentDone());
downloader.setStatus(inf.getStatus());
if (state != TorrentDownloader.STATE_ERROR) {
downloader.setFile(inf.getFile());
}
} catch (Exception e) {
e.printStackTrace();
}
switch (state) {
case TorrentDownloader.STATE_CANCELLED:
downloaderMap.remove(inf.getURL().hashCode());
break;
case TorrentDownloader.STATE_ERROR:
downloaderMap.remove(inf.getURL().hashCode());
break;
}
} catch (Exception e) {
e.printStackTrace();
if (inf.getURL() != null) {
downloaderMap.remove(inf.getURL().hashCode());
}
}
}
}, url, null, tempFile.getCanonicalPath());
torrentDownloader.start();
torrentDownloader.setDeleteFileOnCancel(true);
torrentId = url.hashCode();
logger.fine("started torrent download, id=" + torrentId);
downloaderMap.put(torrentId, downloader);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return -1;
}
return torrentId;
}
public int getPercentageDone(int torrentDownloaderHash) {
TorrentDownloaderLite inf = downloaderMap.get(torrentDownloaderHash);
if (inf != null) {
if (inf.getDownloadState() == TorrentDownloader.STATE_FINISHED) {
return 100;
}
return inf.getPercentDone();
}
return -2;
}
public int getTotalRead(int torrentDownloaderHash) {
TorrentDownloaderLite inf = downloaderMap.get(torrentDownloaderHash);
if (inf != null) {
return inf.getTotalRead();
}
return -1;
}
public String getStatus(int torrentDownloaderHash) {
TorrentDownloaderLite inf = downloaderMap.get(torrentDownloaderHash);
if (inf != null) {
return inf.getStatus();
}
return null;
}
public FileListLite[] getFiles(int torrentDownloaderHash) {
TorrentDownloaderLite inf = downloaderMap.get(torrentDownloaderHash);
if (inf != null && inf.getDownloadState() == TorrentDownloader.STATE_FINISHED) {
File f = inf.getFile();
try {
logger.finest("about to readFromFile()");
TOTorrent torrent = TorrentUtils.readFromFile(f, false);
logger.finest("processing torrent: " + new String(torrent.getName()));
logger.finest("after readFromFile()");
boolean limitedVisibility = torrent.getAdditionalProperty("OneSwarmNoShare") != null;
if (limitedVisibility) {
logger.fine("torrent has OneSwarmNoShare set");
} else {
logger.fine("torrent is shared with all friends");
}
/*
* check the locale
*/
String enc = LocaleTorrentUtil.getCurrentTorrentEncoding(torrent);
if (enc == null) {
logger.finer("torrent lacks encoding field, trying to detect...");
// we might need to query the user for this...
try {
// this will set the encoding field in the torrent
LocaleTorrentUtil.getTorrentEncoding(torrent);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// if that didn't work, set the default
enc = LocaleTorrentUtil.getCurrentTorrentEncoding(torrent);
logger.finer("got encoding: " + enc);
if (enc == null) {
try {
LocaleTorrentUtil.setDefaultTorrentEncoding(torrent);
enc = LocaleTorrentUtil.getCurrentTorrentEncoding(torrent);
logger.finer("setting default encoding instead: " + enc);
} catch (LocaleUtilEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (enc != null) {
TorrentUtils.writeToFile(torrent);
}
}
TOTorrentFile[] files = torrent.getFiles();
logger.finest("after getFiles(), file num=" + files.length);
FileListLite[] fList = new FileListLite[files.length];
for (int i = 0; i < files.length; i++) {
TOTorrentFile torrentFile = files[i];
logger.finest("adding: " + torrentFile.getRelativePath());
byte[][] pathComponents = torrentFile.getPathComponents();
for (byte[] bs : pathComponents) {
logger.finest("path component: " + new String(bs));
}
try {
fList[i] = new FileListLite(new String(Base64.encode(torrent.getHash())),
new String(torrent.getName(), "UTF-8"),
torrentFile.getRelativePath(), torrentFile.getLength(),
files.length, new Date().getTime(), 1, false, false);
} catch (UnsupportedEncodingException e) {
System.err.println(e);
logger.severe(e.toString());
fList[i] = new FileListLite(new String(Base64.encode(torrent.getHash())),
new String(torrent.getName()), torrentFile.getRelativePath(),
torrentFile.getLength(), files.length, new Date().getTime(), 1,
false, false);
}
if (limitedVisibility) {
fList[i].setOneSwarmNoShare(true);
}
FileListFile flf = new FileListFile();
ShareManagerTools.setSha1AndEd2k(torrent, torrentFile, flf);
if (flf.getSha1Hash() != null) {
fList[i].setSha1Hash(new String(Base64.encode(flf.getSha1Hash())));
}
if (flf.getEd2kHash() != null) {
fList[i].setEd2kHash(new String(ShareManagerTools.base16Encode(flf
.getEd2kHash())));
}
}
return fList;
} catch (TOTorrentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return new FileListLite[0];
}
public boolean addTorrent(int torrentDownloaderHash, final FileListLite[] selectedFiles,
final List<PermissionsGroup> inPerms, String path, final boolean noStream) {
TorrentDownloaderLite inf = null;
logger.fine("Adding torrent: " + torrentDownloaderHash);
try {
inf = downloaderMap.get(torrentDownloaderHash);
if (inf == null) {
return false;
}
while (!quit && inf.getDownloadState() != TorrentDownloader.STATE_FINISHED) {
logger.finer("waiting for torrent file to download: downloaded="
+ inf.getTotalRead() + " (" + inf.getPercentDone() + "%)");
Thread.sleep(100);
}
if (!quit) {
File f = inf.getFile();
TOTorrent torrent = TorrentUtils.readFromFile(f, false);
Set<String> selectedFilesSet = new HashSet<String>();
if (selectedFiles != null) {
for (FileListLite fileListLite : selectedFiles) {
selectedFilesSet.add(fileListLite.getFileName());
}
}
ArrayList<GroupBean> converted_groups = new ArrayList<GroupBean>(inPerms.size());
for (PermissionsGroup g : inPerms) {
try {
converted_groups.add(PermissionsDAO.get().getGroup(g.getGroupID()));
} catch (Exception e) {
Debug.out("problem converting PermissionsGroup to GroupBean: "
+ g.getName());
e.printStackTrace();
}
}
return ShareManagerTools.addDownload(selectedFilesSet, converted_groups, path,
noStream, f, torrent) != null;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AzureusCoreException e) {
System.err.println(e);
e.printStackTrace();
} catch (TOTorrentException e) {
System.err.println(e);
e.printStackTrace();
} finally {
File file = inf.getFile();
if (file != null) {
file.delete();
}
}
return false;
}
// private Download addDownload(Torrent torrent) throws DownloadException,
// TorrentException, IOException, InterruptedException {
// String defaultDir =
// COConfigurationManager.getDirectoryParameter("Default save path");
// if( defaultDir == null ) {
// String docPath = SystemProperties.getDocPath();
// File f = new File(docPath, "OneSwarm Downloads");
// ConfigurationManager.getInstance().setParameter("Default save path",
// f.getAbsolutePath());
// defaultDir = f.getAbsolutePath();
// }
// return addDownload(torrent, defaultDir);
// }
//
// private Download addDownload(Torrent torrent, String path) throws
// DownloadException, TorrentException, IOException, InterruptedException {
// // check if the torrent already exists
// Download d;
// if ((d =
// coreInterface.getDownloadManager().getDownload(torrent.getHash())) !=
// null) {
// logger.fine("torrent already exists!");
// return d;
// } else {
//
// File torrentFile = writeTorrentFile(torrent);
// Torrent newTorrent =
// coreInterface.getPluginInterface().getTorrentManager().createFromBEncodedFile(torrentFile);
//
// d = coreInterface.getDownloadManager().addDownloadStopped(newTorrent,
// torrentFile, new File(path));
// return d;
// }
// }
//
// private File writeTorrentFile(Torrent torrent) throws TorrentException {
//
// File torrentMetaInfoDir = CoreInterface.getMetaInfoDir(torrent);
//
// File torrentFile = new File(torrentMetaInfoDir, "metainfo.torrent");
// torrent.writeToFile(torrentFile);
// return torrentFile;
// }
public void shutdown() {
quit = true;
}
public String getTorrentName(int torrentDownloadID) {
TorrentDownloaderLite inf = downloaderMap.get(torrentDownloadID);
TOTorrent torrent;
try {
torrent = TorrentUtils.readFromFile(inf.getFile(), false);
} catch (TOTorrentException e1) {
return null;
}
String name;
try {
name = new String(torrent.getName(), "UTF-8");
return name;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return new String(torrent.getName());
}
}
}