1 package net.pterodactylus.irc.connection;
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.fromNullable;
5 import static com.google.common.base.Optional.of;
6 import static java.lang.String.format;
7 import static java.net.InetAddress.getByName;
8 import static org.apache.log4j.Logger.getLogger;
10 import java.net.InetAddress;
11 import java.net.UnknownHostException;
12 import java.util.List;
14 import net.pterodactylus.irc.Connection;
15 import net.pterodactylus.irc.Reply;
16 import net.pterodactylus.irc.Source;
17 import net.pterodactylus.irc.event.DccAcceptReceived;
18 import net.pterodactylus.irc.event.DccSendReceived;
20 import com.google.common.base.Optional;
21 import com.google.common.eventbus.EventBus;
22 import com.google.common.primitives.Ints;
23 import com.google.common.primitives.Longs;
24 import org.apache.log4j.Logger;
27 * Handles all CTCP messages relevant to DCC transfers.
29 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
31 public class CtcpHandler implements Handler {
33 private static final Logger logger = getLogger(CtcpHandler.class);
34 private final EventBus eventBus;
35 private final Connection connection;
37 public CtcpHandler(EventBus eventBus, Connection connection) {
38 this.eventBus = eventBus;
39 this.connection = connection;
43 public boolean willHandle(Reply reply) {
44 return (commandIs(reply, "PRIVMSG") || commandIs(reply, "NOTICE"))
45 && messageIsCtcp(reply.parameters().get(1));
48 private boolean commandIs(Reply reply, String command) {
49 return reply.command().equalsIgnoreCase(command);
52 public static boolean messageIsCtcp(String message) {
53 return message.startsWith("\u0001") && message.endsWith("\u0001");
57 public void handleReply(Reply reply) {
58 List<String> parameters = reply.parameters();
59 String message = parameters.get(1);
60 Source source = reply.source().get();
61 handleCtcp(source, message);
64 private void handleCtcp(Source client, String message) {
65 String[] messageWords = message.substring(1, message.length() - 1).split(" +");
66 String ctcpCommand = messageWords[0];
67 if (ctcpCommand.equalsIgnoreCase("DCC")) {
68 if (messageWords[1].equalsIgnoreCase("SEND")) {
69 processDccSend(client, message, messageWords);
70 } else if (messageWords[1].equalsIgnoreCase("ACCEPT")) {
71 processDccAccept(client, message, messageWords);
76 private void processDccSend(Source client, String message,
77 String[] messageWords) {
78 Optional<DccSendInformation> dccSendInformation = parseDccSendInformation(messageWords);
79 if (dccSendInformation.isPresent()) {
80 eventBus.post(new DccSendReceived(connection, client,
81 dccSendInformation.get().filename,
82 dccSendInformation.get().internetAddress,
83 dccSendInformation.get().port,
84 dccSendInformation.get().size,
85 dccSendInformation.get().token));
87 logger.warn(format("Received malformed DCC SEND: “%s”", message));
91 private Optional<DccSendInformation> parseDccSendInformation(String[] messageWords) {
92 if (messageWords.length <5) {
95 Optional<InetAddress> internetAddress = parseInetAddress(messageWords[3]);
96 Optional<Integer> port = fromNullable(Ints.tryParse(messageWords[4]));
97 long fileSize = (messageWords.length > 5) ? fromNullable(Longs.tryParse(messageWords[5])).or(-1L) : -1;
98 if (!internetAddress.isPresent() || !port.isPresent()) {
102 if (messageWords.length > 6) {
103 token = messageWords[6];
105 return of(new DccSendInformation(messageWords[2], internetAddress.get(), port.get(), fileSize, token));
108 private static class DccSendInformation {
110 private final String filename;
111 private final InetAddress internetAddress;
112 private final int port;
113 private final long size;
114 private final String token;
116 private DccSendInformation(String filename, InetAddress internetAddress, int port, long size, String token) {
117 this.filename = filename;
118 this.internetAddress = internetAddress;
126 private Optional<InetAddress> parseInetAddress(String ip) {
127 Long ipNumber = Longs.tryParse(ip);
128 if (ipNumber == null) {
132 StringBuilder hostname = new StringBuilder(15);
133 hostname.append((ipNumber >>> 24) & 0xff).append('.');
134 hostname.append((ipNumber >>> 16) & 0xff).append('.');
135 hostname.append((ipNumber >>> 8) & 0xff).append('.');
136 hostname.append(ipNumber & 0xff);
138 return of(getByName(hostname.toString()));
139 } catch (UnknownHostException uhe1) {
144 private void processDccAccept(Source client, String message,
145 String[] messageWords) {
146 Optional<DccAcceptInformation> dccAcceptInformation = parseDccAcceptInformation(messageWords);
147 if (dccAcceptInformation.isPresent()) {
148 eventBus.post(new DccAcceptReceived(connection, client,
149 dccAcceptInformation.get().filename,
150 dccAcceptInformation.get().port,
151 dccAcceptInformation.get().position));
153 logger.warn(format("Received malformed DCC ACCEPT: “%s”", message));
157 private Optional<DccAcceptInformation> parseDccAcceptInformation(
158 String[] messageWords) {
159 if (messageWords.length < 4) {
162 Optional<Integer> port = fromNullable(Ints.tryParse(messageWords[3]));
163 long position = (messageWords.length > 4) ? fromNullable(
164 Longs.tryParse(messageWords[4])).or(-1L) : -1;
165 if (!port.isPresent()) {
168 return of(new DccAcceptInformation(messageWords[2], port.get(), position));
171 private static class DccAcceptInformation {
173 private final String filename;
174 private final int port;
175 private final long position;
177 private DccAcceptInformation(String filename, int port, long position) {
178 this.filename = filename;
180 this.position = position;