package com.limegroup.gnutella.downloader.serial.conversion;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.limewire.collection.Range;
import org.limewire.core.api.Category;
import org.limewire.core.api.file.CategoryManager;
import org.limewire.core.settings.SharingSettings;
import org.limewire.io.Address;
import org.limewire.io.ConnectableImpl;
import org.limewire.io.IOUtils;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.net.address.AddressFactory;
import org.limewire.util.CommonUtils;
import com.google.inject.Inject;
import com.limegroup.gnutella.PushEndpointFactory;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.browser.MagnetOptions;
import com.limegroup.gnutella.downloader.DownloaderType;
import com.limegroup.gnutella.downloader.RemoteFileDescImpl;
import com.limegroup.gnutella.downloader.serial.BTDiskManagerMemento;
import com.limegroup.gnutella.downloader.serial.BTDiskManagerMementoImpl;
import com.limegroup.gnutella.downloader.serial.BTDownloadMemento;
import com.limegroup.gnutella.downloader.serial.BTDownloadMementoImpl;
import com.limegroup.gnutella.downloader.serial.BTMetaInfoMemento;
import com.limegroup.gnutella.downloader.serial.BTMetaInfoMementoImpl;
import com.limegroup.gnutella.downloader.serial.DownloadMemento;
import com.limegroup.gnutella.downloader.serial.GnutellaDownloadMemento;
import com.limegroup.gnutella.downloader.serial.GnutellaDownloadMementoImpl;
import com.limegroup.gnutella.downloader.serial.MagnetDownloadMemento;
import com.limegroup.gnutella.downloader.serial.MagnetDownloadMementoImpl;
import com.limegroup.gnutella.downloader.serial.OldDownloadConverter;
import com.limegroup.gnutella.downloader.serial.RemoteHostMemento;
import com.limegroup.gnutella.downloader.serial.TorrentFileSystemMemento;
import com.limegroup.gnutella.downloader.serial.TorrentFileSystemMementoImpl;
import com.limegroup.gnutella.downloader.serial.conversion.DownloadConverterObjectInputStream.Version;
public class OldDownloadConverterImpl implements OldDownloadConverter {
private static Log LOG = LogFactory.getLog(OldDownloadConverterImpl.class);
private final PushEndpointFactory pushEndpointFactory;
private final AddressFactory addressFactory;
private final CategoryManager categoryManager;
@Inject
public OldDownloadConverterImpl(PushEndpointFactory pushEndpointFactory, AddressFactory addressFactory, CategoryManager categoryManager) {
this.pushEndpointFactory = pushEndpointFactory;
this.addressFactory = addressFactory;
this.categoryManager = categoryManager;
}
public List<DownloadMemento> readAndConvertOldDownloads(File inputFile) throws IOException {
if(!inputFile.exists())
throw new java.io.FileNotFoundException("file " + inputFile + " doesn't exist!");
DownloadConverterObjectInputStream in = null;
List roots = null;
SerialIncompleteFileManager sifm = null;
Version[] versions = Version.values();
for(int i = 0; i < versions.length; i++) {
try {
in = new DownloadConverterObjectInputStream(new BufferedInputStream(new FileInputStream(inputFile)));
in.deserializeVersion(versions[i]);
roots = (List)in.readObject();
sifm = (SerialIncompleteFileManager)in.readObject();
break;
} catch(StreamCorruptedException sce) {
LOG.debug("Unable to deserialize from version: " + versions[i], sce);
continue;
} catch(ClassNotFoundException cnfe) {
throw (IOException)new IOException().initCause(cnfe);
} finally {
IOUtils.close(in);
}
}
if(roots != null && sifm != null)
return convertSerialRootsToMementos(roots, sifm);
else
return Collections.emptyList();
}
private List<DownloadMemento> convertSerialRootsToMementos(List roots,
SerialIncompleteFileManager sifm) throws IOException {
List<DownloadMemento> mementos = new ArrayList<DownloadMemento>(roots.size());
for(Object o : roots) {
if(o instanceof SerialBTDownloader)
addBTDownloader(mementos, (SerialBTDownloader)o, sifm);
else if(o instanceof SerialInNetworkDownloader)
; // ignore for conversions -- they'll restart on their own
else if(o instanceof SerialMagnetDownloader)
addMagnet(mementos, (SerialMagnetDownloader)o, sifm);
else if(o instanceof SerialResumeDownloader)
addResume(mementos, (SerialResumeDownloader)o, sifm);
else if(o instanceof SerialRequeryDownloader)
; // ignore!
else if(o instanceof SerialStoreDownloader)
addStore(mementos, (SerialStoreDownloader)o, sifm);
else if(o instanceof SerialManagedDownloader)
addManaged(mementos, (SerialManagedDownloader)o, sifm);
else
LOG.warn("Unable to convert read object: " + o);
}
return mementos;
}
private void addGnutellaProperties(GnutellaDownloadMemento memento, Map<String, Serializable> properties, List<Range> ranges, File incompleteFile, Set<SerialRemoteFileDesc> rfds) {
memento.setSavedBlocks(ranges);
memento.setIncompleteFile(incompleteFile);
memento.setRemoteHosts(convertToMementos(rfds));
memento.setContentLength(properties.get("fileSize") == null ? -1 : ((Number)properties.get("fileSize")).longValue());
memento.setSha1Urn((URN)properties.get("sha1Urn"));
addCommonProperties(memento, properties);
}
@SuppressWarnings("unchecked")
private void addCommonProperties(DownloadMemento memento, Map<String, Serializable> properties) {
memento.setAttributes((Map<String, Object>)properties.get("attributes"));
memento.setDefaultFileName((String)properties.get("defaultFileName"));
memento.setSaveFile((File)properties.get("saveFile"));
}
private void addStore(List<DownloadMemento> mementos, SerialStoreDownloader o, SerialIncompleteFileManager sifm) {
File incompleteFile = getIncompleteFile(o, sifm);
List<Range> ranges = getRanges(incompleteFile, sifm);
GnutellaDownloadMemento memento = new GnutellaDownloadMementoImpl();
memento.setDownloadType(DownloaderType.STORE);
addGnutellaProperties(memento, o.getProperties(), ranges, incompleteFile, o.getRemoteFileDescs());
mementos.add(memento);
}
private void addManaged(List<DownloadMemento> mementos, SerialManagedDownloader o, SerialIncompleteFileManager sifm) {
File incompleteFile = getIncompleteFile(o, sifm);
List<Range> ranges = getRanges(incompleteFile, sifm);
GnutellaDownloadMemento memento = new GnutellaDownloadMementoImpl();
memento.setDownloadType(DownloaderType.MANAGED);
addGnutellaProperties(memento, o.getProperties(), ranges, incompleteFile, o.getRemoteFileDescs());
mementos.add(memento);
}
private void addResume(List<DownloadMemento> mementos, SerialResumeDownloader o, SerialIncompleteFileManager sifm) {
File incompleteFile = getIncompleteFile(o, sifm);
List<Range> ranges = getRanges(incompleteFile, sifm);
o.getProperties().put("fileSize", o.getSize());
o.getProperties().put("sha1Urn", o.getUrn());
o.getProperties().put("defaultFileName", o.getName());
GnutellaDownloadMemento memento = new GnutellaDownloadMementoImpl();
memento.setDownloadType(DownloaderType.MANAGED);
addGnutellaProperties(memento, o.getProperties(), ranges, incompleteFile, o.getRemoteFileDescs());
mementos.add(memento);
}
private void addMagnet(List<DownloadMemento> mementos, SerialMagnetDownloader o, SerialIncompleteFileManager sifm) {
File incompleteFile = getIncompleteFile(o, sifm);
List<Range> ranges = getRanges(incompleteFile, sifm);
if(o.getUrn() != null)
o.getProperties().put("sha1Urn", o.getUrn());
MagnetDownloadMemento memento = new MagnetDownloadMementoImpl();
memento.setDownloadType(DownloaderType.MAGNET);
memento.setMagnet((MagnetOptions)o.getProperties().get("MAGNET"));
addGnutellaProperties(memento, o.getProperties(), ranges, incompleteFile, o.getRemoteFileDescs());
mementos.add(memento);
}
private void addBTDownloader(List<DownloadMemento> mementos, SerialBTDownloader o, SerialIncompleteFileManager sifm) throws IOException {
BTDownloadMemento memento = new BTDownloadMementoImpl();
memento.setDownloadType(DownloaderType.BTDOWNLOADER);
memento.setBtMetaInfoMemento(toBTMetaInfoMemento((SerialBTMetaInfo)o.getProperties().get("metainfo")));
addCommonProperties(memento, o.getProperties());
mementos.add(memento);
}
private BTMetaInfoMemento toBTMetaInfoMemento(SerialBTMetaInfo info) throws IOException {
BTMetaInfoMemento memento = new BTMetaInfoMementoImpl();
memento.setFileSystem(toFileSystemMemento(info.getFileSystem()));
memento.setFolderData(toBTDiskManagerMemento(info.getDiskManagerData()));
memento.setHashes(info.getHashes());
memento.setInfoHash(info.getInfoHash());
memento.setPieceLength(info.getPieceLength());
memento.setPrivate(info.isPrivate());
memento.setRatio(info.getHistoricRatio());
try {
memento.setTrackers(info.getTrackers());
} catch (URISyntaxException e) {
IOException ioe = new IOException();
ioe.initCause(e);
throw ioe;
}
return memento;
}
private BTDiskManagerMemento toBTDiskManagerMemento(SerialDiskManagerData data) {
BTDiskManagerMemento memento = new BTDiskManagerMementoImpl();
memento.setPartialBlocks(data.getPartialBlocks());
memento.setVerifiedBlocks(data.getVerifiedBlocks());
memento.setVerifying(data.isVerifying());
return memento;
}
private TorrentFileSystemMemento toFileSystemMemento(SerialTorrentFileSystem system) {
TorrentFileSystemMemento memento = new TorrentFileSystemMementoImpl();
memento.setCompleteFile(system.getCompleteFile());
memento.setFiles(system.getFiles());
memento.setFolders(system.getFolders());
memento.setIncompleteFile(system.getIncompleteFile());
memento.setName(system.getName());
memento.setTotalSize(system.getTotalSize());
return memento;
}
private List<Range> getRanges(File incompleteFile, SerialIncompleteFileManager ifm) {
List<Range> ranges = ifm.getBlocks().get(incompleteFile);
if(ranges != null) {
List<Range> fixedRanges = new ArrayList<Range>(ranges.size());
for(Range range : ranges) {
fixedRanges.add(Range.createRange(range.getLow(), range.getHigh() -1));
}
return fixedRanges;
}
return Collections.emptyList();
}
private File getIncompleteFile(SerialManagedDownloader download, SerialIncompleteFileManager sifm) {
URN sha1 = getSha1(download);
File incompleteFile = null;
if(download instanceof SerialResumeDownloader)
incompleteFile = ((SerialResumeDownloader)download).getIncompleteFile();
if(sha1 != null)
incompleteFile = sifm.getHashes().get(sha1);
if(incompleteFile == null) {
File saveFile = (File)download.getProperties().get("saveFile");
if(saveFile != null) {
String defaultName = (String)download.getProperties().get("defaultFileName");
if(defaultName != null) {
Category category = categoryManager.getCategoryForFilename(defaultName);
saveFile = new File(SharingSettings.getSaveDirectory(category), defaultName);
}
}
Number size = (Number)download.getProperties().get("fileSize");
if(download instanceof SerialResumeDownloader)
size = ((SerialResumeDownloader)download).getSize();
if (saveFile != null && size != null) {
String name = CommonUtils.convertFileName(saveFile.getName());
incompleteFile = new File(SharingSettings.INCOMPLETE_DIRECTORY.get(), "T-"
+ size.longValue() + "-" + name);
}
}
return incompleteFile;
}
private URN getSha1(SerialManagedDownloader download) {
URN sha1 = null;
if(download instanceof SerialMagnetDownloader)
sha1 = ((SerialMagnetDownloader)download).getUrn();
else if(download instanceof SerialResumeDownloader)
sha1 = ((SerialResumeDownloader)download).getUrn();
if(sha1 != null && !sha1.isSHA1())
sha1 = null;
for(SerialRemoteFileDesc rfd : download.getRemoteFileDescs()) {
if(sha1 != null)
break;
for(URN urn : rfd.getUrns()) {
if(urn.isSHA1()) {
sha1 = urn;
break;
}
}
}
return sha1;
}
private Set<RemoteHostMemento> convertToMementos(Set<SerialRemoteFileDesc> rfds) {
Set<RemoteHostMemento> mementos = new HashSet<RemoteHostMemento>(rfds.size());
for(SerialRemoteFileDesc rfd : rfds) {
try {
Address address = getAddress(rfd);
RemoteHostMemento memento = new RemoteHostMemento(address, rfd.getFilename(), rfd
.getIndex(), rfd.getClientGUID(), rfd.getSpeed(), rfd.getSize(), rfd.getQuality(), rfd.isReplyToMulticast(), rfd
.getXml(), rfd.getUrns(), rfd.isBrowseHostEnabled(), rfd.getVendor(),
rfd.isHttp11(), RemoteFileDescImpl.TYPE, addressFactory);
if(rfd instanceof SerialUrlRemoteFileDesc)
memento.setCustomUrl(((SerialUrlRemoteFileDesc)rfd).getUrl());
mementos.add(memento);
} catch (IOException ie) {
LOG.debug("", ie);
}
}
return mementos;
}
Address getAddress(SerialRemoteFileDesc rfd) throws IOException {
if (rfd.isFirewalled() ) {
if(rfd.getHttpPushAddr() != null) {
return pushEndpointFactory.createPushEndpoint(rfd.getHttpPushAddr());
} else {
// This is from very old versions, or versions that didn't have proxies.
// In this case, we still make it, but the address might be private, in which
// case the only useful bit of info is the clientGUID.
if (!RemoteFileDesc.BOGUS_IP.equals(rfd.getHost())) {
return pushEndpointFactory.createPushEndpoint(rfd.getClientGUID(), IpPort.EMPTY_SET, (byte)0, 0, new IpPortImpl(rfd.getHost(), rfd.getPort()));
} else {
return pushEndpointFactory.createPushEndpoint(rfd.getClientGUID());
}
}
} else {
return new ConnectableImpl(rfd.getHost(), rfd.getPort(), rfd.isTlsCapable());
}
}
}