package net.pterodactylus.xdcc.core;
+import static net.pterodactylus.xdcc.data.Download.FILTER_RUNNING;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import net.pterodactylus.irc.event.DccDownloadFinished;
import net.pterodactylus.irc.event.DccSendReceived;
import net.pterodactylus.irc.event.NicknameChanged;
-import net.pterodactylus.irc.event.PrivateNoticeReceived;
import net.pterodactylus.irc.event.PrivateMessageReceived;
+import net.pterodactylus.irc.event.PrivateNoticeReceived;
import net.pterodactylus.irc.util.MessageCleaner;
import net.pterodactylus.irc.util.RandomNickname;
import net.pterodactylus.xdcc.core.event.BotAdded;
import net.pterodactylus.xdcc.data.Server;
import com.google.common.base.Optional;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashBasedTable;
+import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.eventbus.EventBus;
private final Table<Network, String, Bot> networkBots = HashBasedTable.create();
/** The current downloads. */
- private final Map<String, Download> downloads = Maps.newHashMap();
+ private final Multimap<String, Download> downloads = HashMultimap.create();
/** The current DCC receivers. */
private final Collection<DccReceiver> dccReceivers = Lists.newArrayList();
//
/**
+ * Returns all currently known connections.
+ *
+ * @return All currently known connections
+ */
+ public Collection<Connection> connections() {
+ return networkConnections.values();
+ }
+
+ /**
* Returns all configured channels. Due to various circumstances, configured
* channels might not actually be joined.
*
}
/**
- * Returns the currently active DCC receivers.
+ * Returns all currently running downloads.
*
- * @return The currently active DCC receivers
+ * @return All currently running downloads
*/
- public Collection<DccReceiver> dccReceivers() {
- return dccReceivers;
+ public Collection<Download> downloads() {
+ return downloads.values();
}
//
return;
}
+ /* check if we are already downloading the file? */
+ if (downloads.containsKey(pack.name())) {
+ Collection<Download> packDownloads = downloads.get(pack.name());
+ Collection<Download> runningDownloads = FluentIterable.from(packDownloads).filter(FILTER_RUNNING).toSet();
+ if (!runningDownloads.isEmpty()) {
+ Download download = runningDownloads.iterator().next();
+ eventBus.post(new GenericMessage(String.format("File %s is already downloading from %s (%s).", pack.name(), download.bot().name(), download.bot().network().name())));
+ return;
+ }
+ StringBuilder bots = new StringBuilder();
+ for (Download download : packDownloads) {
+ if (bots.length() > 0) {
+ bots.append(", ");
+ }
+ bots.append(download.bot().name()).append(" (").append(download.bot().network().name()).append(')');
+ }
+ eventBus.post(new GenericMessage(String.format("File %s is already requested from %d bots (%s).", pack.name(), packDownloads.size(), bots.toString())));
+ }
+
Download download = new Download(bot, pack);
downloads.put(pack.name(), download);
}
}
+ /**
+ * Closes the given connection.
+ *
+ * @param connection
+ * The connection to close
+ */
+ public void closeConnection(Connection connection) {
+ try {
+ connection.close();
+ } catch (IOException ioe1) {
+ /* TODO */
+ }
+ }
+
//
// ABSTRACTIDLESERVICE METHODS
//
Bot bot;
synchronized (networkBots) {
if (!networkBots.contains(network.get(), channelMessageReceived.source().nick().get())) {
- bot = new Bot(network.get()).name(channelMessageReceived.source().nick().get());
+ bot = new Bot(network.get(), channelMessageReceived.source().nick().get());
networkBots.put(network.get(), channelMessageReceived.source().nick().get(), bot);
eventBus.post(new BotAdded(bot));
} else {
return;
}
- eventBus.post(new GenericMessage(String.format("Notice from %s (%s): %s", privateNoticeReceived.reply().source(), network.get(), privateNoticeReceived.text())));
+ eventBus.post(new GenericMessage(String.format("Notice from %s (%s): %s", privateNoticeReceived.reply().source().get(), network.get(), privateNoticeReceived.text())));
}
/**
* The DCC SEND event
*/
@Subscribe
- public void dccSendReceived(DccSendReceived dccSendReceived) {
- Optional<Network> network = getNetwork(dccSendReceived.connection());
+ public void dccSendReceived(final DccSendReceived dccSendReceived) {
+ final Optional<Network> network = getNetwork(dccSendReceived.connection());
if (!network.isPresent()) {
return;
}
- Download download = downloads.get(dccSendReceived.filename());
- if (download == null) {
+ Collection<Download> packDownloads = downloads.get(dccSendReceived.filename());
+ if (packDownloads.isEmpty()) {
/* unknown download, ignore. */
return;
}
+ /* check if it’s already downloading. */
+ Collection<Download> runningDownloads = FluentIterable.from(packDownloads).filter(FILTER_RUNNING).toSet();
+ if (!runningDownloads.isEmpty()) {
+ eventBus.post(new GenericMessage(String.format("Ignoring offer for %s, it’s already being downloaded.", dccSendReceived.filename())));
+ return;
+ }
+
+ /* locate the correct download. */
+ Collection<Download> requestedDownload = FluentIterable.from(packDownloads).filter(new Predicate<Download>() {
+
+ @Override
+ public boolean apply(Download download) {
+ return download.bot().network().equals(network.get()) && download.bot().name().equalsIgnoreCase(dccSendReceived.source().nick().get());
+ }
+ }).toSet();
+
+ /* we did not request this download. */
+ if (requestedDownload.isEmpty()) {
+ return;
+ }
+
+ Download download = requestedDownload.iterator().next();
+
/* check if the file already exists. */
File outputFile = new File(temporaryDirectory, dccSendReceived.filename());
if (outputFile.exists()) {
}
/* remove download. */
- downloads.remove(download);
+ downloads.removeAll(download.pack().name());
return;
}
}
@Subscribe
- public void dccAcceptReceived(DccAcceptReceived dccAcceptReceived) {
- Optional<Network> network = getNetwork(dccAcceptReceived.connection());
+ public void dccAcceptReceived(final DccAcceptReceived dccAcceptReceived) {
+ final Optional<Network> network = getNetwork(dccAcceptReceived.connection());
if (!network.isPresent()) {
return;
}
- Download download = downloads.get(dccAcceptReceived.filename());
- if (download == null) {
+ Collection<Download> packDownloads = downloads.get(dccAcceptReceived.filename());
+ if (packDownloads.isEmpty()) {
/* unknown download, ignore. */
return;
}
+ /* check if it’s already downloading. */
+ Collection<Download> runningDownloads = FluentIterable.from(packDownloads).filter(FILTER_RUNNING).toSet();
+ if (!runningDownloads.isEmpty()) {
+ eventBus.post(new GenericMessage(String.format("Ignoring offer for %s, it’s already being downloaded.", dccAcceptReceived.filename())));
+ return;
+ }
+
+ /* locate the correct download. */
+ Collection<Download> requestedDownload = FluentIterable.from(packDownloads).filter(new Predicate<Download>() {
+
+ @Override
+ public boolean apply(Download download) {
+ return download.bot().network().equals(network.get()) && download.bot().name().equalsIgnoreCase(dccAcceptReceived.source().nick().get());
+ }
+ }).toSet();
+
+ /* we did not request this download. */
+ if (requestedDownload.isEmpty()) {
+ return;
+ }
+
+ Download download = requestedDownload.iterator().next();
+
try {
File outputFile = new File(temporaryDirectory, dccAcceptReceived.filename());
if (outputFile.length() != dccAcceptReceived.position()) {
eventBus.post(new GenericError(String.format("Download %s from %s does not start at the right position!")));
logger.log(Level.WARNING, String.format("Download %s from %s: have %d bytes but wants to resume from %d!", dccAcceptReceived.filename(), dccAcceptReceived.source(), outputFile.length(), dccAcceptReceived.position()));
- downloads.remove(download);
+ downloads.removeAll(download.pack().name());
return;
}
OutputStream outputStream = new FileOutputStream(outputFile, true);
*/
@Subscribe
public void dccDownloadFinished(DccDownloadFinished dccDownloadFinished) {
- Download download = downloads.get(dccDownloadFinished.dccReceiver().filename());
- if (download == null) {
- /* probably shouldn’t happen. */
+
+ /* locate the correct download. */
+ Collection<Download> requestedDownload = FluentIterable.from(downloads.get(dccDownloadFinished.dccReceiver().filename())).filter(FILTER_RUNNING).toSet();
+ if (requestedDownload.isEmpty()) {
+ /* this seems wrong. */
+ logger.warning("Download finished but could not be located.");
return;
}
+ Download download = requestedDownload.iterator().next();
try {
download.outputStream().close();
file.renameTo(new File(finalDirectory, download.pack().name()));
eventBus.post(new DownloadFinished(download));
dccReceivers.remove(dccDownloadFinished.dccReceiver());
- downloads.remove(download);
+ downloads.removeAll(download.pack().name());
} catch (IOException ioe1) {
/* TODO - handle all the errors. */
logger.log(Level.WARNING, String.format("Could not move file %s to directory %s.", download.filename(), finalDirectory), ioe1);
*/
@Subscribe
public void dccDownloadFailed(DccDownloadFailed dccDownloadFailed) {
- Download download = downloads.get(dccDownloadFailed.dccReceiver().filename());
- if (download == null) {
- /* probably shouldn’t happen. */
+
+ /* locate the correct download. */
+ Collection<Download> requestedDownload = FluentIterable.from(downloads.get(dccDownloadFailed.dccReceiver().filename())).filter(FILTER_RUNNING).toSet();
+ if (requestedDownload.isEmpty()) {
+ /* this seems wrong. */
+ logger.warning("Download finished but could not be located.");
return;
}
+ Download download = requestedDownload.iterator().next();
try {
Closeables.close(download.outputStream(), true);
eventBus.post(new DownloadFailed(download));
dccReceivers.remove(dccDownloadFailed.dccReceiver());
- downloads.remove(download);
+ downloads.removeAll(download.pack().name());
} catch (IOException ioe1) {
/* swallow silently. */
}