package edu.washington.cs.oneswarm.ui.gwt; import java.io.File; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; 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.util.Base32; import org.gudy.azureus2.core3.util.HashWrapper; import org.gudy.azureus2.core3.util.SystemProperties; import org.gudy.azureus2.plugins.PluginInterface; import org.gudy.azureus2.plugins.disk.DiskManagerFileInfo; import org.gudy.azureus2.plugins.download.Download; import org.gudy.azureus2.plugins.download.DownloadException; import org.gudy.azureus2.plugins.download.DownloadListener; import org.gudy.azureus2.plugins.download.DownloadManager; import org.gudy.azureus2.plugins.download.DownloadRemovalVetoException; import org.gudy.azureus2.plugins.torrent.Torrent; import org.gudy.azureus2.plugins.torrent.TorrentException; import org.gudy.azureus2.plugins.torrent.TorrentFile; 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.share.ShareManagerTools; 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.server.StatelessSwarmFilter; import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.FFMpegAsyncOperationManager; import edu.washington.cs.oneswarm.ui.gwt.server.ffmpeg.FFMpegAsyncOperationManager.DataNotAvailableException; public class CoreInterface { private boolean quit = false; private final PluginInterface pluginInterface; private final F2FInterface f2fInterface; private ConcurrentHashMap<RequiresShutdown, Boolean> shutdownObjects; private String sessionID; private StatelessSwarmFilter mSwarmFilter = null; public CoreInterface(PluginInterface pluginInterface) { this.pluginInterface = pluginInterface; this.sessionID = generateSessionID(); this.shutdownObjects = new ConcurrentHashMap<RequiresShutdown, Boolean>(); this.f2fInterface = new F2FInterface(pluginInterface); Runtime.getRuntime().addShutdownHook(new Thread(new ShutDownThread(this))); mSwarmFilter = new StatelessSwarmFilter(this); } public DownloadManager getDownloadManager() { return pluginInterface.getDownloadManager(); } public String getSessionID() { return sessionID; } public void startDownload(String torrentID) throws DownloadException { startDownload(getDownload(torrentID)); } public void startDownload(Download download) throws DownloadException { download.setForceStart(true); // if there is some error, force recheck if (download.getState() == Download.ST_ERROR) { download.recheckData(); log("rechecking data"); } if (download.getState() == Download.ST_STOPPED || download.getState() == Download.ST_QUEUED) { log("Restarting download"); download.restart(); } else if (download.getState() == Download.ST_READY) { log("starting download"); download.start(); } // PIAMOD -- we'll undo this elsewhere. we use force starts to override // azureus's default (incomprehensible) behavior // download.setForceStart(false); } public void stopDownload(String torrentID) throws DownloadException { stopDownload(getDownload(torrentID)); } public void stopDownload(Download download) throws DownloadException { if (download.getState() != Download.ST_STOPPED) { download.stop(); } } public Download getDownload(String torrentID) { if (torrentID.startsWith(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX)) { torrentID = torrentID.substring(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX.length()); byte[] torrentHash = Base32.decode(torrentID); try { return pluginInterface.getDownloadManager().getDownload(torrentHash); } catch (DownloadException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } public void shutdown() { this.quit = true; // stop everything for (RequiresShutdown shutdown : shutdownObjects.keySet()) { shutdown.shutdown(); } } public boolean removeTorrent(String torrentID, final boolean delete_torrent, final boolean delete_data) { try { Download d = this.getDownload(torrentID); if (d == null) { return false; } int dl_state = d.getState(); // check if it is stopped already if (dl_state != Download.ST_STOPPED) { d.stop(); // if not, stop it and remove when state has changed d.addListener(new DownloadListener() { public void positionChanged(Download download, int oldPosition, int newPosition) { } public void stateChanged(Download download, int old_state, int new_state) { if ((new_state == Download.ST_STOPPED || new_state == Download.ST_ERROR)) { download.removeListener(this); try { deleteStoppedDownload(download, delete_torrent, delete_data); } catch (DownloadException e) { e.printStackTrace(); } catch (DownloadRemovalVetoException e) { e.printStackTrace(); } } } }); } deleteStoppedDownload(d, delete_torrent, delete_data); return true; } catch (DownloadException e) { e.printStackTrace(); } catch (DownloadRemovalVetoException e) { e.printStackTrace(); } return false; } private void deleteStoppedDownload(Download download, boolean delete_torrent, boolean delete_data) throws DownloadException, DownloadRemovalVetoException { if (delete_torrent && delete_data) { download.remove(true, true); } else if (delete_torrent && !delete_data) { download.remove(true, false); } else if (!delete_torrent && delete_data) { download.getStats().deleteDataFiles(); } if (delete_data && !delete_torrent) { download.recheckData(); } } private String generateSessionID() { MessageDigest md; String runID = ""; try { md = MessageDigest.getInstance("SHA"); for (int i = 0; i < 1000; i++) { md.update((byte) Math.random()); } byte[] md5Bytes = md.digest(); String hexString = new String(Base32.encode(md5Bytes)); runID = hexString; } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } return runID; } public void addShutdownObject(RequiresShutdown obj) { System.out.println("Adding shutdown " + obj.toString()); shutdownObjects.put(obj, true); } public void removeShutdownObject(RequiresShutdown obj) { if (shutdownObjects.contains(obj)) { System.out.println("Removing shutdown " + obj.toString()); } shutdownObjects.remove(obj); } private class ShutDownThread implements Runnable { private CoreInterface coreInterface; public ShutDownThread(CoreInterface coreInterface) { this.coreInterface = coreInterface; } public void run() { coreInterface.shutdown(); } } public F2FInterface getF2FInterface() { return f2fInterface; } public PluginInterface getPluginInterface() { return pluginInterface; } public static File getMetaInfoDir(Torrent torrent) throws TorrentException { return getMetaInfoDir(torrent.getHash()); } public static File getMetaInfoDir(byte[] torrentHash) throws TorrentException { String oneSwarmMetaInfoDir = SystemProperties.getMetaInfoPath(); String torrentHex = new String(Base32.encode(torrentHash)); char firstChar = torrentHex.charAt(0); String torrentMetaInfoDirString = oneSwarmMetaInfoDir + SystemProperties.SEP + firstChar + SystemProperties.SEP + torrentHex; File torrentMetaInfoDir = new File(torrentMetaInfoDirString); torrentMetaInfoDir.mkdirs(); if (torrentMetaInfoDir.isDirectory()) { return torrentMetaInfoDir; } return null; } public File getImageFile(Download download) throws TorrentException { TorrentFile activeFile = CoreTools.getBiggestPreviewableFile(download); if (activeFile == null) { return null; } DiskManagerFileInfo fileInfo = CoreTools.getDiskManagerFileInfo(activeFile, download); if (fileInfo == null) { return null; } try { return FFMpegAsyncOperationManager.getInstance().getPreviewImage( download.getTorrent().getHash(), fileInfo.getFile(), 0, TimeUnit.MILLISECONDS); } catch (DataNotAvailableException e) { return null; } } private void log(String msg) { System.out.println(msg); } /** * * @param hash * base32 * @return true if this is one of our friend's files (i.e., we haven't * started a download, but we want to show in the UI) */ public boolean isF2FHash(String hash) { if (hash.startsWith(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX)) { hash = hash.substring(OneSwarmConstants.BITTORRENT_MAGNET_PREFIX.length()); } org.gudy.azureus2.core3.download.DownloadManager dm = AzureusCoreImpl.getSingleton() .getGlobalManager().getDownloadManager(new HashWrapper(Base32.decode(hash))); return dm == null; // == null -> we don't have it -> f2f. } public StatelessSwarmFilter getSwarmFilter() { return mSwarmFilter; } public String getRemoteAccessRate() { if (remoteAccessForward == null) { return "0"; } else { long total = 0; List<Map<String, String>> stats = remoteAccessForward.getRemoteAccessStats(); for (Map<String, String> s : stats) { long rate = Long.parseLong(s.get("upload_rate")); total += rate; } return "" + total; } } private RemoteAccessForward remoteAccessForward; public void setRemoteAccess(RemoteAccessForward remoteAccessForward) { this.remoteAccessForward = remoteAccessForward; } public String getRemoteAccessIps() { if (remoteAccessForward == null) { return ""; } else { HashSet<String> ips = new HashSet<String>(); List<Map<String, String>> stats = remoteAccessForward.getRemoteAccessStats(); for (Map<String, String> s : stats) { String ip = s.get("remote_ip"); String host = s.get("remote_dns"); if (host == null) { ips.add(ip); } else { ips.add(ip + " (" + host + ")"); } } StringBuilder b = new StringBuilder(); for (String ip : ips) { b.append(ip + ", "); } if (b.length() > 2) { return b.toString().substring(0, b.length() - 2); } else { return ""; } } } public RemoteAccessForward getRemoteAccessForward() { return remoteAccessForward; } }