X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fxdcc%2Fcore%2FCore.java;h=4314933af57cbc0ac15654d5f05f60a217fb3de6;hb=dce5a70668de45ae55cd8795cc0ec095bcf7152c;hp=8eea508514b4687bafcd54d6543fde17ec678470;hpb=1550de41bcf0afcb011e32ab4d737e766ae31618;p=xudocci.git diff --git a/src/main/java/net/pterodactylus/xdcc/core/Core.java b/src/main/java/net/pterodactylus/xdcc/core/Core.java index 8eea508..4314933 100644 --- a/src/main/java/net/pterodactylus/xdcc/core/Core.java +++ b/src/main/java/net/pterodactylus/xdcc/core/Core.java @@ -17,7 +17,11 @@ package net.pterodactylus.xdcc.core; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -29,26 +33,36 @@ import java.util.logging.Logger; import net.pterodactylus.irc.Connection; import net.pterodactylus.irc.ConnectionBuilder; import net.pterodactylus.irc.DccReceiver; +import net.pterodactylus.irc.event.ChannelJoined; import net.pterodactylus.irc.event.ChannelMessageReceived; import net.pterodactylus.irc.event.ConnectionEstablished; +import net.pterodactylus.irc.event.DccDownloadFailed; +import net.pterodactylus.irc.event.DccDownloadFinished; +import net.pterodactylus.irc.event.DccSendReceived; import net.pterodactylus.irc.util.MessageCleaner; import net.pterodactylus.irc.util.RandomNickname; import net.pterodactylus.xdcc.core.event.BotAdded; import net.pterodactylus.xdcc.core.event.CoreStarted; +import net.pterodactylus.xdcc.core.event.DownloadFailed; +import net.pterodactylus.xdcc.core.event.DownloadFinished; +import net.pterodactylus.xdcc.core.event.DownloadStarted; import net.pterodactylus.xdcc.data.Bot; import net.pterodactylus.xdcc.data.Channel; +import net.pterodactylus.xdcc.data.Download; import net.pterodactylus.xdcc.data.Network; import net.pterodactylus.xdcc.data.Pack; import net.pterodactylus.xdcc.data.Server; import com.google.common.base.Optional; import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; +import com.google.common.io.Closeables; import com.google.common.util.concurrent.AbstractIdleService; import com.google.inject.Inject; @@ -65,15 +79,30 @@ public class Core extends AbstractIdleService { /** The event bus. */ private final EventBus eventBus; + /** The temporary directory to download files to. */ + private final String temporaryDirectory; + + /** The directory to move finished downloads to. */ + private final String finalDirectory; + /** The channels that should be monitored. */ private final Collection channels = Sets.newHashSet(); + /** The channels that are currentlymonitored. */ + private final Collection joinedChannels = Sets.newHashSet(); + + /** The channels that are joined but not configured. */ + private final Collection extraChannels = Sets.newHashSet(); + /** The current network connections. */ private final Map networkConnections = Collections.synchronizedMap(Maps.newHashMap()); /** The currently known bots. */ private final Table networkBots = HashBasedTable.create(); + /** The current downloads. */ + private final Map downloads = Maps.newHashMap(); + /** The current DCC receivers. */ private final Collection dccReceivers = Sets.newHashSet(); @@ -82,10 +111,16 @@ public class Core extends AbstractIdleService { * * @param eventBus * The event bus + * @param temporaryDirectory + * The directory to download files to + * @param finalDirectory + * The directory to move finished files to */ @Inject - public Core(EventBus eventBus) { + public Core(EventBus eventBus, String temporaryDirectory, String finalDirectory) { this.eventBus = eventBus; + this.temporaryDirectory = temporaryDirectory; + this.finalDirectory = finalDirectory; } // @@ -93,6 +128,34 @@ public class Core extends AbstractIdleService { // /** + * Returns all configured channels. Due to various circumstances, configured + * channels might not actually be joined. + * + * @return All configured channels + */ + public Collection channels() { + return ImmutableSet.copyOf(channels); + } + + /** + * Returns all currently joined channels. + * + * @return All currently joined channels + */ + public Collection joinedChannels() { + return ImmutableSet.copyOf(joinedChannels); + } + + /** + * Returns all currently joined channels that are not configured. + * + * @return All currently joined but not configured channels + */ + public Collection extraChannels() { + return ImmutableSet.copyOf(extraChannels); + } + + /** * Returns all currently known bots. * * @return All currently known bots @@ -124,6 +187,30 @@ public class Core extends AbstractIdleService { channels.add(channel); } + /** + * Fetches the given pack from the given bot. + * + * @param bot + * The bot to fetch the pack from + * @param pack + * The pack to fetch + */ + public void fetch(Bot bot, Pack pack) { + Connection connection = networkConnections.get(bot.network()); + if (connection == null) { + return; + } + + Download download = new Download(bot, pack); + downloads.put(pack.name(), download); + + try { + connection.sendMessage(bot.name(), "XDCC SEND " + pack.id()); + } catch (IOException ioe1) { + logger.log(Level.WARNING, "Could not send message to bot!", ioe1); + } + } + // // ABSTRACTIDLESERVICE METHODS // @@ -186,6 +273,33 @@ public class Core extends AbstractIdleService { } /** + * Shows a message when a channel was joined by us. + * + * @param channelJoined + * The channel joined event + */ + @Subscribe + public void channelJoined(ChannelJoined channelJoined) { + if (channelJoined.connection().isSource(channelJoined.client())) { + Optional network = getNetwork(channelJoined.connection()); + if (!network.isPresent()) { + return; + } + + Optional channel = getChannel(network.get(), channelJoined.channel()); + if (!channel.isPresent()) { + /* it’s an extra channel. */ + extraChannels.add(new Channel(network.get(), channelJoined.channel())); + logger.info(String.format("Joined extra Channel %s on %s.", channelJoined.channel(), network.get().name())); + return; + } + + joinedChannels.add(channel.get()); + logger.info(String.format("Joined Channel %s on %s.", channelJoined.channel(), network.get().name())); + } + } + + /** * If a message on a channel is received, it is parsed for pack information * with is then added to a bot. * @@ -228,6 +342,87 @@ public class Core extends AbstractIdleService { logger.fine(String.format("Bot %s now has %d packs.", bot, bot.packs().size())); } + /** + * Starts a DCC download. + * + * @param dccSendReceived + * The DCC SEND event + */ + @Subscribe + public void dccSendReceived(DccSendReceived dccSendReceived) { + Optional network = getNetwork(dccSendReceived.connection()); + if (!network.isPresent()) { + return; + } + + Download download = downloads.get(dccSendReceived.filename()); + if (download == null) { + /* unknown download, ignore. */ + return; + } + + logger.info(String.format("Starting download of %s.", dccSendReceived.filename())); + try { + File outputFile = new File(temporaryDirectory, dccSendReceived.filename()); + OutputStream fileOutputStream = new FileOutputStream(outputFile); + DccReceiver dccReceiver = new DccReceiver(eventBus, dccSendReceived.inetAddress(), dccSendReceived.port(), dccSendReceived.filename(), dccSendReceived.filesize(), fileOutputStream); + download.filename(outputFile.getPath()).outputStream(fileOutputStream).dccReceiver(dccReceiver); + dccReceivers.add(dccReceiver); + dccReceiver.start(); + eventBus.post(new DownloadStarted(download)); + } catch (FileNotFoundException fnfe1) { + logger.log(Level.WARNING, "Could not open file for download!", fnfe1); + } + } + + /** + * Closes the output stream of the download and moves the file to the final + * location. + * + * @param dccDownloadFinished + * The DCC download finished event + */ + @Subscribe + public void dccDownloadFinished(DccDownloadFinished dccDownloadFinished) { + Download download = downloads.get(dccDownloadFinished.dccReceiver().filename()); + if (download == null) { + /* probably shouldn’t happen. */ + return; + } + + try { + download.outputStream().close(); + File file = new File(download.filename()); + file.renameTo(new File(finalDirectory, download.filename())); + eventBus.post(new DownloadFinished(download)); + } 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); + } + } + + /** + * Closes the output stream and notifies all listeners of the failure. + * + * @param dccDownloadFailed + * The DCC download failed event + */ + @Subscribe + public void dccDownloadFailed(DccDownloadFailed dccDownloadFailed) { + Download download = downloads.get(dccDownloadFailed.dccReceiver().filename()); + if (download == null) { + /* probably shouldn’t happen. */ + return; + } + + try { + Closeables.close(download.outputStream(), true); + eventBus.post(new DownloadFailed(download)); + } catch (IOException ioe1) { + /* swallow silently. */ + } + } + // // PRIVATE METHODS // @@ -251,6 +446,25 @@ public class Core extends AbstractIdleService { } /** + * Returns the configured channel for the given network and name. + * + * @param network + * The network the channel is located on + * @param channelName + * The name of the channel + * @return The configured channel, or {@link Optional#absent()} if no + * configured channel matching the given network and name was found + */ + public Optional getChannel(Network network, String channelName) { + for (Channel channel : channels) { + if (channel.network().equals(network) && (channel.name().equals(channelName))) { + return Optional.of(channel); + } + } + return Optional.absent(); + } + + /** * Parses {@link Pack} information from the given message. * * @param message