package uc.files; import java.io.File; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import uc.DCClient; import uc.IUser; import uc.crypto.HashValue; import uc.crypto.TigerHashValue; import uc.files.AbstractDownloadable.AbstractDownloadableFile; import uc.files.IDownloadable.IDownloadableFile; import uc.files.downloadqueue.AbstractDownloadQueueEntry; import uc.files.search.FileSearch; import uc.protocols.IProtocolCommand; /** * represents a magnet Link * can be used to download things just with a link received in chat * * @author Quicksilver * */ public class MagnetLink extends AbstractDownloadableFile implements IDownloadableFile { private static final Map<String,String> pattern = new HashMap<String,String>(); public static String KEYWORD_TOPIC = "kt"; private static final String TEXT = "(?:[^\\x20\\x0C\\x0A&]*)"; static { pattern.put("xt", "urn:tree:tiger:("+TigerHashValue.TTHREGEX+")" ); pattern.put("xl", "("+IProtocolCommand.FILESIZE +")"); } // private static final String // XT = "(?:xt\\=urn:tree:tiger:("+TigerHashValue.TTHREGEX+"))" // // ,XL = "(?:xl\\=("+IProtocolCommand.FILESIZE +"))" // // ,DN = "(?:dn\\=("+IProtocolCommand.TEXT_NONEWLINE_NOSPACE+"))" // // ,KT = "(?:kt\\=("+IProtocolCommand.TEXT_NONEWLINE_NOSPACE+"))" // ,OTHER = "(?:(..)(?:\\.\\d+)?\\=("+IProtocolCommand.TEXT_NONEWLINE_NOSPACE+"))"; private static final String PARM = "(?:(\\w{2})(?:\\.\\d+)?\\=("+TEXT+"))"; //"(?:"+XT+"|"+XL+"|"+OTHER+")"; public static final String MagnetURI = "magnet:\\?"+PARM+"(?:&"+PARM+")*"; // "" + // "(?:xt\\=urn:tree:tiger:("+TigerHashValue.TTHREGEX+"))?" + // "&?(?:xl\\=("+IProtocolCommand.FILESIZE +"))?" // +"&?(?:dn\\=("+IProtocolCommand.TEXT_NONEWLINE_NOSPACE+"))?"; public static final Pattern MAGNET_PAT = Pattern.compile(MagnetURI); /** * * @param s * @return a magnet link from the given string.. null if not a valid magnet link */ public static MagnetLink parse(String magnetURI) { Matcher magnet = MAGNET_PAT.matcher(magnetURI); if (magnet.matches()) { Map<String,String> allInfo = new HashMap<String,String>(); for (int i = 1; i <= magnet.groupCount(); i+=2) { String key = magnet.group(i); String value = magnet.group(i+1); String mod = pattern.get(key); if (value != null && key != null) { if (mod != null) { Matcher m = Pattern.compile(mod).matcher(value); if (m.matches()) { value = m.group(1); } else { value = null; } } else { try { value = URLDecoder.decode(value, "utf-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } catch (NullPointerException npe) { throw new IllegalStateException("Causedby: "+magnetURI+" "+i,npe); } } } if (value != null) { allInfo.put(key, value); } } HashValue hash = null; if (allInfo.containsKey("xt")) { hash = HashValue.createHash(allInfo.get("xt")); } long size = -1; if (allInfo.containsKey("xl")) { size = Long.parseLong(allInfo.get("xl")); } String name = allInfo.get("dn"); //boolean complete = hash != null && size != -1 && name != null; return new MagnetLink(magnetURI,hash,size,name,allInfo); } return null; } private final String fullString; private final Map<String,String> other = new HashMap<String,String>(); private final HashValue hash; private final long filesize; private final String filename; public MagnetLink(IDownloadableFile idf) { this(null,idf.getTTHRoot(),idf.getSize(),idf.getName(),Collections.<String,String>emptyMap()); } public MagnetLink(String fullString,HashValue hash, long filesize, String filename,Map<String,String> allInfo) { super(); this.fullString = fullString; this.hash = hash; this.filesize = filesize; this.filename = filename; other.putAll(allInfo); } public String toString() { if (fullString != null) { return fullString; } try { return String.format("magnet:?xt=urn:tree:%s:%s&xl=%d&dn=%s", hash.magnetString(),hash,filesize,URLEncoder.encode(filename, "utf-8")); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e); } } @Override public AbstractDownloadQueueEntry download(File target) { AbstractDownloadQueueEntry adqe = super.download(target); DCClient.get().search( new FileSearch(hash)); //always search for alternatives.. return adqe; } public boolean isComplete() { return hash != null && filesize != -1 && filename != null; } public long getSize() { return filesize; } @Override public String getPath() { return filename; } @Override public HashValue getTTHRoot() { return hash; } @Override public IUser getUser() { throw new UnsupportedOperationException(); } @Override public Collection<IUser> getIterable() { return Collections.emptyList(); } public String getName() { if (filename == null) { return null; } else { return super.getName(); } } public String get(String parm) { return other.get(parm); } @Override public int nrOfUsers() { return 0; } }