🚧 Parse token from DCC SEND
[xudocci.git] / src / main / java / net / pterodactylus / irc / connection / CtcpHandler.java
1 package net.pterodactylus.irc.connection;
2
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;
9
10 import java.net.InetAddress;
11 import java.net.UnknownHostException;
12 import java.util.List;
13
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;
19
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;
25
26 /**
27  * Handles all CTCP messages relevant to DCC transfers.
28  *
29  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
30  */
31 public class CtcpHandler implements Handler {
32
33         private static final Logger logger = getLogger(CtcpHandler.class);
34         private final EventBus eventBus;
35         private final Connection connection;
36
37         public CtcpHandler(EventBus eventBus, Connection connection) {
38                 this.eventBus = eventBus;
39                 this.connection = connection;
40         }
41
42         @Override
43         public boolean willHandle(Reply reply) {
44                 return (commandIs(reply, "PRIVMSG") || commandIs(reply, "NOTICE"))
45                                 && messageIsCtcp(reply.parameters().get(1));
46         }
47
48         private boolean commandIs(Reply reply, String command) {
49                 return reply.command().equalsIgnoreCase(command);
50         }
51
52         public static boolean messageIsCtcp(String message) {
53                 return message.startsWith("\u0001") && message.endsWith("\u0001");
54         }
55
56         @Override
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);
62         }
63
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);
72                         }
73                 }
74         }
75
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));
86                 } else {
87                         logger.warn(format("Received malformed DCC SEND: “%s”", message));
88                 }
89         }
90
91         private Optional<DccSendInformation> parseDccSendInformation(String[] messageWords) {
92                 if (messageWords.length <5) {
93                         return absent();
94                 }
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()) {
99                         return absent();
100                 }
101                 String token = null;
102                 if (messageWords.length > 6) {
103                         token = messageWords[6];
104                 }
105                 return of(new DccSendInformation(messageWords[2], internetAddress.get(), port.get(), fileSize, token));
106         }
107
108         private static class DccSendInformation {
109
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;
115
116                 private DccSendInformation(String filename, InetAddress internetAddress, int port, long size, String token) {
117                         this.filename = filename;
118                         this.internetAddress = internetAddress;
119                         this.port = port;
120                         this.size = size;
121                         this.token = token;
122                 }
123
124         }
125
126         private Optional<InetAddress> parseInetAddress(String ip) {
127                 Long ipNumber = Longs.tryParse(ip);
128                 if (ipNumber == null) {
129                         return absent();
130                 }
131
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);
137                 try {
138                         return of(getByName(hostname.toString()));
139                 } catch (UnknownHostException uhe1) {
140                         return absent();
141                 }
142         }
143
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));
152                 } else {
153                         logger.warn(format("Received malformed DCC ACCEPT: “%s”", message));
154                 }
155         }
156
157         private Optional<DccAcceptInformation> parseDccAcceptInformation(
158                         String[] messageWords) {
159                 if (messageWords.length < 4) {
160                         return absent();
161                 }
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()) {
166                         return absent();
167                 }
168                 return of(new DccAcceptInformation(messageWords[2], port.get(), position));
169         }
170
171         private static class DccAcceptInformation {
172
173                 private final String filename;
174                 private final int port;
175                 private final long position;
176
177                 private DccAcceptInformation(String filename, int port, long position) {
178                         this.filename = filename;
179                         this.port = port;
180                         this.position = position;
181                 }
182
183         }
184
185 }