X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fmain%2Fjava%2Fnet%2Fpterodactylus%2Fxdcc%2Fcore%2FCore.java;h=2c68c76e09ee43374a73b49139ac5a0bbc807577;hb=029ac191fbe3b39b323a76790ba1605a380e02e1;hp=0c66644be59e7d27ad2c73519a584061898ab77b;hpb=dca760d9a6b05c401fdb67729cdd43281bb5daf6;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 0c66644..2c68c76 100644 --- a/src/main/java/net/pterodactylus/xdcc/core/Core.java +++ b/src/main/java/net/pterodactylus/xdcc/core/Core.java @@ -34,13 +34,26 @@ 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.ChannelLeft; import net.pterodactylus.irc.event.ChannelMessageReceived; +import net.pterodactylus.irc.event.ClientQuit; import net.pterodactylus.irc.event.ConnectionEstablished; +import net.pterodactylus.irc.event.DccAcceptReceived; +import net.pterodactylus.irc.event.DccDownloadFailed; +import net.pterodactylus.irc.event.DccDownloadFinished; import net.pterodactylus.irc.event.DccSendReceived; +import net.pterodactylus.irc.event.NicknameChanged; +import net.pterodactylus.irc.event.PrivateMessageReceived; 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.core.event.GenericError; +import net.pterodactylus.xdcc.core.event.GenericMessage; +import net.pterodactylus.xdcc.core.event.MessageReceived; import net.pterodactylus.xdcc.data.Bot; import net.pterodactylus.xdcc.data.Channel; import net.pterodactylus.xdcc.data.Download; @@ -57,6 +70,7 @@ 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; @@ -98,7 +112,7 @@ public class Core extends AbstractIdleService { private final Map downloads = Maps.newHashMap(); /** The current DCC receivers. */ - private final Collection dccReceivers = Sets.newHashSet(); + private final Collection dccReceivers = Lists.newArrayList(); /** * Creates a new core. @@ -294,6 +308,90 @@ public class Core extends AbstractIdleService { } /** + * Removes bots that leave a channel, or channels when it’s us that’s leaving. + * + * @param channelLeft + * The channel left event + */ + @Subscribe + public void channelLeft(ChannelLeft channelLeft) { + Optional network = getNetwork(channelLeft.connection()); + if (!network.isPresent()) { + return; + } + + Bot bot = networkBots.get(network.get(), channelLeft.client().nick().get()); + if (bot == null) { + /* maybe it was us? */ + if (channelLeft.connection().isSource(channelLeft.client())) { + Optional channel = getChannel(network.get(), channelLeft.channel()); + if (!channel.isPresent()) { + /* maybe it was an extra channel? */ + channel = getExtraChannel(network.get(), channelLeft.channel()); + if (!channel.isPresent()) { + /* okay, whatever. */ + return; + } + + extraChannels.remove(channel); + } else { + channels.remove(channel.get()); + } + + eventBus.post(new GenericMessage(String.format("Left Channel %s on %s.", channel.get().name(), channel.get().network().name()))); + } + + return; + } + + Bot removedBot = networkBots.remove(network.get(), channelLeft.client().nick().get()); + if (removedBot != null) { + eventBus.post(new GenericMessage(String.format("Bot %s (%s) was removed, %d packs removed.", removedBot.name(), removedBot.network().name(), removedBot.packs().size()))); + } + } + + /** + * Removes a client (which may be a bot) from the table of known bots. + * + * @param clientQuit + * The client quit event + */ + @Subscribe + public void clientQuit(ClientQuit clientQuit) { + Optional network = getNetwork(clientQuit.connection()); + if (!network.isPresent()) { + return; + } + + Bot removedBot = networkBots.remove(network.get(), clientQuit.client().nick().get()); + if (removedBot != null) { + eventBus.post(new GenericMessage(String.format("Bot %s (%s) was removed, %d packs removed.", removedBot.name(), removedBot.network().name(), removedBot.packs().size()))); + } + } + + /** + * If the nickname of a bit changes, remove it from the old name and store it + * under the new name. + * + * @param nicknameChanged + * The nickname changed event + */ + @Subscribe + public void nicknameChanged(NicknameChanged nicknameChanged) { + Optional network = getNetwork(nicknameChanged.connection()); + if (!network.isPresent()) { + return; + } + + Bot bot = networkBots.remove(network.get(), nicknameChanged.client().nick().get()); + if (bot == null) { + return; + } + + networkBots.put(network.get(), nicknameChanged.newNickname(), bot); + } + + /** * If a message on a channel is received, it is parsed for pack information * with is then added to a bot. * @@ -337,6 +435,17 @@ public class Core extends AbstractIdleService { } /** + * Forward all private messages to every console. + * + * @param privateMessageReceived + * The private message recevied event + */ + @Subscribe + public void privateMessageReceived(PrivateMessageReceived privateMessageReceived) { + eventBus.post(new MessageReceived(privateMessageReceived.source(), privateMessageReceived.message())); + } + + /** * Starts a DCC download. * * @param dccSendReceived @@ -355,15 +464,130 @@ public class Core extends AbstractIdleService { return; } - logger.info(String.format("Starting download of %s.", dccSendReceived.filename())); + /* check if the file already exists. */ + File outputFile = new File(temporaryDirectory, dccSendReceived.filename()); + if (outputFile.exists()) { + long existingFileSize = outputFile.length(); + + /* file already complete? */ + if ((dccSendReceived.filesize() > -1) && (existingFileSize >= dccSendReceived.filesize())) { + /* file is apparently already complete. just move it. */ + if (outputFile.renameTo(new File(finalDirectory, download.pack().name()))) { + eventBus.post(new GenericMessage(String.format("File %s already downloaded.", download.pack().name()))); + } else { + eventBus.post(new GenericMessage(String.format("File %s already downloaded but not moved to %s.", download.pack().name(), finalDirectory))); + } + + /* remove download. */ + downloads.remove(download); + return; + } + + /* file not complete yet, DCC resume it. */ + try { + download.remoteAddress(dccSendReceived.inetAddress()).filesize(dccSendReceived.filesize()); + dccSendReceived.connection().sendDccResume(dccSendReceived.source().nick().get(), dccSendReceived.filename(), dccSendReceived.port(), existingFileSize); + } catch (IOException ioe1) { + eventBus.post(new GenericError(String.format("Could not send DCC RESUME %s to %s (%s).", dccSendReceived.filename(), dccSendReceived.source().nick().get(), ioe1.getMessage()))); + } + + return; + } + + /* file does not exist, start the download. */ 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); + eventBus.post(new GenericError(String.format("Could not start download of %s from %s (%s).", dccSendReceived.filename(), dccSendReceived.source().nick().get(), fnfe1.getMessage()))); + } + } + + @Subscribe + public void dccAcceptReceived(DccAcceptReceived dccAcceptReceived) { + Optional network = getNetwork(dccAcceptReceived.connection()); + if (!network.isPresent()) { + return; + } + + Download download = downloads.get(dccAcceptReceived.filename()); + if (download == null) { + /* unknown download, ignore. */ + return; + } + + 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); + return; + } + OutputStream outputStream = new FileOutputStream(outputFile, true); + DccReceiver dccReceiver = new DccReceiver(eventBus, download.remoteAddress(), dccAcceptReceived.port(), dccAcceptReceived.filename(), dccAcceptReceived.position(), download.filesize(), outputStream); + download.filename(outputFile.getPath()).outputStream(outputStream).dccReceiver(dccReceiver); + dccReceivers.add(dccReceiver); + dccReceiver.start(); + eventBus.post(new DownloadStarted(download)); + } catch (FileNotFoundException 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.pack().name())); + eventBus.post(new DownloadFinished(download)); + dccReceivers.remove(dccDownloadFinished.dccReceiver()); + downloads.remove(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)); + dccReceivers.remove(dccDownloadFailed.dccReceiver()); + downloads.remove(download); + } catch (IOException ioe1) { + /* swallow silently. */ } } @@ -401,7 +625,26 @@ public class Core extends AbstractIdleService { */ public Optional getChannel(Network network, String channelName) { for (Channel channel : channels) { - if (channel.network().equals(network) && (channel.name().equals(channelName))) { + if (channel.network().equals(network) && (channel.name().equalsIgnoreCase(channelName))) { + return Optional.of(channel); + } + } + return Optional.absent(); + } + + /** + * Returns the extra 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 extra channel, or {@link Optional#absent()} if no extra channel + * matching the given network and name was found + */ + public Optional getExtraChannel(Network network, String channelName) { + for (Channel channel : extraChannels) { + if (channel.network().equals(network) && (channel.name().equalsIgnoreCase(channelName))) { return Optional.of(channel); } }