ircparser 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/ircparser.rb +3 -1
- data/lib/ircparser/error.rb +1 -1
- data/lib/ircparser/message.rb +10 -164
- data/lib/ircparser/prefix.rb +8 -21
- data/lib/ircparser/stream.rb +46 -0
- data/lib/ircparser/wire/rfc.rb +248 -0
- data/test/test_message.rb +25 -0
- data/test/test_prefix.rb +46 -17
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b3ba0c73061105629280badf2eda78d434a53c09bca59f77f73397740a6f062
|
4
|
+
data.tar.gz: b49d3de744a1fec6d9c9fabb17ca2df3c4416dc38f8d0afe953e9d0f4587d7b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04633fd5973f4b80cfdfa50b7c01c75683e9ecea0fe4e087c9d410d3a6cce649158eecc57e4902909fee8900031ebe50c3b5a6c9f070363120b711d762d29de8
|
7
|
+
data.tar.gz: fc2a8e5cadb156963f16743d857703c3bdf4900da0f86a18a9814e13ef52532282d7eee4ba7d1ee761439d46ee7107a62a5da1eb833de330ddc8bf7e33c22762
|
data/lib/ircparser.rb
CHANGED
@@ -16,9 +16,11 @@
|
|
16
16
|
module IRCParser
|
17
17
|
|
18
18
|
# Public: The version of IRCParser in use.
|
19
|
-
VERSION = '0.
|
19
|
+
VERSION = '0.6.0'
|
20
20
|
end
|
21
21
|
|
22
22
|
require_relative 'ircparser/error'
|
23
23
|
require_relative 'ircparser/message'
|
24
24
|
require_relative 'ircparser/prefix'
|
25
|
+
require_relative 'ircparser/stream'
|
26
|
+
require_relative 'ircparser/wire/rfc'
|
data/lib/ircparser/error.rb
CHANGED
@@ -21,7 +21,7 @@ module IRCParser
|
|
21
21
|
# Public: The value which failed to parse correctly.
|
22
22
|
attr_reader :value
|
23
23
|
|
24
|
-
# Public:
|
24
|
+
# Public: Initialises a new parse error with the specified invalid value.
|
25
25
|
#
|
26
26
|
# value - The value which failed to parse correctly.
|
27
27
|
def initialize value
|
data/lib/ircparser/message.rb
CHANGED
@@ -18,31 +18,24 @@ module IRCParser
|
|
18
18
|
# Public: Represents an IRC message.
|
19
19
|
class Message
|
20
20
|
|
21
|
-
# Internal: A regular expression which matches a tag.
|
22
|
-
MATCH_TAG = /^(?<name>[^\s=]+?)(?:=(?<value>[^\s;]+))?$/
|
23
|
-
|
24
|
-
# Internal: The characters which need to be escaped in tag values.
|
25
|
-
TAG_ESCAPES = {
|
26
|
-
'\\' => '\\\\',
|
27
|
-
';' => '\:',
|
28
|
-
"\s" => '\s',
|
29
|
-
"\r" => '\r',
|
30
|
-
"\n" => '\n'
|
31
|
-
}
|
32
|
-
|
33
21
|
# Public: Command name (e.g. PRIVMSG).
|
34
22
|
attr_reader :command
|
35
23
|
|
36
24
|
# Public: An array of command parameters.
|
37
25
|
attr_reader :parameters
|
38
26
|
|
39
|
-
# Public: The prefix of this command or nil if
|
27
|
+
# Public: The prefix of this command or nil if unprefixed.
|
40
28
|
attr_reader :prefix
|
41
29
|
|
42
30
|
# Public: A hash of IRCv3 message tags.
|
43
31
|
attr_reader :tags
|
44
32
|
|
45
|
-
# Public:
|
33
|
+
# Public: Initializes a new message.
|
34
|
+
#
|
35
|
+
# command - Command name (e.g. PRIVMSG).
|
36
|
+
# parameters - An array of command parameters.
|
37
|
+
# prefix - The prefix of this command or nil if unprefixed.
|
38
|
+
# tags - A hash of IRCv3 message tags.
|
46
39
|
def initialize command: String.new, parameters: Array.new, prefix: nil, tags: Hash.new
|
47
40
|
@command = command
|
48
41
|
@parameters = parameters
|
@@ -50,163 +43,16 @@ module IRCParser
|
|
50
43
|
@tags = tags
|
51
44
|
end
|
52
45
|
|
53
|
-
# Public: Parses an IRC message from
|
46
|
+
# Public: Parses an IRC message from wire form.
|
54
47
|
#
|
55
48
|
# line - The line to attempt to parse.
|
56
49
|
def self.parse line
|
57
|
-
|
58
|
-
# Ruby really needs some kind of basic type checking.
|
59
|
-
raise IRCParser::Error.new(line), "line is not a string" unless line.is_a? String
|
60
|
-
|
61
|
-
# Split the message up into an array of tokens.
|
62
|
-
buffer = line.clone
|
63
|
-
current_token = __get_token buffer
|
64
|
-
components = Hash.new
|
65
|
-
|
66
|
-
# Have we encountered IRCv3 message tags?
|
67
|
-
components[:tags] = Hash.new
|
68
|
-
if current_token != nil && current_token[0] == '@'
|
69
|
-
components[:tags] = __parse_tags current_token
|
70
|
-
current_token = __get_token buffer
|
71
|
-
end
|
72
|
-
|
73
|
-
# Have we encountered the prefix of this message?
|
74
|
-
if current_token != nil && current_token[0] == ':'
|
75
|
-
components[:prefix] = IRCParser::Prefix.new current_token[1..-1]
|
76
|
-
current_token = __get_token buffer
|
77
|
-
end
|
78
|
-
|
79
|
-
# The command parameter is mandatory.
|
80
|
-
if current_token != nil
|
81
|
-
components[:command] = current_token.upcase
|
82
|
-
current_token = __get_final_token buffer
|
83
|
-
else
|
84
|
-
raise IRCParser::Error.new(line), 'message is missing the command name'
|
85
|
-
end
|
86
|
-
|
87
|
-
# Try to parse all of the remaining parameters.
|
88
|
-
components[:parameters] = Array.new
|
89
|
-
while current_token != nil
|
90
|
-
components[:parameters] << current_token
|
91
|
-
current_token = __get_final_token buffer
|
92
|
-
end
|
93
|
-
|
94
|
-
return IRCParser::Message.new components
|
50
|
+
return IRCParser::RFCWireFormat.objectify line.clone
|
95
51
|
end
|
96
52
|
|
97
|
-
|
98
53
|
# Public: Serializes the message to a string.
|
99
54
|
def to_s
|
100
|
-
|
101
|
-
|
102
|
-
# Serialize the tags.
|
103
|
-
unless tags.empty?
|
104
|
-
buffer += '@'
|
105
|
-
buffer += __serialize_tags
|
106
|
-
buffer += ' '
|
107
|
-
end
|
108
|
-
|
109
|
-
# Serialize the prefix.
|
110
|
-
unless prefix.nil?
|
111
|
-
buffer += ':'
|
112
|
-
buffer += prefix.to_s
|
113
|
-
buffer += ' '
|
114
|
-
end
|
115
|
-
|
116
|
-
# Serialize the command.
|
117
|
-
buffer += command
|
118
|
-
|
119
|
-
# Serialize the parameters
|
120
|
-
buffer += __serialize_parameters
|
121
|
-
|
122
|
-
# We're done!
|
123
|
-
return buffer
|
124
|
-
end
|
125
|
-
|
126
|
-
private
|
127
|
-
|
128
|
-
# Internal: Retrieves a space delimited token from the buffer.
|
129
|
-
#
|
130
|
-
# data - The buffer to retrieve the token from.
|
131
|
-
def self.__get_token buffer
|
132
|
-
return nil if buffer.empty?
|
133
|
-
position = buffer.index ' '
|
134
|
-
if position == nil
|
135
|
-
token = buffer.clone
|
136
|
-
buffer.clear
|
137
|
-
return token
|
138
|
-
end
|
139
|
-
token = buffer.slice! 0...position
|
140
|
-
position = buffer.index /\S+/
|
141
|
-
if position == nil
|
142
|
-
buffer.clear
|
143
|
-
else
|
144
|
-
buffer.slice! 0...position
|
145
|
-
end
|
146
|
-
return token
|
147
|
-
end
|
148
|
-
|
149
|
-
# Internal: Retrieves a space delimited token that may be a <trailing> parameter.
|
150
|
-
#
|
151
|
-
# data - The buffer to retrieve the token from.
|
152
|
-
def self.__get_final_token buffer
|
153
|
-
return nil if buffer.empty?
|
154
|
-
if buffer[0] == ':'
|
155
|
-
token = buffer[1..-1]
|
156
|
-
buffer.clear
|
157
|
-
return token
|
158
|
-
end
|
159
|
-
return __get_token buffer
|
160
|
-
end
|
161
|
-
|
162
|
-
# Internal: Parse tags from network form to a Hash.
|
163
|
-
#
|
164
|
-
# token - A list of tags in network form.
|
165
|
-
def self.__parse_tags token
|
166
|
-
tags = Hash.new
|
167
|
-
token[1..-1].split(';').each do |tag|
|
168
|
-
if tag =~ MATCH_TAG
|
169
|
-
name, value = $~['name'], $~['value']
|
170
|
-
TAG_ESCAPES.each do |unescaped, escaped|
|
171
|
-
value.gsub! escaped, unescaped
|
172
|
-
end unless value.nil?
|
173
|
-
tags[name] = value
|
174
|
-
else
|
175
|
-
raise IRCParser::Error.new(tag), "tag is malformed"
|
176
|
-
end
|
177
|
-
end
|
178
|
-
return tags
|
179
|
-
end
|
180
|
-
|
181
|
-
# Internal: Serializes parameters from an Array to network form.
|
182
|
-
def __serialize_parameters
|
183
|
-
buffer = String.new
|
184
|
-
@parameters.each_with_index do |parameter, index|
|
185
|
-
trailing = parameter.include? ' '
|
186
|
-
if trailing && index != @parameters.size-1
|
187
|
-
raise IRCParser::Error.new(parameter), "only the last parameter may contain spaces"
|
188
|
-
end
|
189
|
-
|
190
|
-
buffer += ' '
|
191
|
-
if trailing || parameter.empty?
|
192
|
-
buffer += ':'
|
193
|
-
buffer += parameter
|
194
|
-
break
|
195
|
-
end
|
196
|
-
buffer += parameter
|
197
|
-
end
|
198
|
-
return buffer
|
199
|
-
end
|
200
|
-
|
201
|
-
# Internal: Serializes tags from a Hash to network form.
|
202
|
-
def __serialize_tags
|
203
|
-
buffer = @tags.dup.map do |key, value|
|
204
|
-
TAG_ESCAPES.each do |unescaped, escaped|
|
205
|
-
value.gsub! unescaped, Regexp.escape(escaped)
|
206
|
-
end unless value.nil?
|
207
|
-
value.nil? ? key : key + '=' + value
|
208
|
-
end.join ';'
|
209
|
-
return buffer
|
55
|
+
return IRCParser::RFCWireFormat.stringify self.clone
|
210
56
|
end
|
211
57
|
end
|
212
58
|
end
|
data/lib/ircparser/prefix.rb
CHANGED
@@ -18,9 +18,6 @@ module IRCParser
|
|
18
18
|
# Public: Represents the prefix of an IRC message.
|
19
19
|
class Prefix
|
20
20
|
|
21
|
-
# Internal: A regular expression which matches a n!u@h mask.
|
22
|
-
MATCH_PREFIX = /^(?<nick>[^@!]+) (?:!(?<user>[^@]+))? (?:@(?<host>.+))?$/x
|
23
|
-
|
24
21
|
# Public: The hostname of this prefix or nil if no hostname was given.
|
25
22
|
attr_reader :host
|
26
23
|
|
@@ -30,25 +27,15 @@ module IRCParser
|
|
30
27
|
# Public: The username of this prefix or nil if no username was given.
|
31
28
|
attr_reader :user
|
32
29
|
|
33
|
-
# Public:
|
30
|
+
# Public: Initialises a new message prefix.
|
34
31
|
#
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
raise IRCParser::Error.new(prefix), 'prefix is not a user mask or server name'
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Public: serialises this prefix to the network form.
|
47
|
-
def to_s
|
48
|
-
buffer = @nick
|
49
|
-
buffer += "!#{@user}" unless @user.nil?
|
50
|
-
buffer += "@#{@host}" unless @host.nil?
|
51
|
-
return buffer
|
32
|
+
# nick - The nickname of this user.
|
33
|
+
# user - The username of this prefix or nil if no username was given.
|
34
|
+
# host - The hostname of this prefix or nil if no hostname was given.
|
35
|
+
def initialize nick: nil, user: nil, host: nil
|
36
|
+
@nick = nick
|
37
|
+
@user = user
|
38
|
+
@host = host
|
52
39
|
end
|
53
40
|
end
|
54
41
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# IRCParser - Internet Relay Chat Message Parser
|
2
|
+
#
|
3
|
+
# Copyright (C) 2015-2017 Peter "SaberUK" Powell <petpow@saberuk.com>
|
4
|
+
#
|
5
|
+
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
6
|
+
# fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
7
|
+
# in all copies.
|
8
|
+
#
|
9
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
10
|
+
# SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
11
|
+
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
12
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
13
|
+
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
14
|
+
# OF THIS SOFTWARE.
|
15
|
+
|
16
|
+
module IRCParser
|
17
|
+
|
18
|
+
# Public: Parses IRC messages from a stream of data.
|
19
|
+
class Stream
|
20
|
+
|
21
|
+
# Public: The contents of the stream buffer.
|
22
|
+
attr_reader :buffer
|
23
|
+
|
24
|
+
# Public: The block which is called when a message is parsed.
|
25
|
+
attr_reader :block
|
26
|
+
|
27
|
+
# Public: Initialize a new stream.
|
28
|
+
def initialize &block
|
29
|
+
unless block.is_a? Proc
|
30
|
+
raise TypeError, "Wrong argument type #{block.class} (expected Proc)"
|
31
|
+
end
|
32
|
+
@block = block
|
33
|
+
end
|
34
|
+
|
35
|
+
# Public: Appends data to the stream buffer.
|
36
|
+
#
|
37
|
+
# data - The data to append.
|
38
|
+
def append data
|
39
|
+
(@buffer ||= '') << data
|
40
|
+
while @buffer.slice! /(.*?)\r?\n/
|
41
|
+
message = IRCParser::Message.parse $1
|
42
|
+
@block.call message
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# IRCParser - Internet Relay Chat Message Parser
|
2
|
+
#
|
3
|
+
# Copyright (C) 2015-2017 Peter "SaberUK" Powell <petpow@saberuk.com>
|
4
|
+
#
|
5
|
+
# Permission to use, copy, modify, and/or distribute this software for any purpose with or without
|
6
|
+
# fee is hereby granted, provided that the above copyright notice and this permission notice appear
|
7
|
+
# in all copies.
|
8
|
+
#
|
9
|
+
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
10
|
+
# SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
11
|
+
# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
12
|
+
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
13
|
+
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
14
|
+
# OF THIS SOFTWARE.
|
15
|
+
|
16
|
+
module IRCParser
|
17
|
+
|
18
|
+
# Internal: Implements objectification and stringification for the RFC wire format.
|
19
|
+
module RFCWireFormat
|
20
|
+
|
21
|
+
# Internal: Objectifies a message from the RFC wire format to an IRCParser::Message.
|
22
|
+
#
|
23
|
+
# str - A String containing a message in the RFC wire format.
|
24
|
+
def self.objectify str
|
25
|
+
|
26
|
+
# Ruby really needs some kind of basic type checking.
|
27
|
+
unless str.is_a? String
|
28
|
+
raise IRCParser::Error.new(line), "message is not a string"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Split the message up into an array of tokens.
|
32
|
+
current_token = self.__get_token str
|
33
|
+
components = Hash.new
|
34
|
+
|
35
|
+
# Have we encountered IRCv3 message tags?
|
36
|
+
components[:tags] = Hash.new
|
37
|
+
if current_token != nil && current_token[0] == '@'
|
38
|
+
components[:tags] = self.__objectify_tags current_token
|
39
|
+
current_token = self.__get_token str
|
40
|
+
end
|
41
|
+
|
42
|
+
# Have we encountered the prefix of this message?
|
43
|
+
if current_token != nil && current_token[0] == ':'
|
44
|
+
components[:prefix] = self.__objectify_prefix current_token
|
45
|
+
current_token = self.__get_token str
|
46
|
+
end
|
47
|
+
|
48
|
+
# The command parameter is mandatory.
|
49
|
+
if current_token != nil
|
50
|
+
components[:command] = current_token.upcase
|
51
|
+
current_token = self.__get_final_token str
|
52
|
+
else
|
53
|
+
raise IRCParser::Error.new(line), 'message is missing the command name'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Try to parse all of the remaining parameters.
|
57
|
+
components[:parameters] = Array.new
|
58
|
+
while current_token != nil
|
59
|
+
components[:parameters] << current_token
|
60
|
+
current_token = self.__get_final_token str
|
61
|
+
end
|
62
|
+
|
63
|
+
return IRCParser::Message.new components
|
64
|
+
end
|
65
|
+
|
66
|
+
# Internal: Stringifies a message from an IRCParser::Message to the RFC wire format.
|
67
|
+
#
|
68
|
+
# obj - An IRCParser::Message to stringify to the RFC wire format.
|
69
|
+
def self.stringify obj
|
70
|
+
|
71
|
+
# Ruby really needs some kind of basic type checking.
|
72
|
+
unless obj.is_a? IRCParser::Message
|
73
|
+
raise IRCParser::Error.new(obj), "message is not an IRCParser::Message"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Stringify the tags.
|
77
|
+
buffer = String.new
|
78
|
+
unless obj.tags.empty?
|
79
|
+
buffer += '@'
|
80
|
+
buffer += self.__stringify_tags obj.tags
|
81
|
+
buffer += ' '
|
82
|
+
end
|
83
|
+
|
84
|
+
# Stringify the prefix.
|
85
|
+
unless obj.prefix.nil?
|
86
|
+
buffer += ':'
|
87
|
+
buffer += self.__stringify_prefix obj.prefix
|
88
|
+
buffer += ' '
|
89
|
+
end
|
90
|
+
|
91
|
+
# Stringify the command.
|
92
|
+
buffer += obj.command
|
93
|
+
|
94
|
+
# Stringify the parameters
|
95
|
+
buffer += self.__stringify_parameters obj.parameters
|
96
|
+
|
97
|
+
# We're done!
|
98
|
+
return buffer
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# Internal: A regular expression which matches a n!u@h mask.
|
104
|
+
MATCH_PREFIX = /^:(?<nick>[^@!]+) (?:!(?<user>[^@]+))? (?:@(?<host>.+))?$/x
|
105
|
+
|
106
|
+
# Internal: A regular expression which matches a tag.
|
107
|
+
MATCH_TAG = /^(?<name>[^\s=]+?)(?:=(?<value>[^\s;]+))?$/
|
108
|
+
|
109
|
+
# Internal: The characters which need to be escaped in tag values.
|
110
|
+
TAG_ESCAPES = {
|
111
|
+
'\\\\' => '\\',
|
112
|
+
'\:' => ';',
|
113
|
+
'\s' => "\s",
|
114
|
+
'\r' => "\r",
|
115
|
+
'\n' => "\n",
|
116
|
+
}
|
117
|
+
|
118
|
+
# Internal: Retrieves a space delimited token from the buffer.
|
119
|
+
#
|
120
|
+
# buffer - The buffer to retrieve the token from.
|
121
|
+
def self.__get_token buffer
|
122
|
+
return nil if buffer.empty?
|
123
|
+
position = buffer.index ' '
|
124
|
+
if position == nil
|
125
|
+
token = buffer.clone
|
126
|
+
buffer.clear
|
127
|
+
return token
|
128
|
+
end
|
129
|
+
token = buffer.slice! 0...position
|
130
|
+
position = buffer.index /\S+/
|
131
|
+
if position == nil
|
132
|
+
buffer.clear
|
133
|
+
else
|
134
|
+
buffer.slice! 0...position
|
135
|
+
end
|
136
|
+
return token
|
137
|
+
end
|
138
|
+
|
139
|
+
# Internal: Retrieves a space delimited token that may be a <trailing> parameter.
|
140
|
+
#
|
141
|
+
# buffer - The buffer to retrieve the token from.
|
142
|
+
def self.__get_final_token buffer
|
143
|
+
return nil if buffer.empty?
|
144
|
+
if buffer[0] == ':'
|
145
|
+
token = buffer[1..-1]
|
146
|
+
buffer.clear
|
147
|
+
return token
|
148
|
+
end
|
149
|
+
return self.__get_token buffer
|
150
|
+
end
|
151
|
+
|
152
|
+
# Internal: Objectifies the prefix from the RFC wire format to an IRCParser::Prefix.
|
153
|
+
#
|
154
|
+
# token - A String containing the prefix in the RFC wire format.
|
155
|
+
def self.__objectify_prefix prefix
|
156
|
+
unless MATCH_PREFIX =~ prefix
|
157
|
+
raise IRCParser::Error.new(prefix), 'prefix is not a user mask or server name'
|
158
|
+
end
|
159
|
+
return IRCParser::Prefix.new nick: $~[:nick], user: $~[:user], host: $~[:host]
|
160
|
+
end
|
161
|
+
|
162
|
+
# Internal: Objectifies tags from the RFC wire format to a Hash.
|
163
|
+
#
|
164
|
+
# token - A String containing tags in the RFC wire format.
|
165
|
+
def self.__objectify_tags token
|
166
|
+
tags = Hash.new
|
167
|
+
token[1..-1].split(';').each do |tag|
|
168
|
+
if tag =~ MATCH_TAG
|
169
|
+
value = nil
|
170
|
+
value_index = 0
|
171
|
+
while $~['value'] != nil && value_index < $~['value'].size
|
172
|
+
value ||= String.new
|
173
|
+
if $~['value'][value_index] == '\\'
|
174
|
+
escape = $~['value'].slice(value_index, 2)
|
175
|
+
if TAG_ESCAPES.include? escape
|
176
|
+
value += TAG_ESCAPES[escape]
|
177
|
+
value_index += 1
|
178
|
+
end
|
179
|
+
else
|
180
|
+
value += $~['value'][value_index]
|
181
|
+
end
|
182
|
+
value_index += 1
|
183
|
+
end
|
184
|
+
tags[$~['name']] = value
|
185
|
+
else
|
186
|
+
raise IRCParser::Error.new(tag), 'tag is malformed'
|
187
|
+
end
|
188
|
+
end
|
189
|
+
return tags
|
190
|
+
end
|
191
|
+
|
192
|
+
# Internal: Stringifies parameters from an Array to the RFC wire format.
|
193
|
+
#
|
194
|
+
# parameters - An Array to stringify to the RFC wire format.
|
195
|
+
def self.__stringify_parameters parameters
|
196
|
+
buffer = String.new
|
197
|
+
parameters.each_with_index do |parameter, index|
|
198
|
+
trailing = parameter.include? ' '
|
199
|
+
if trailing && index != parameters.size-1
|
200
|
+
raise IRCParser::Error.new(parameter), 'only the last parameter may contain spaces'
|
201
|
+
end
|
202
|
+
|
203
|
+
buffer += ' '
|
204
|
+
if trailing || parameter.empty?
|
205
|
+
buffer += ':'
|
206
|
+
buffer += parameter
|
207
|
+
break
|
208
|
+
end
|
209
|
+
buffer += parameter
|
210
|
+
end
|
211
|
+
return buffer
|
212
|
+
end
|
213
|
+
|
214
|
+
# Internal: Stringifies the prefix from an IRCParser::Prefix to the RFC wire format.
|
215
|
+
#
|
216
|
+
# tags - An IRCParser::Prefix to stringify to the RFC wire format.
|
217
|
+
def self.__stringify_prefix prefix
|
218
|
+
buffer = prefix.nick
|
219
|
+
buffer += "!#{prefix.user}" unless prefix.user.nil?
|
220
|
+
buffer += "@#{prefix.host}" unless prefix.host.nil?
|
221
|
+
return buffer
|
222
|
+
end
|
223
|
+
|
224
|
+
# Internal: Stringifies tags from a Hash to the RFC wire format.
|
225
|
+
#
|
226
|
+
# tags - A Hash of tags to stringify to the RFC wire format.
|
227
|
+
def self.__stringify_tags tags
|
228
|
+
buffer = String.new
|
229
|
+
tags.each.with_index do |tag, idx|
|
230
|
+
key, value = tag
|
231
|
+
buffer += key
|
232
|
+
unless value.nil?
|
233
|
+
buffer += '='
|
234
|
+
value.each_char do |chr|
|
235
|
+
if TAG_ESCAPES.has_value? chr
|
236
|
+
buffer += TAG_ESCAPES.key chr
|
237
|
+
else
|
238
|
+
buffer += chr
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
buffer += ';' if idx < tags.size - 1
|
243
|
+
end
|
244
|
+
return buffer
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
data/test/test_message.rb
CHANGED
@@ -168,6 +168,31 @@ describe IRCParser::Message do
|
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
|
+
describe 'when checking we can handle parsing malformed tags' do
|
172
|
+
before do
|
173
|
+
@text = '@foo=wibble\Zwobble\\ COMMAND'
|
174
|
+
@message = IRCParser::Message.parse @text
|
175
|
+
end
|
176
|
+
it 'should strip invalid and trailing escapes' do
|
177
|
+
@message.tags['foo'].must_equal 'wibbleZwobble'
|
178
|
+
end
|
179
|
+
it 'should serialise back to a well formed value' do
|
180
|
+
@message.to_s.must_equal '@foo=wibbleZwobble COMMAND'
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe 'when checking we can handle serialising without creating malformed tags' do
|
185
|
+
before do
|
186
|
+
@tags = {
|
187
|
+
'foo' => 'wibble\Zwobble\\'
|
188
|
+
}
|
189
|
+
@message = IRCParser::Message.new tags: @tags, command: 'COMMAND'
|
190
|
+
end
|
191
|
+
it 'should serialise without creating malformed tags' do
|
192
|
+
@message.to_s.must_equal '@foo=wibble\\\\Zwobble\\\\ COMMAND'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
171
196
|
describe 'when checking we can handle serialising malformed parameters' do
|
172
197
|
it 'should throw an IRCParser::Error when a non <trailing> parameter contains spaces' do
|
173
198
|
proc {
|
data/test/test_prefix.rb
CHANGED
@@ -20,41 +20,70 @@ require 'minitest/autorun'
|
|
20
20
|
|
21
21
|
describe IRCParser::Prefix do
|
22
22
|
PREFIXES = {
|
23
|
-
'nick!user@host'
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
'nick!user@host' => {
|
24
|
+
nick: 'nick',
|
25
|
+
user: 'user',
|
26
|
+
host: 'host'
|
27
|
+
},
|
28
|
+
'nick!user' => {
|
29
|
+
nick: 'nick',
|
30
|
+
user: 'user',
|
31
|
+
host: nil
|
32
|
+
},
|
33
|
+
'nick@host' => {
|
34
|
+
nick: 'nick',
|
35
|
+
user: nil,
|
36
|
+
host: 'host'
|
37
|
+
},
|
38
|
+
'nick' => {
|
39
|
+
nick: 'nick',
|
40
|
+
user: nil,
|
41
|
+
host: nil
|
42
|
+
},
|
43
|
+
'irc.example.com' => {
|
44
|
+
nick: 'irc.example.com',
|
45
|
+
user: nil,
|
46
|
+
host: nil
|
47
|
+
},
|
28
48
|
}
|
29
49
|
|
30
50
|
PREFIXES.each do |serialized, deserialized|
|
31
51
|
describe 'when checking a valid prefix' do
|
32
52
|
before do
|
33
|
-
@
|
53
|
+
@text = ":#{serialized} COMMAND"
|
54
|
+
@message = IRCParser::Message.parse @text
|
34
55
|
end
|
35
56
|
it 'should consist of the correct components' do
|
36
57
|
%i(nick user host).each do |component|
|
37
58
|
if deserialized[component].nil?
|
38
|
-
@prefix.send(component).must_be_nil
|
59
|
+
@message.prefix.send(component).must_be_nil
|
39
60
|
else
|
40
|
-
@prefix.send(component).must_equal deserialized[component]
|
61
|
+
@message.prefix.send(component).must_equal deserialized[component]
|
41
62
|
end
|
42
63
|
end
|
43
64
|
end
|
44
65
|
it 'should serialise back to the same text' do
|
45
|
-
@
|
66
|
+
@message.to_s.must_equal @text
|
46
67
|
end
|
47
68
|
end
|
48
69
|
end
|
49
70
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
71
|
+
MALFORMED = [
|
72
|
+
'nick!@',
|
73
|
+
'!user@',
|
74
|
+
'!@host',
|
75
|
+
'nick!user@',
|
76
|
+
'nick!@host',
|
77
|
+
'!user@host',
|
78
|
+
]
|
79
|
+
|
80
|
+
MALFORMED.each do |prefix|
|
81
|
+
describe 'when checking an invalid user prefix' do
|
82
|
+
it 'should throw an IRCParser::Error when components are missing' do
|
83
|
+
proc {
|
84
|
+
IRCParser::Message.parse ":#{prefix} COMMAND"
|
85
|
+
}.must_raise IRCParser::Error
|
86
|
+
end
|
58
87
|
end
|
59
88
|
end
|
60
89
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ircparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter "SaberUK" Powell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|
@@ -68,6 +68,8 @@ files:
|
|
68
68
|
- lib/ircparser/error.rb
|
69
69
|
- lib/ircparser/message.rb
|
70
70
|
- lib/ircparser/prefix.rb
|
71
|
+
- lib/ircparser/stream.rb
|
72
|
+
- lib/ircparser/wire/rfc.rb
|
71
73
|
- test/test_message.rb
|
72
74
|
- test/test_prefix.rb
|
73
75
|
homepage: https://github.com/SaberUK/ircparser
|
@@ -90,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
92
|
version: '0'
|
91
93
|
requirements: []
|
92
94
|
rubyforge_project:
|
93
|
-
rubygems_version: 2.
|
95
|
+
rubygems_version: 2.7.5
|
94
96
|
signing_key:
|
95
97
|
specification_version: 4
|
96
98
|
summary: An IRCv3 message parser.
|