Add support for tag escapes

This commit is contained in:
ShadowNinja 2014-05-30 22:46:37 -04:00
parent 81d28bf4dc
commit c27e2ec7e9
3 changed files with 71 additions and 61 deletions

View File

@ -178,7 +178,9 @@ function meta:think()
local line = getline(self, 3)
if line and #line > 0 then
if not self:invoke("OnRaw", line) then
self:handle(parse(line))
local msg = Message()
msg:fromRFC1459(line)
self:handle(msg)
end
else
break
@ -227,7 +229,8 @@ function meta:whois(nick)
while true do
local line = getline(self, 3)
if line then
local msg = parse(line)
local msg = Message()
msg:fromRFC1249(line)
local handler = whoisHandlers[msg.command]
if handler then

View File

@ -2,6 +2,7 @@ local assert = assert
local setmetatable = setmetatable
local unpack = unpack
local pairs = pairs
local insert = table.insert
module "irc"
@ -17,6 +18,18 @@ function Message(cmd, args)
}, msg_meta)
end
local tag_escapes = {
[";"] = "\\:",
[" "] = "\\s",
["\0"] = "\\0",
["\\"] = "\\\\",
["\r"] = "\\r",
["\n"] = "\\n",
}
local tag_unescapes = {}
for x, y in pairs(tag_escapes) do tag_unescapes[y] = x end
function msg_meta:toRFC1459()
s = ""
@ -25,10 +38,7 @@ function msg_meta:toRFC1459()
for key, value in pairs(self.tags) do
s = s..key
if value ~= true then
assert(not value:find("[%z\07\r\n; ]"),
"NUL, BELL, CR, LF, semicolon, and"
.." space are not allowed in RFC1459"
.." formated tag values.")
value = value:gsub("[; %z\\\r\n]", tag_escapes)
s = s.."="..value
end
s = s..";"
@ -59,6 +69,55 @@ function msg_meta:toRFC1459()
return s
end
local function parsePrefix(prefix)
local user = {}
user.nick, user.username, user.host = prefix:match("^(.+)!(.+)@(.+)$")
if not user.nick and prefix:find(".", 1, true) then
user.server = prefix
end
return user
end
function msg_meta:fromRFC1459(line)
-- IRCv3 tags
if line:sub(1, 1) == "@" then
self.tags = {}
local space = line:find(" ", 1, true)
-- For each semicolon-delimited section from after
-- the @ character to before the space character.
for tag in line:sub(2, space - 1):gmatch("([^;]+)") do
local eq = tag:find("=", 1, true)
if eq then
self.tags[tag:sub(1, eq - 1)] =
tag:sub(eq + 1):gsub("\\([:s0\\rn])", tag_unescapes)
else
self.tags[tag] = true
end
end
line = line:sub(space + 1)
end
if line:sub(1, 1) == ":" then
local space = line:find(" ", 1, true)
self.prefix = line:sub(2, space - 1)
self.user = parsePrefix(self.prefix)
line = line:sub(space + 1)
end
local pos
self.command, pos = line:match("(%S+)()")
line = line:sub(pos)
for pos, param in line:gmatch("()(%S+)") do
if param:sub(1, 1) == ":" then
param = line:sub(pos + 1)
insert(self.args, param)
break
end
insert(self.args, param)
end
end
function msgs.privmsg(to, text)
return Message("PRIVMSG", {to, text})
end
@ -135,9 +194,9 @@ end
function msgs.mode(target, modes)
-- We have to split the modes parameter because the mode string and
-- each parameter are seperate arguments (The first command is incorrect)
-- MODE :+ov Nick1 Nick2
-- MODE +ov Nick1 Nick2
mt = split(modes)
-- MODE foo :+ov Nick1 Nick2
-- MODE foo +ov Nick1 Nick2
local mt = split(modes)
return Message("MODE", {target, unpack(mt)})
end

View File

@ -10,49 +10,6 @@ local random = math.random
module "irc"
-- Protocol parsing
function parse(line)
local msg = Message()
-- IRCv3 tags
if line:sub(1, 1) == "@" then
msg.tags = {}
local space = line:find(" ", 1, true)
-- For each semicolon-delimited section from after
-- the @ character to before the space character.
for tag in line:sub(2, space - 1):gmatch("([^;]+)") do
local eq = tag:find("=", 1, true)
if eq then
msg.tags[tag:sub(1, eq - 1)] = tag:sub(eq + 1)
else
msg.tags[tag] = true
end
end
line = line:sub(space + 1)
end
if line:sub(1, 1) == ":" then
local space = line:find(" ", 1, true)
msg.prefix = line:sub(2, space - 1)
msg.user = parsePrefix(msg.prefix)
line = line:sub(space + 1)
end
local pos
msg.command, pos = line:match("(%S+)()")
line = line:sub(pos)
for pos, param in line:gmatch("()(%S+)") do
if param:sub(1, 1) == ":" then
param = line:sub(pos + 1)
table.insert(msg.args, param)
break
end
table.insert(msg.args, param)
end
return msg
end
function parseNick(conn, nick)
local access = {}
@ -73,15 +30,6 @@ function parseNick(conn, nick)
return access, name
end
function parsePrefix(prefix)
local user = {}
user.nick, user.username, user.host = prefix:match("^(.+)!(.+)@(.+)$")
if not user.nick and prefix:find(".", 1, true) then
user.server = prefix
end
return user
end
function updatePrefixModes(conn)
if conn.prefixmode and conn.modeprefix then
return