From 1f642f9d331f87eaf5d85295480dc20b05b71d63 Mon Sep 17 00:00:00 2001 From: Kenzie Togami Date: Thu, 15 Sep 2016 20:50:16 -0700 Subject: [PATCH] mbaxter edition --- .../org/kitteh/irc/client/library/Client.java | 7 ++- .../irc/client/library/element/DCCChat.java | 4 +- .../client/library/element/DCCExchange.java | 14 ++---- .../irc/client/library/element/User.java | 11 +++-- .../library/event/user/DCCRequestEvent.java | 47 +++++++++++++++++++ .../library/implementation/ActorProvider.java | 3 ++ .../library/implementation/EventListener.java | 28 +++++++++-- .../library/implementation/NettyManager.java | 14 ++++-- 8 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 src/main/java/org/kitteh/irc/client/library/event/user/DCCRequestEvent.java diff --git a/src/main/java/org/kitteh/irc/client/library/Client.java b/src/main/java/org/kitteh/irc/client/library/Client.java index b27e38bb0..36f08158a 100644 --- a/src/main/java/org/kitteh/irc/client/library/Client.java +++ b/src/main/java/org/kitteh/irc/client/library/Client.java @@ -31,6 +31,7 @@ import org.kitteh.irc.client.library.event.client.ClientConnectedEvent; import org.kitteh.irc.client.library.event.user.DCCConnectedEvent; import org.kitteh.irc.client.library.event.user.DCCFailedEvent; +import org.kitteh.irc.client.library.event.user.DCCSocketBoundEvent; import org.kitteh.irc.client.library.event.user.PrivateCTCPQueryEvent; import org.kitteh.irc.client.library.feature.AuthManager; import org.kitteh.irc.client.library.feature.CapabilityManager; @@ -835,8 +836,10 @@ default void sendMultiLineNotice(@Nonnull MessageReceiver target, @Nonnull Strin /** * Sends a DCC CHAT request to the target. * - *

When the chat is connected, a {@link DCCConnectedEvent} will be fired. If the connection fails, - * a {@link DCCFailedEvent} will be fired.

+ *

When the server socket is bound locally, a + * {@link DCCSocketBoundEvent} will be fired. When the chat is connected, + * a {@link DCCConnectedEvent} will be fired. If the connection fails, a + * {@link DCCFailedEvent} will be fired.

*/ void requestDCCChat(@Nonnull User target); } diff --git a/src/main/java/org/kitteh/irc/client/library/element/DCCChat.java b/src/main/java/org/kitteh/irc/client/library/element/DCCChat.java index 2a501d0c7..5764da31e 100644 --- a/src/main/java/org/kitteh/irc/client/library/element/DCCChat.java +++ b/src/main/java/org/kitteh/irc/client/library/element/DCCChat.java @@ -29,8 +29,8 @@ public interface DCCChat extends DCCExchange { /** * Sends the user a message over DCC chat. * - *

This method won't return until the chat is connected and the message has - * been sent.

+ *

This method won't work if the chat is not + * {@link #isConnected() connected}.

* * @param message the message to send */ diff --git a/src/main/java/org/kitteh/irc/client/library/element/DCCExchange.java b/src/main/java/org/kitteh/irc/client/library/element/DCCExchange.java index 1b3377818..4a6259f26 100644 --- a/src/main/java/org/kitteh/irc/client/library/element/DCCExchange.java +++ b/src/main/java/org/kitteh/irc/client/library/element/DCCExchange.java @@ -31,30 +31,24 @@ */ public interface DCCExchange extends Actor { /** - * Gets the socket address of the local end. - * May return {@link Optional#empty()} if not connected. - * * @return the socket address of the local end */ Optional getLocalSocketAddress(); /** - * Gets the socket address of the remote end. - * May return {@link Optional#empty()} if not connected. - * * @return the socket address of the remote end */ Optional getRemoteSocketAddress(); /** - * Gets the connection status. - * - * @return {@code true} if the exchange is connected, otherwise false + * @return {@code true} if the exchange is connected, otherwise + * {@code false} */ boolean isConnected(); /** - * Closes this DCC chat. + * Closes this DCC exchange. It will no longer be + * {@link #isConnected() connected}. */ void close(); } diff --git a/src/main/java/org/kitteh/irc/client/library/element/User.java b/src/main/java/org/kitteh/irc/client/library/element/User.java index 46c9786eb..885cbcc51 100644 --- a/src/main/java/org/kitteh/irc/client/library/element/User.java +++ b/src/main/java/org/kitteh/irc/client/library/element/User.java @@ -26,6 +26,7 @@ import org.kitteh.irc.client.library.Client; import org.kitteh.irc.client.library.event.user.DCCConnectedEvent; import org.kitteh.irc.client.library.event.user.DCCFailedEvent; +import org.kitteh.irc.client.library.event.user.DCCSocketBoundEvent; import org.kitteh.irc.client.library.feature.CapabilityManager; import org.kitteh.irc.client.library.feature.ServerInfo; @@ -104,16 +105,18 @@ public interface User extends MessageReceiver, Staleable { */ boolean isAway(); + /** - * Sends a DCC CHAT request to this user. + * Sends a DCC CHAT request to the target. * - *

When the chat is connected, a {@link DCCConnectedEvent} will be fired. If the connection fails, - * a {@link DCCFailedEvent} will be fired.

+ *

When the server socket is bound locally, a + * {@link DCCSocketBoundEvent} will be fired. When the chat is connected, + * a {@link DCCConnectedEvent} will be fired. If the connection fails, a + * {@link DCCFailedEvent} will be fired.

* * @see Client#requestDCCChat(User) */ default void requestDCCChat() { this.getClient().requestDCCChat(this); } - } diff --git a/src/main/java/org/kitteh/irc/client/library/event/user/DCCRequestEvent.java b/src/main/java/org/kitteh/irc/client/library/event/user/DCCRequestEvent.java new file mode 100644 index 000000000..903802bfb --- /dev/null +++ b/src/main/java/org/kitteh/irc/client/library/event/user/DCCRequestEvent.java @@ -0,0 +1,47 @@ +package org.kitteh.irc.client.library.event.user; + +import org.kitteh.irc.client.library.Client; +import org.kitteh.irc.client.library.element.ServerMessage; +import org.kitteh.irc.client.library.element.User; +import org.kitteh.irc.client.library.event.abstractbase.ActorEventBase; +import org.kitteh.irc.client.library.util.Sanity; + +import javax.annotation.Nonnull; +import java.util.List; + +public class DCCRequestEvent extends ActorEventBase { + private final String type; + private final String ip; + private final int port; + + public DCCRequestEvent(@Nonnull Client client, @Nonnull List originalMessages, @Nonnull String type, @Nonnull String ip, int port, @Nonnull User actor) { + super(client, originalMessages, actor); + Sanity.nullCheck(type, "type cannot be null"); + Sanity.nullCheck(ip, "ip cannot be null"); + this.type = type; + this.ip = ip; + this.port = port; + } + + public String getType() { + return this.type; + } + + public String getIp() { + return this.ip; + } + + public int getPort() { + return this.port; + } + + /** + * Accepts the request and connect to the socket. + * + *

This will trigger {@link DCCConnectedEvent} if successful and + * {@link DCCFailedEvent} otherwise.

+ */ + public void accept() { + + } +} diff --git a/src/main/java/org/kitteh/irc/client/library/implementation/ActorProvider.java b/src/main/java/org/kitteh/irc/client/library/implementation/ActorProvider.java index 00207843e..82c7cbe54 100644 --- a/src/main/java/org/kitteh/irc/client/library/implementation/ActorProvider.java +++ b/src/main/java/org/kitteh/irc/client/library/implementation/ActorProvider.java @@ -813,6 +813,9 @@ private IRCDCCChatSnapshot(@Nonnull IRCDCCChat actor) { @Override public void sendMessage(@Nonnull String message) { Sanity.nullCheck(message, "message cannot be null"); + if (this.nettyChannel == null) { + return; + } this.nettyChannel.writeAndFlush(message.trim()); } } diff --git a/src/main/java/org/kitteh/irc/client/library/implementation/EventListener.java b/src/main/java/org/kitteh/irc/client/library/implementation/EventListener.java index b39349ffd..efc1e3617 100644 --- a/src/main/java/org/kitteh/irc/client/library/implementation/EventListener.java +++ b/src/main/java/org/kitteh/irc/client/library/implementation/EventListener.java @@ -73,6 +73,7 @@ import org.kitteh.irc.client.library.event.helper.ClientEvent; import org.kitteh.irc.client.library.event.helper.ClientReceiveServerMessageEvent; import org.kitteh.irc.client.library.event.helper.MonitoredNickStatusEvent; +import org.kitteh.irc.client.library.event.user.DCCRequestEvent; import org.kitteh.irc.client.library.event.user.MonitoredNickListEvent; import org.kitteh.irc.client.library.event.user.MonitoredNickListFullEvent; import org.kitteh.irc.client.library.event.user.MonitoredNickOfflineEvent; @@ -978,11 +979,7 @@ public void ctcp(ClientReceiveCommandEvent event) { reply = "FINGER om nom nom tasty finger"; break; case "DCC": - // Handle targeted DCC chat - // this.fire(new DCCRequestEvent()); - // TODO how can the parameters be extracted @mbaxter??? - // I only have DCC of the following: - // DCC + handleDccEvent(user, event.getOriginalMessages(), event.getParameters()); break; } if (ctcpMessage.startsWith("PING ")) { @@ -1005,6 +1002,27 @@ public void ctcp(ClientReceiveCommandEvent event) { } } + private void handleDccEvent(User user, List originalMessages, List parameters) { + String dccType = CTCPUtil.fromCTCP(parameters.get(2)); + if (dccType.equals("CHAT")) { + String chatType = CTCPUtil.fromCTCP(parameters.get(3)); + if (!chatType.equals("chat")) { + return; + } + String ip = CTCPUtil.fromCTCP(parameters.get(4)); + String port = CTCPUtil.fromCTCP(parameters.get(5)); + int portInt; + try { + portInt = Integer.parseInt(port); + } catch (NumberFormatException invalidPort) { + return; + } + fire(new DCCRequestEvent(this.client, originalMessages, dccType, ip, portInt, user)); + } else { + // do logging + } + } + @CommandFilter("MODE") @Handler(priority = Integer.MAX_VALUE - 1) public void mode(ClientReceiveCommandEvent event) { diff --git a/src/main/java/org/kitteh/irc/client/library/implementation/NettyManager.java b/src/main/java/org/kitteh/irc/client/library/implementation/NettyManager.java index a3f86e338..0d79ce587 100644 --- a/src/main/java/org/kitteh/irc/client/library/implementation/NettyManager.java +++ b/src/main/java/org/kitteh/irc/client/library/implementation/NettyManager.java @@ -306,11 +306,9 @@ public String toString() { } static class DCCConnection extends ChannelInitializer { - private final IRCDCCExchange exchange; private final InternalClient client; - // Only allow one connection per DCC. Weird, I know. - private boolean oneConnection; + private int connectionsMade; private DCCConnection(IRCDCCExchange ex, InternalClient client) { this.exchange = ex; @@ -319,11 +317,12 @@ private DCCConnection(IRCDCCExchange ex, InternalClient client) { @Override public void initChannel(SocketChannel channel) throws Exception { - if (this.oneConnection) { + if (this.connectionsMade > 0) { + // Only one connection is allowed. channel.close(); return; } - this.oneConnection = true; + this.connectionsMade++; this.exchange.setNettyChannel(channel); dccConnections.computeIfAbsent(this.client, c -> new ArrayList<>()).add(channel); @@ -384,6 +383,11 @@ protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Except public void channelInactive(ChannelHandlerContext ctx) throws Exception { // kill DCC when inactive ctx.close(); + DCCConnection.this.exchange.setLocalAddress(null); + DCCConnection.this.exchange.setRemoteAddress(null); + DCCConnection.this.exchange.setConnected(false); + // Close related ServerSocket + ctx.channel().parent().close(); DCCConnection.this.client.getEventManager().callEvent(new DCCConnectionClosedEvent(DCCConnection.this.client, Collections.emptyList(), DCCConnection.this.exchange.snapshot())); } });