Browse Source

Initial work on sane transcoding for the IRC parser

Issue 1754

Change-Id: I3cdc794dce1df63686d3b161e78a316983b872ca
Reviewed-on: http://gerrit.dmdirc.com/1505
Automatic-Compile: DMDirc Local Commits <dmdirc@googlemail.com>
Reviewed-by: Gregory Holmes <greg@dmdirc.com>
tags/0.6.5
Chris Smith 13 years ago
parent
commit
df92212200

+ 43
- 0
src/com/dmdirc/parser/common/SystemEncoder.java View File

@@ -0,0 +1,43 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.parser.common;
24
+
25
+import com.dmdirc.parser.interfaces.Encoder;
26
+
27
+/**
28
+ * A basic {@link Encoder} implementation that just encodes messages using
29
+ * the system's default charset.
30
+ *
31
+ * @since 0.6.5
32
+ * @author chris
33
+ */
34
+public class SystemEncoder implements Encoder {
35
+
36
+    /** {@inheritDoc} */
37
+    @Override
38
+    public String encode(final String source, final String target,
39
+            final byte[] message, final int offset, final int length) {
40
+        return new String(message, offset, length);
41
+    }
42
+
43
+}

+ 48
- 0
src/com/dmdirc/parser/interfaces/Encoder.java View File

@@ -0,0 +1,48 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.parser.interfaces;
24
+
25
+/**
26
+ * Defines standard methods to allow for an external object to encode text
27
+ * for a {@link Parser}.
28
+ *
29
+ * @since 0.6.5
30
+ * @author chris
31
+ */
32
+public interface Encoder {
33
+
34
+    /**
35
+     * Encode the specified message which was sent from the optional source
36
+     * to the optional target using an appropriate charset of the implementor's
37
+     * choosing.
38
+     *
39
+     * @param source The source of the message or null if not applicable
40
+     * @param target The target of the message or null if not applicable
41
+     * @param message The message as read from the wire
42
+     * @param offset The offset within the 'message' array to start reading
43
+     * @param length The number of bytes to read from the message array
44
+     * @return An encoded version of the message
45
+     */
46
+    String encode(String source, String target, byte[] message, int offset, int length);
47
+
48
+}

+ 22
- 19
src/com/dmdirc/parser/irc/IRCParser.java View File

@@ -28,13 +28,14 @@ import com.dmdirc.parser.common.IgnoreList;
28 28
 import com.dmdirc.parser.common.MyInfo;
29 29
 import com.dmdirc.parser.common.ParserError;
30 30
 import com.dmdirc.parser.common.QueuePriority;
31
+import com.dmdirc.parser.common.SystemEncoder;
32
+import com.dmdirc.parser.interfaces.Encoder;
31 33
 import com.dmdirc.parser.interfaces.SecureParser;
32 34
 import com.dmdirc.parser.interfaces.callbacks.*;
35
+import com.dmdirc.parser.irc.IRCReader.ReadLine;
33 36
 import com.dmdirc.parser.irc.outputqueue.OutputQueue;
34 37
 
35
-import java.io.BufferedReader;
36 38
 import java.io.IOException;
37
-import java.io.InputStreamReader;
38 39
 import java.net.InetAddress;
39 40
 import java.net.InetSocketAddress;
40 41
 import java.net.Proxy;
