package org.limewire.core.impl.download.listener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import org.limewire.bittorrent.BTData;
import org.limewire.bittorrent.BTDataImpl;
import org.limewire.bittorrent.TorrentManager;
import org.limewire.bittorrent.bencoding.Token;
import org.limewire.core.api.download.DownloadAction;
import org.limewire.core.api.download.DownloadItem;
import org.limewire.core.api.download.DownloadException;
import org.limewire.core.settings.SharingSettings;
import org.limewire.listener.EventListener;
import org.limewire.logging.Log;
import org.limewire.logging.LogFactory;
import org.limewire.util.FileUtils;
import org.limewire.util.Objects;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.limegroup.bittorrent.BTTorrentFileDownloader;
import com.limegroup.gnutella.ActivityCallback;
import com.limegroup.gnutella.DownloadManager;
import com.limegroup.gnutella.Downloader;
import com.limegroup.gnutella.Downloader.DownloadState;
import com.limegroup.gnutella.downloader.CoreDownloader;
import com.limegroup.gnutella.downloader.DownloadStateEvent;
import com.limegroup.gnutella.library.FileCollection;
import com.limegroup.gnutella.library.GnutellaFiles;
/**
* Listens for downloads of .torrent files to complete. When the download
* finishes then the torrent download will be started.
*/
public class TorrentDownloadListener implements EventListener<DownloadStateEvent> {
private static final Log LOG = LogFactory.getLog(TorrentDownloadListener.class);
private final Downloader downloader;
private final DownloadManager downloadManager;
private final ActivityCallback activityCallback;
private final List<DownloadItem> downloadItems;
private final FileCollection gnutellaFileList;
private final TorrentManager torrentManager;
@Inject
public TorrentDownloadListener(DownloadManager downloadManager,
ActivityCallback activityCallback, @GnutellaFiles FileCollection gnutellaFileList,
TorrentManager torrentManager, @Assisted List<DownloadItem> downloadItems,
@Assisted Downloader downloader) {
this.downloader = Objects.nonNull(downloader, "downloader");
this.downloadManager = Objects.nonNull(downloadManager, "downloadManager");
this.torrentManager = Objects.nonNull(torrentManager, "torrentManager");
this.gnutellaFileList = Objects.nonNull(gnutellaFileList, "gnutellaFileList");
this.activityCallback = Objects.nonNull(activityCallback, "activityCallback");
this.downloadItems = Objects.nonNull(downloadItems, "downloadItems");
if (downloader.getState() == DownloadState.COMPLETE) {
if (downloader instanceof CoreDownloader) {
handleEvent(new DownloadStateEvent((CoreDownloader) downloader,
DownloadState.COMPLETE));
}
}
}
@Override
public void handleEvent(DownloadStateEvent event) {
DownloadState downloadStatus = event.getType();
if (DownloadState.COMPLETE == downloadStatus) {
if (downloader instanceof BTTorrentFileDownloader) {
handleBTTorrentFileDownloader();
} else {
handleCoreDownloader();
}
}
}
private void handleCoreDownloader() {
File possibleTorrentFile = null;
possibleTorrentFile = downloader.getSaveFile();
String fileExtension = FileUtils.getFileExtension(possibleTorrentFile);
if ("torrent".equalsIgnoreCase(fileExtension)) {
try {
shareTorrentFile(possibleTorrentFile);
downloadManager.downloadTorrent(possibleTorrentFile, null, false);
downloadItems.remove(getDownloadItem(downloader));
} catch (DownloadException e) {
final File torrentFile = possibleTorrentFile;
activityCallback.handleDownloadException(new DownloadAction() {
@Override
public void download(File saveDirectory, boolean overwrite)
throws DownloadException {
downloadManager.downloadTorrent(torrentFile, saveDirectory, overwrite);
downloadItems.remove(getDownloadItem(downloader));
}
@Override
public void downloadCanceled(DownloadException ignored) {
// nothing to do
}
}, e, false);
}
}
}
private void handleBTTorrentFileDownloader() {
File torrentFile = null;
final BTTorrentFileDownloader btTorrentFileDownloader = (BTTorrentFileDownloader) downloader;
try {
torrentFile = btTorrentFileDownloader.getTorrentFile();
shareTorrentFile(torrentFile);
downloadManager.downloadTorrent(torrentFile, null, false);
downloadItems.remove(getDownloadItem(downloader));
} catch (DownloadException e) {
final File torrentFileFinal = torrentFile;
activityCallback.handleDownloadException(new DownloadAction() {
@Override
public void download(File saveDirectory, boolean overwrite) throws DownloadException {
downloadManager.downloadTorrent(torrentFileFinal, saveDirectory, overwrite);
downloadItems.remove(getDownloadItem(downloader));
}
@Override
public void downloadCanceled(DownloadException ex) {
if (!torrentManager.isDownloadingTorrent(torrentFileFinal)) {
// need to delete to clean up the torrent file in the
// incomplete directory
FileUtils.forceDelete(torrentFileFinal);
}
}
}, e, false);
}
}
DownloadItem getDownloadItem(Downloader downloader) {
DownloadItem item = (DownloadItem) downloader.getAttribute(DownloadItem.DOWNLOAD_ITEM);
return item;
}
private File getSharedTorrentMetaDataFile(BTData btData) {
String fileName = btData.getName().concat(".torrent");
File f = new File(SharingSettings.getSaveDirectory(), fileName);
return f;
}
/**
* Returns true if the code was executed correctly. False if there was an
* error trying to share the file. If the file was not supposed to be
* shared, and was not shared, true would still be returned.
*/
private boolean shareTorrentFile(File torrentFile) {
if (torrentManager.isDownloadingTorrent(torrentFile)) {
return true;
}
if (!SharingSettings.SHARE_DOWNLOADED_FILES_IN_NON_SHARED_DIRECTORIES.getValue()) {
return true;
}
BTData btData = null;
FileInputStream torrentInputStream = null;
try {
torrentInputStream = new FileInputStream(torrentFile);
Map<?, ?> torrentFileMap = (Map<?, ?>) Token.parse(torrentInputStream.getChannel());
btData = new BTDataImpl(torrentFileMap);
} catch (IOException e) {
LOG.error("Error reading torrent file: " + torrentFile, e);
return false;
} finally {
FileUtils.close(torrentInputStream);
}
if (btData.isPrivate()) {
gnutellaFileList.remove(torrentFile);
return true;
}
File saveDir = SharingSettings.getSaveDirectory();
File torrentParent = torrentFile.getParentFile();
if (torrentParent.equals(saveDir)) {
// already in saveDir
gnutellaFileList.add(torrentFile);
return true;
}
final File tFile = getSharedTorrentMetaDataFile(btData);
if (tFile.equals(torrentFile)) {
gnutellaFileList.add(tFile);
return true;
}
gnutellaFileList.remove(tFile);
File backup = null;
if (tFile.exists()) {
backup = new File(tFile.getParent(), tFile.getName().concat(".bak"));
FileUtils.forceRename(tFile, backup);
}
if (FileUtils.copy(torrentFile, tFile)) {
gnutellaFileList.add(tFile);
} else {
if (backup != null) {
// restore backup
if (FileUtils.forceRename(backup, tFile)) {
gnutellaFileList.add(tFile);
}
}
}
return true;
}
}