package edu.washington.cs.oneswarm.f2f; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.gudy.azureus2.core3.download.DownloadManager; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLSet; import org.gudy.azureus2.core3.torrent.TOTorrentException; import org.gudy.azureus2.core3.torrent.TOTorrentFactory; import org.gudy.azureus2.core3.torrent.impl.TOTorrentImpl; import org.gudy.azureus2.core3.util.BEncoder; import org.gudy.azureus2.core3.util.Base32; import org.gudy.azureus2.core3.util.ByteFormatter; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.HashWrapper; import org.gudy.azureus2.core3.util.SystemProperties; import com.aelitis.azureus.core.impl.AzureusCoreImpl; import edu.washington.cs.oneswarm.f2f.messaging.OSF2FMessage; import edu.washington.cs.oneswarm.f2f.multisource.Sha1HashManager; import edu.washington.cs.oneswarm.f2f.permissions.PermissionsDAO; public class MetaInfoManager { private static java.util.logging.Logger logger = Logger.getLogger(MetaInfoManager.class .getName()); private final HashMap<Integer, Boolean> existingInfoHashes; public MetaInfoManager() { existingInfoHashes = new HashMap<Integer, Boolean>(); } public byte[] getMetaInfo(Friend remoteFriend, byte type, byte[] infohash) { DownloadManager dm = AzureusCoreImpl.getSingleton().getGlobalManager() .getDownloadManager(new HashWrapper(infohash)); if (dm != null) { // it is "allowed", check permissions boolean allowed = PermissionsDAO.get().hasPermissions(remoteFriend.getPublicKey(), infohash); if (allowed) { TOTorrent torrent = dm.getTorrent(); switch (type) { case OSF2FMessage.METAINFO_TYPE_BITTORRENT: try { TOTorrent clone = TOTorrentFactory.deserialiseFromMap(torrent .serialiseToMap()); if (clone.getPrivate()) { String replaceWithUrl = "http://tracker.removed.was.private.torrent.invalid/announce"; clone.setAnnounceURL(new URL(replaceWithUrl)); clone.getAnnounceURLGroup().setAnnounceURLSets( new TOTorrentAnnounceURLSet[0]); } /* * if the swarm has custom permissions (not shared with * all friends) set the OneSwarmNoShare flag to 1 to * indicate that the file should have special * permissions */ if (!PermissionsDAO.get().hasAllFriendsPermission(infohash)) { clone.setAdditionalLongProperty(TOTorrentImpl.OS_NO_SHARE, new Long(1)); clone.setAdditionalLongProperty(TOTorrentImpl.OS_NO_TEXT_SEARCH, new Long(1)); } /** * Strip out anything that's specific to us locally... */ clone.removeAdditionalProperties(); if (logger.isLoggable(Level.FINEST)) { System.out.println("*************original torrent*******************"); torrent.print(); System.out.println("*************filtered torrent*******************"); clone.print(); System.out.println("************************************************"); } Map root = clone.serialiseToMap(); return BEncoder.encode(root); } catch (IOException e) { Debug.out("failed to serialize torrent", e); } catch (TOTorrentException e) { Debug.out("failed to serialize torrent", e); } break; case OSF2FMessage.METAINFO_TYPE_THUMBNAIL: try { File imageFile = getImageFile(torrent.getHash(), false); if (imageFile != null && imageFile.exists() && imageFile.length() > 0) { logger.fine("got image request, looking at file " + imageFile.getAbsolutePath()); BufferedInputStream source = new BufferedInputStream( new FileInputStream(imageFile)); byte[] allData = readStream(source); logger.fine("read " + allData.length + " bytes"); return allData; } return new byte[0]; } catch (TOTorrentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } break; default: Debug.out("metainfo type '" + type + "' not supported"); break; } } else { logger.fine("got metainfo request for " + new String(dm.getTorrent().getName()) + " but friend '" + remoteFriend.getNick() + "' has no access to that file"); } } return null; } public List<byte[]> getTorrentThumbnailNeeded(List<byte[]> infohashes) { List<byte[]> newHashes = new LinkedList<byte[]>(); logger.finest("getTorrentThumbnailNeeded considering: " + infohashes.size() + " hashes"); for (byte[] infohash : infohashes) { if (!existingInfoHashes.containsKey(Arrays.hashCode(infohash))) { DownloadManager dm = AzureusCoreImpl.getSingleton().getGlobalManager() .getDownloadManager(new HashWrapper(infohash)); // don't send requests for stuff we already have data for if (dm != null) { existingInfoHashes.put(Arrays.hashCode(infohash), true); logger.finest("we already have: " + ByteFormatter.encodeString(infohash)); } else { // new hash, lets check if we have it on disk try { if (!hasImage(infohash)) { // check if we tried this file before logger.finest("checking refresh to get image for: " + ByteFormatter.encodeString(infohash)); if (getNextPreviewRequestTime(infohash) <= System.currentTimeMillis()) { newHashes.add(infohash); logger.finest("added: " + ByteFormatter.encodeString(infohash)); } else { logger.finest("too old: " + ByteFormatter.encodeString(infohash)); } } else { existingInfoHashes.put(Arrays.hashCode(infohash), true); logger.finest("we already have image for: " + ByteFormatter.encodeString(infohash)); } } catch (TOTorrentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } else { logger.finest("we already have: " + ByteFormatter.encodeString(infohash)); } } Collections.reverse(newHashes); // newest things first return newHashes; } private static byte[] readStream(BufferedInputStream source) throws IOException { ArrayList<byte[]> data = new ArrayList<byte[]>(); int read = 0; int total = 0; byte[] buffer = new byte[32 * 1024]; while ((read = source.read(buffer, 0, buffer.length)) != -1) { byte[] toSave = new byte[read]; System.arraycopy(buffer, 0, toSave, 0, toSave.length); data.add(toSave); total += read; } source.close(); byte[] allData = new byte[total]; int pos = 0; for (int i = 0; i < data.size(); i++) { byte[] b = data.get(i); System.arraycopy(b, 0, allData, pos, b.length); pos += b.length; } return allData; } private static File getMetaInfoDir(byte[] hash) throws TOTorrentException { String oneSwarmMetaInfoDir = SystemProperties.getMetaInfoPath(); // String torrentHex = new String(Hex.encode(torrent.getHash())); String torrentHex = new String(Base32.encode(hash)); 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; } private static File getImageFile(byte[] hash, boolean allowFriend) throws TOTorrentException { File metaInfoDir = getMetaInfoDir(hash); List<File> metainfoFiles = Arrays.asList(metaInfoDir.listFiles(new FileFilter() { public boolean accept(File pathname) { String name = pathname.getName(); return name.startsWith("preview_") && name.endsWith(".png"); } })); Collections.sort(metainfoFiles, new Comparator<File>() { public int compare(File o1, File o2) { return (int) (o1.lastModified() - o2.lastModified()); } }); File friendFile = null; for (int i = 0; i < metainfoFiles.size(); i++) { File currFile = metainfoFiles.get(i); if (currFile.getName().equals("preview_friend.png")) { friendFile = currFile; } else { return currFile; } } if (allowFriend && friendFile != null) { return friendFile; } else { return null; } } private static boolean hasImage(byte[] hash) throws TOTorrentException { File imgFile = getImageFile(hash, false); if (imgFile != null && imgFile.exists() && imgFile.length() > 0) { return true; } imgFile = getImageFile(hash, true); if (imgFile != null && imgFile.exists() && imgFile.length() > 0) { return true; } return false; } private static long getNextPreviewRequestTime(byte[] hash) throws TOTorrentException, IOException { File errorfile = new File(getMetaInfoDir(hash), "preview_friend_error.log"); long timeVal = System.currentTimeMillis() - 10; if (errorfile.exists()) { BufferedReader reader = new BufferedReader(new FileReader(errorfile)); String line = reader.readLine(); reader.close(); try { timeVal = Long.parseLong(line); } catch (Exception e) { } } return timeVal; } private static long getLastPreviewRequestInterval(byte[] hash) throws TOTorrentException, IOException { File errorfile = new File(getMetaInfoDir(hash), "preview_friend_error.log"); long timeVal = 900000; // 15 minutes if (errorfile.exists()) { BufferedReader reader = new BufferedReader(new FileReader(errorfile)); String line = reader.readLine(); line = reader.readLine(); reader.close(); try { timeVal = Long.parseLong(line); } catch (Exception e) { } } return timeVal; } public void gotImageResponse(byte[] infohash, byte[] data) { try { if (data == null || data.length == 0) { // System.err.println( // "got image response of len 0, increment counter?"); // wait with this for a couple of versions previewImageRequestFailed(infohash); } else { File imageFile = new File(getMetaInfoDir(infohash), "preview_friend.png"); logger.fine("got image response, writing to: " + imageFile.getPath()); BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(imageFile)); out.write(data); out.close(); } } catch (TOTorrentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void previewImageRequestFailed(byte[] hash) throws TOTorrentException, IOException { long previousInterval = 900000; // 15 minutes try { previousInterval = getLastPreviewRequestInterval(hash); } catch (Exception e) { } long newInterval = Math.min(previousInterval * 2, 604800000); // 1 week long nextTime = System.currentTimeMillis() + newInterval; logger.fine("request failed, previousInterval: " + previousInterval + " nextInterval: " + newInterval + " next time: " + (new Date(nextTime))); File errorFile = new File(getMetaInfoDir(hash), "preview_friend_error.log"); FileWriter f = new FileWriter(errorFile); f.write("" + (nextTime) + "\n"); f.write("" + newInterval + "\n"); f.close(); } }