@@ -196,7 +197,7 @@ public class IRCParser implements SecureParser, Runnable {
196 197
     final Map<Character, Byte> chanModesOther = new HashMap<Character, Byte>();
197 198
 
198 199
     /** The last line of input recieved from the server */
199
-    String lastLine = "";
200
+    ReadLine lastLine = null;
200 201
     /** Should the lastline (where given) be appended to the "data" part of any onErrorInfo call? */
201 202
     boolean addLastLine = false;
202 203
 
@@ -232,8 +233,10 @@ public class IRCParser implements SecureParser, Runnable {
232 233
     private Socket socket;
233 234
     /** Used for writing to the server. */
234 235
     private OutputQueue out;
236
+    /** The encoder to use to encode incoming lines. */
237
+    private Encoder encoder = new SystemEncoder();
235 238
     /** Used for reading from the server. */
236
-    private BufferedReader in;
239
+    private IRCReader in;
237 240
 
238 241
     /** This is the default TrustManager for SSL Sockets, it trusts all ssl certs. */
239 242
     private final TrustManager[] trustAllCerts = {
@@ -673,7 +676,7 @@ public class IRCParser implements SecureParser, Runnable {
673 676
         nNextKeyUser = 1;
674 677
         serverName = "";
675 678
         networkName = "";
676
-        lastLine = "";
679
+        lastLine = null;
677 680
         myself = new IRCClientInfo(this, "myself").setFake(true);
678 681
 
679 682
         synchronized (serverInformationLines) {
@@ -789,7 +792,7 @@ public class IRCParser implements SecureParser, Runnable {
789 792
         out.setQueueEnabled(true);
790 793
         currentSocketState = SocketState.OPEN;
791 794
         callDebugInfo(DEBUG_SOCKET, "\t-> Opening socket input stream BufferedReader");
792
-        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
795
+        in = new IRCReader(socket.getInputStream(), encoder);
793 796
         callDebugInfo(DEBUG_SOCKET, "\t-> Socket Opened");
794 797
     }
795 798
 
@@ -832,7 +835,7 @@ public class IRCParser implements SecureParser, Runnable {
832 835
 
833 836
     /**
834 837
      * Begin execution.
835
-     * Connect to server, and start parsing incomming lines
838
+     * Connect to server, and start parsing incoming lines
836 839
      */
837 840
     @Override
838 841
     public void run() {
@@ -1042,21 +1045,21 @@ public class IRCParser implements SecureParser, Runnable {
1042 1045
     }
1043 1046
 
1044 1047
     /** {@inheritDoc} */
1045
-        @Override
1048
+    @Override
1046 1049
     public String getNetworkName() {
1047 1050
         return networkName;
1048 1051
     }
1049 1052
 
1050 1053
     /** {@inheritDoc} */
1051
-        @Override
1054
+    @Override
1052 1055
     public String getServerName() {
1053 1056
         return serverName;
1054 1057
     }
1055 1058
 
1056 1059
     /** {@inheritDoc} */
1057
-        @Override
1060
+    @Override
1058 1061
     public String getLastLine() {
1059
-        return lastLine;
1062
+        return lastLine == null ? "" : lastLine.getLine();
1060 1063
     }
1061 1064
 
1062 1065
     /** {@inheritDoc} */
@@ -1068,21 +1071,21 @@ public class IRCParser implements SecureParser, Runnable {
1068 1071
     }
1069 1072
 
1070 1073
     /**
1071
-     * Process a line and call relevent methods for handling.
1074
+     * Process a line and call relevant methods for handling.
1072 1075
      *
1073
-     * @param line IRC Line to process
1076
+     * @param line Line read from the IRC server
1074 1077
      */
1075
-    protected void processLine(final String line) {
1076
-        callDataIn(line);
1078
+    protected void processLine(final ReadLine line) {
1079
+        callDataIn(line.getLine());
1077 1080
 
1078
-        final String[] token = tokeniseLine(line);
1081
+        final String[] token = line.getTokens();
1079 1082
         int nParam;
1080 1083
         setPingNeeded(false);
1081
-//        pingCountDown = pingCountDownLength;
1082 1084
 
1083 1085
         if (token.length < 2) {
1084 1086
             return;
1085 1087
         }
1088
+
1086 1089
         try {
1087 1090
             final String sParam = token[1];
1088 1091
             if (token[0].equalsIgnoreCase("PING") || token[1].equalsIgnoreCase("PING")) {
@@ -1112,7 +1115,7 @@ public class IRCParser implements SecureParser, Runnable {
1112 1115
                         } else {
1113 1116
                             // Store 001 - 005 for informational purposes.
1114 1117
                             synchronized (serverInformationLines) {
1115
-                                serverInformationLines.add(line);
1118
+                                serverInformationLines.add(line.getLine());
1116 1119
                             }
1117 1120
                         }
1118 1121
                     }
@@ -1155,7 +1158,7 @@ public class IRCParser implements SecureParser, Runnable {
1155 1158
     private IRCStringConverter stringConverter = null;
1156 1159
 
1157 1160
     /** {@inheritDoc} */
1158
-        @Override
1161
+    @Override
1159 1162
     public IRCStringConverter getStringConverter() {
1160 1163
         if (stringConverter == null) {
1161 1164
             stringConverter = new IRCStringConverter((byte) 4);

+ 218
- 0
src/com/dmdirc/parser/irc/IRCReader.java View File

@@ -0,0 +1,218 @@
1
+/*
2
+ * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package com.dmdirc.parser.irc;
24
+
25
+import com.dmdirc.parser.interfaces.Encoder;
26
+
27
+import java.io.Closeable;
28
+import java.io.IOException;
29
+import java.io.InputStream;
30
+
31
+/**
32
+ * A {@link java.io.BufferedReader}-style reader that is aware of the IRC
33
+ * protocol and can transcode text appropriately.
34
+ *
35
+ * @since 0.6.5
36
+ * @author chris
37
+ */
38
+public class IRCReader implements Closeable {
39
+
40
+    /** Maximum length for an IRC line in bytes. */
41
+    private static final int LINE_LENGTH = 512;
42
+
43
+    /** The input stream to read input from. */
44
+    private final InputStream stream;
45
+
46
+    /** The encoder to use to encode lines. */
47
+    private final Encoder encoder;
48
+
49
+    /**
50
+     * Creates a new IRCReader which will read from the specified stream.
51
+     *
52
+     * @param inputStream The stream to read input from
53
+     * @param encoder The encoder to use to encode lines
54
+     */
55
+    public IRCReader(final InputStream inputStream, final Encoder encoder) {
56
+        this.stream = inputStream;
57
+        this.encoder = encoder;
58
+    }
59
+
60
+    /**
61
+     * Reads a line from the underlying input stream, tokenises it, and
62
+     * requests that this reader's encoder encodes the message part of the
63
+     * line, if any.
64
+     *
65
+     * @return A wrapped line tokenised per RFC1459, or null if the stream ends
66
+     * @throws IOException If an IOException is encountered reading the
67
+     * underlying stream
68
+     */
69
+    public ReadLine readLine() throws IOException {
70
+        final byte[] line = new byte[LINE_LENGTH];
71
+        int offset = 0;
72
+        int paramOffset = -1;
73
+        int chr = 0, lastChr = 0;
74
+
75
+        while (offset < 512 && (chr = stream.read()) > -1) {
76
+            line[offset++] = (byte) chr;
77
+
78
+            if (lastChr == ' ' && chr == ':' && paramOffset == -1) {
79
+                // We've found the last param
80
+                paramOffset = offset;
81
+            } else if (lastChr == '\r' && chr == '\n') {
82
+                // End of the line
83
+                break;
84
+            }
85
+
86
+            lastChr = chr;
87
+        }
88
+
89
+        if (chr == -1) {
90
+            // Hit the end of the stream
91
+            return null;
92
+        }
93
+
94
+        return processLine(line, offset - 2, paramOffset);
95
+    }
96
+
97
+    /**
98
+     * Processes the specified line into a wrapped {@link ReadLine} instance.
99
+     *
100
+     * @param line The line as read from the wire
101
+     * @param length The length of the line in bytes
102
+     * @param paramOffset The offset of the first byte of the trailing parameter
103
+     * (i.e., the first byte following the ASCII sequence ' :'), or -1 if no
104
+     * such parameter exists.
105
+     * @return A corresponding {@link ReadLine} instance
106
+     */
107
+    private ReadLine processLine(final byte[] line, final int length, final int paramOffset) {
108
+        final String firstPart = new String(line, 0, paramOffset == -1 ? length : paramOffset - 2);
109
+        final String[] firstTokens = firstPart.split(" ");
110
+
111
+        final String[] tokens;
112
+        if (paramOffset > -1) {
113
+            final String source = getSource(firstTokens);
114
+            final String destination = getDestination(firstTokens);
115
+
116
+            final String lastPart = encoder.encode(source, destination, line,
117
+                    paramOffset, length - paramOffset);
118
+            tokens = new String[firstTokens.length + 1];
119
+            System.arraycopy(firstTokens, 0, tokens, 0, firstTokens.length);
120
+            tokens[firstTokens.length] = lastPart;
121
+        } else {
122
+            tokens = firstTokens;
123
+        }
124
+
125
+        return new ReadLine(new String(line, 0, length), tokens);
126
+    }
127
+
128
+    /**
129
+     * Determines the 'source' of a line made up of the specified tokens. A
130
+     * source is described by the first token if and only if that token starts
131
+     * with a colon.
132
+     *
133
+     * @param tokens The tokens to extract a source from
134
+     * @return The relevant source or null if none specified
135
+     */
136
+    private String getSource(final String[] tokens) {
137
+        if (tokens.length > 0 && tokens[0].length() > 1 && tokens[0].charAt(0) == ':') {
138
+            return tokens[0].substring(1);
139
+        }
140
+
141
+        return null;
142
+    }
143
+
144
+    /**
145
+     * Determines the 'destination' of a line made up of the specified tokens.
146
+     * A destination exists only if a source exists
147
+     * (see {@link #getSource(java.lang.String[])}), and is contained within
148
+     * the third argument for non-numeric lines, and fourth for numerics.
149
+     *
150
+     * @param tokens The tokens to extract a destination from
151
+     * @return The relevant destination or null if none specified
152
+     */
153
+    private String getDestination(final String[] tokens) {
154
+        if (tokens.length > 0 && tokens[0].length() >= 3 && tokens[0].charAt(0) == ':') {
155
+            final int target = tokens[1].matches("^[0-9]+$") ? 3 : 2;
156
+
157
+            if (tokens.length > target) {
158
+                return tokens[target];
159
+            }
160
+        }
161
+
162
+        return null;
163
+    }
164
+
165
+    /** {@inheritDoc} */
166
+    @Override
167
+    public void close() throws IOException {
168
+        stream.close();
169
+    }
170
+
171
+    /**
172
+     * Represents a line that has been read from the IRC server and encoded
173
+     * appropriately.
174
+     */
175
+    public static class ReadLine {
176
+
177
+        /** A representation of the read-line using a default encoding. */
178
+        private final String line;
179
+        /** The tokens found in the line, individually encoded as appropriate. */
180
+        private final String[] tokens;
181
+
182
+        /**
183
+         * Creates a new instance of {@link ReadLine} with the specified line
184
+         * and tokens.
185
+         *
186
+         * @param line A string representation of the line
187
+         * @param tokens The tokens which make up the line
188
+         */
189
+        public ReadLine(final String line, final String[] tokens) {
190
+            this.line = line;
191
+            this.tokens = tokens;
192
+        }
193
+
194
+        /**
195
+         * Retrieves a string representation of the line that has been read.
196
+         * This may be encoded using a charset which is not appropriate for
197
+         * displaying all of the line's contents, and is intended for debug
198
+         * purposes only.
199
+         *
200
+         * @return A string representation of the line
201
+         */
202
+        public String getLine() {
203
+            return line;
204
+        }
205
+
206
+        /**
207
+         * Retrieves an array of tokens extracted from the specified line.
208
+         * Each token may have a different encoding.
209
+         *
210
+         * @return The line's tokens
211
+         */
212
+        public String[] getTokens() {
213
+            return tokens;
214
+        }
215
+
216
+    }
217
+
218
+}

+ 1
- 1
test/com/dmdirc/harness/parser/TestParser.java View File

@@ -60,7 +60,7 @@ public class TestParser extends IRCParser implements Parser {
60 60
     }
61 61
     
62 62
     public void injectLine(String line) {
63
-        processLine(line);
63
+        processLine(new IRCReader.ReadLine(line, IRCParser.tokeniseLine(line)));
64 64
     }
65 65
     
66 66
     public void injectConnectionStrings() {

Loading…
Cancel
Save