Mercurial > prosody-modules
comparison mod_ircd/mod_ircd.in.lua @ 491:5b3db688213d
mod_ircd: Fixed nick change logic (thanks mva), so that the self nick-change "flag" is removed properly, improved the logic to use verse's room_mt:change_nick (thanks Zash) yet to be pushed into main, added squished verse with the meta method included.
| author | Marco Cirillo <maranda@lightwitch.org> |
|---|---|
| date | Fri, 02 Dec 2011 20:53:09 +0000 |
| parents | 00b77a9f2d5f |
| children | 1a71e0e21a29 |
comparison
equal
deleted
inserted
replaced
| 490:00b77a9f2d5f | 491:5b3db688213d |
|---|---|
| 12 | 12 |
| 13 local component_jid, component_secret, muc_server, port_number = | 13 local component_jid, component_secret, muc_server, port_number = |
| 14 module.host, nil, module:get_option_string("conference_server"), module:get_option_number("listener_port", 7000); | 14 module.host, nil, module:get_option_string("conference_server"), module:get_option_number("listener_port", 7000); |
| 15 | 15 |
| 16 if not muc_server then | 16 if not muc_server then |
| 17 module:log ("error", "You need to set the MUC server! halting.") | 17 module:log ("error", "You need to set the MUC server! halting.") |
| 18 return false; | 18 return false; |
| 19 end | 19 end |
| 20 | 20 |
| 21 package.loaded["util.sha1"] = require "util.encodings"; | 21 package.loaded["util.sha1"] = require "util.encodings"; |
| 22 local verse = require "verse" | 22 local verse = require "verse" |
| 23 require "verse.component" | 23 require "verse.component" |
| 24 require "socket" | 24 require "socket" |
| 25 c = verse.new(); | 25 c = verse.new(); |
| 26 c:add_plugin("groupchat"); | 26 c:add_plugin("groupchat"); |
| 27 | 27 |
| 28 local function verse2prosody(e) | 28 local function verse2prosody(e) |
| 29 return c:event("stanza", e.stanza) or true; | 29 return c:event("stanza", e.stanza) or true; |
| 30 end | 30 end |
| 31 module:hook("message/bare", verse2prosody); | 31 module:hook("message/bare", verse2prosody); |
| 32 module:hook("message/full", verse2prosody); | 32 module:hook("message/full", verse2prosody); |
| 33 module:hook("presence/bare", verse2prosody); | 33 module:hook("presence/bare", verse2prosody); |
| 34 module:hook("presence/full", verse2prosody); | 34 module:hook("presence/full", verse2prosody); |
| 37 | 37 |
| 38 local jid = require "util.jid"; | 38 local jid = require "util.jid"; |
| 39 local nodeprep = require "util.encodings".stringprep.nodeprep; | 39 local nodeprep = require "util.encodings".stringprep.nodeprep; |
| 40 | 40 |
| 41 local function utf8_clean (s) | 41 local function utf8_clean (s) |
| 42 local push, join = table.insert, table.concat; | 42 local push, join = table.insert, table.concat; |
| 43 local r, i = {}, 1; | 43 local r, i = {}, 1; |
| 44 if not(s and #s > 0) then | 44 if not(s and #s > 0) then |
| 45 return "" | 45 return "" |
| 46 end | 46 end |
| 47 while true do | 47 while true do |
| 48 local c = s:sub(i,i) | 48 local c = s:sub(i,i) |
| 49 local b = c:byte(); | 49 local b = c:byte(); |
| 50 local w = ( | 50 local w = ( |
| 51 (b >= 9 and b <= 10 and 0) or | 51 (b >= 9 and b <= 10 and 0) or |
| 52 (b >= 32 and b <= 126 and 0) or | 52 (b >= 32 and b <= 126 and 0) or |
| 53 (b >= 192 and b <= 223 and 1) or | 53 (b >= 192 and b <= 223 and 1) or |
| 54 (b >= 224 and b <= 239 and 2) or | 54 (b >= 224 and b <= 239 and 2) or |
| 55 (b >= 240 and b <= 247 and 3) or | 55 (b >= 240 and b <= 247 and 3) or |
| 56 (b >= 248 and b <= 251 and 4) or | 56 (b >= 248 and b <= 251 and 4) or |
| 57 (b >= 251 and b <= 252 and 5) or nil | 57 (b >= 251 and b <= 252 and 5) or nil |
| 58 ) | 58 ) |
| 59 if not w then | 59 if not w then |
| 60 push(r, "?") | 60 push(r, "?") |
| 61 else | 61 else |
| 62 local n = i + w; | 62 local n = i + w; |
| 63 if w == 0 then | 63 if w == 0 then |
| 64 push(r, c); | 64 push(r, c); |
| 65 elseif n > #s then | 65 elseif n > #s then |
| 66 push(r, ("?"):format(b)); | 66 push(r, ("?"):format(b)); |
| 67 else | 67 else |
| 68 local e = s:sub(i+1,n); | 68 local e = s:sub(i+1,n); |
| 69 if e:match('^[\128-\191]*$') then | 69 if e:match('^[\128-\191]*$') then |
| 70 push(r, c); | 70 push(r, c); |
| 71 push(r, e); | 71 push(r, e); |
| 72 i = n; | 72 i = n; |
| 73 else | 73 else |
| 74 push(r, ("?"):format(b)); | 74 push(r, ("?"):format(b)); |
| 75 end | 75 end |
| 76 end | 76 end |
| 77 end | 77 end |
| 78 i = i + 1; | 78 i = i + 1; |
| 79 if i > #s then | 79 if i > #s then |
| 80 break | 80 break |
| 81 end | 81 end |
| 82 end | 82 end |
| 83 return join(r); | 83 return join(r); |
| 84 end | 84 end |
| 85 | 85 |
| 86 local function parse_line(line) | 86 local function parse_line(line) |
| 87 local ret = {}; | 87 local ret = {}; |
| 88 if line:sub(1,1) == ":" then | 88 if line:sub(1,1) == ":" then |
| 89 ret.from, line = line:match("^:(%w+)%s+(.*)$"); | 89 ret.from, line = line:match("^:(%w+)%s+(.*)$"); |
| 90 end | 90 end |
| 91 for part in line:gmatch("%S+") do | 91 for part in line:gmatch("%S+") do |
| 92 if part:sub(1,1) == ":" then | 92 if part:sub(1,1) == ":" then |
| 93 ret[#ret+1] = line:match(":(.*)$"); | 93 ret[#ret+1] = line:match(":(.*)$"); |
| 94 break | 94 break |
| 95 end | 95 end |
| 96 ret[#ret+1]=part; | 96 ret[#ret+1]=part; |
| 97 end | 97 end |
| 98 return ret; | 98 return ret; |
| 99 end | 99 end |
| 100 | 100 |
| 101 local function build_line(parts) | 101 local function build_line(parts) |
| 102 if #parts > 1 then | 102 if #parts > 1 then |
| 103 parts[#parts] = ":" .. parts[#parts]; | 103 parts[#parts] = ":" .. parts[#parts]; |
| 104 end | 104 end |
| 105 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); | 105 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); |
| 106 end | 106 end |
| 107 | 107 |
| 108 local function irc2muc(channel, nick) | 108 local function irc2muc(channel, nick) |
| 109 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; | 109 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; |
| 110 if not nick then | 110 if not nick then |
| 111 return jid.join(room, muc_server); | 111 return jid.join(room, muc_server); |
| 112 else | 112 else |
| 113 return jid.join(room, muc_server, nick); | 113 return jid.join(room, muc_server, nick); |
| 114 end | 114 end |
| 115 end | 115 end |
| 116 local function muc2irc(room) | 116 local function muc2irc(room) |
| 117 local channel, _, nick = jid.split(room); | 117 local channel, _, nick = jid.split(room); |
| 118 return "#"..channel, nick; | 118 return "#"..channel, nick; |
| 119 end | 119 end |
| 120 local role_map = { | 120 local role_map = { |
| 121 moderator = "@", | 121 moderator = "@", |
| 122 participant = "", | 122 participant = "", |
| 123 visitor = "", | 123 visitor = "", |
| 124 none = "" | 124 none = "" |
| 125 } | 125 } |
| 126 local aff_map = { | 126 local aff_map = { |
| 127 owner = "~", | 127 owner = "~", |
| 128 administrator = "&", | 128 administrator = "&", |
| 129 member = "+", | 129 member = "+", |
| 130 none = "" | 130 none = "" |
| 131 } | 131 } |
| 132 local role_modemap = { | 132 local role_modemap = { |
| 133 moderator = "o", | 133 moderator = "o", |
| 134 participant = "", | 134 participant = "", |
| 135 visitor = "", | 135 visitor = "", |
| 136 none = "" | 136 none = "" |
| 137 } | 137 } |
| 138 local aff_modemap = { | 138 local aff_modemap = { |
| 139 owner = "q", | 139 owner = "q", |
| 140 administrator = "a", | 140 administrator = "a", |
| 141 member = "v", | 141 member = "v", |
| 142 none = "" | 142 none = "" |
| 143 } | 143 } |
| 144 | 144 |
| 145 local irc_listener = { default_port = port_number, default_mode = "*l" }; | 145 local irc_listener = { default_port = port_number, default_mode = "*l" }; |
| 146 | 146 |
| 147 local sessions = {}; | 147 local sessions = {}; |
| 154 local st = require "util.stanza"; | 154 local st = require "util.stanza"; |
| 155 | 155 |
| 156 local conference_server = muc_server; | 156 local conference_server = muc_server; |
| 157 | 157 |
| 158 local function irc_close_session(session) | 158 local function irc_close_session(session) |
| 159 session.conn:close(); | 159 session.conn:close(); |
| 160 end | 160 end |
| 161 | 161 |
| 162 function irc_listener.onincoming(conn, data) | 162 function irc_listener.onincoming(conn, data) |
| 163 local session = sessions[conn]; | 163 local session = sessions[conn]; |
| 164 if not session then | 164 if not session then |
| 165 session = { conn = conn, host = component_jid, reset_stream = function () end, | 165 session = { conn = conn, host = component_jid, reset_stream = function () end, |
| 166 close = irc_close_session, log = logger.init("irc"..(conn.id or "1")), | 166 close = irc_close_session, log = logger.init("irc"..(conn.id or "1")), |
| 167 rooms = {}, roster = {}, has_un = false }; | 167 rooms = {}, roster = {}, has_un = false }; |
| 168 sessions[conn] = session; | 168 sessions[conn] = session; |
| 169 | 169 |
| 170 function session.data(data) | 170 function session.data(data) |
| 171 local parts = parse_line(data); | 171 local parts = parse_line(data); |
| 172 module:log("debug", require"util.serialization".serialize(parts)); | 172 module:log("debug", require"util.serialization".serialize(parts)); |
| 173 local command = table.remove(parts, 1); | 173 local command = table.remove(parts, 1); |
| 174 if not command then | 174 if not command then |
| 175 return; | 175 return; |
| 176 end | 176 end |
| 177 command = command:upper(); | 177 command = command:upper(); |
| 178 if not session.username and not session.nick then | 178 if not session.username and not session.nick then |
| 179 if not (command == "USER" or command == "NICK") then | 179 if not (command == "USER" or command == "NICK") then |
| 180 module:log("debug", "Client tried to send command %s before registering", command); | 180 module:log("debug", "Client tried to send command %s before registering", command); |
| 181 return session.send{from=muc_server, "451", command, "You have not completed the registration."} | 181 return session.send{from=muc_server, "451", command, "You have not completed the registration."} |
| 182 end | 182 end |
| 183 end | 183 end |
| 184 if commands[command] then | 184 if commands[command] then |
| 185 local ret = commands[command](session, parts); | 185 local ret = commands[command](session, parts); |
| 186 if ret then | 186 if ret then |
| 187 return session.send(ret); | 187 return session.send(ret); |
| 188 end | 188 end |
| 189 else | 189 else |
| 190 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; | 190 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; |
| 191 return module:log("debug", "Unknown command: %s", command); | 191 return module:log("debug", "Unknown command: %s", command); |
| 192 end | 192 end |
| 193 end | 193 end |
| 194 | 194 |
| 195 function session.send(data) | 195 function session.send(data) |
| 196 if type(data) == "string" then | 196 if type(data) == "string" then |
| 197 return conn:write(data.."\r\n"); | 197 return conn:write(data.."\r\n"); |
| 198 elseif type(data) == "table" then | 198 elseif type(data) == "table" then |
| 199 local line = build_line(data); | 199 local line = build_line(data); |
| 200 module:log("debug", line); | 200 module:log("debug", line); |
| 201 conn:write(line.."\r\n"); | 201 conn:write(line.."\r\n"); |
| 202 end | 202 end |
| 203 end | 203 end |
| 204 end | 204 end |
| 205 | 205 |
| 206 if data then | 206 if data then |
| 207 session.data(data); | 207 session.data(data); |
| 208 end | 208 end |
| 209 end | 209 end |
| 210 | 210 |
| 211 function irc_listener.ondisconnect(conn, error) | 211 function irc_listener.ondisconnect(conn, error) |
| 212 local session = sessions[conn]; | 212 local session = sessions[conn]; |
| 213 | 213 |
| 214 if session then | 214 if session then |
| 215 for _, room in pairs(session.rooms) do | 215 for _, room in pairs(session.rooms) do |
| 216 room:leave("Disconnected"); | 216 room:leave("Disconnected"); |
| 217 end | 217 end |
| 218 if session.nick then | 218 if session.nick then |
| 219 nicks[session.nick] = nil; | 219 nicks[session.nick] = nil; |
| 220 end | 220 end |
| 221 if session.full_jid then | 221 if session.full_jid then |
| 222 jids[session.full_jid] = nil; | 222 jids[session.full_jid] = nil; |
| 223 end | 223 end |
| 224 if session.username then | 224 if session.username then |
| 225 usernames[session.username] = nil; | 225 usernames[session.username] = nil; |
| 226 end | 226 end |
| 227 end | 227 end |
| 228 sessions[conn] = nil; | 228 sessions[conn] = nil; |
| 229 end | 229 end |
| 230 | 230 |
| 231 local function nick_inuse(nick) | 231 local function nick_inuse(nick) |
| 232 if nicks[nick] then return true else return false end | 232 if nicks[nick] then return true else return false end |
| 233 end | 233 end |
| 234 local function check_username(un) | 234 local function check_username(un) |
| 235 local count = 0; | 235 local count = 0; |
| 236 local result; | 236 local result; |
| 237 | 237 |
| 238 for name, given in pairs(usernames) do | 238 for name, given in pairs(usernames) do |
| 239 if un == given then count = count + 1; end | 239 if un == given then count = count + 1; end |
| 240 end | 240 end |
| 241 | 241 |
| 242 result = count + 1; | 242 result = count + 1; |
| 243 | 243 |
| 244 if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end | 244 if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end |
| 245 end | |
| 246 local function change_nick_st(fulljid, roomjid, tonick) | |
| 247 return st.presence({ from = fulljid, to = roomjid, type = "unavailable" }):tag("status"):text("Changing nickname to: "..tonick):up(); | |
| 248 end | 245 end |
| 249 local function set_t_data(session, full_jid) | 246 local function set_t_data(session, full_jid) |
| 250 session.full_jid = full_jid; | 247 session.full_jid = full_jid; |
| 251 jids[full_jid] = session; | 248 jids[full_jid] = session; |
| 252 jids[full_jid]["ar_last"] = {}; | 249 jids[full_jid]["ar_last"] = {}; |
| 253 jids[full_jid]["nicks_changing"] = {}; | 250 jids[full_jid]["nicks_changing"] = {}; |
| 254 | 251 |
| 255 if session.nick then nicks[session.nick] = session; end | 252 if session.nick then nicks[session.nick] = session; end |
| 256 end | 253 end |
| 257 local function send_motd(session) | 254 local function send_motd(session) |
| 258 local nick = session.nick; | 255 local nick = session.nick; |
| 259 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; | 256 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; |
| 260 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; | 257 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; |
| 261 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} | 258 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} |
| 262 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; | 259 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; |
| 263 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; | 260 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; |
| 264 session.send{from = muc_server, "372", nick, "-"}; | 261 session.send{from = muc_server, "372", nick, "-"}; |
| 265 session.send{from = muc_server, "372", nick, "- Please be warned that this is only a partial irc implementation,"}; | 262 session.send{from = muc_server, "372", nick, "- Please be warned that this is only a partial irc implementation,"}; |
| 266 session.send{from = muc_server, "372", nick, "- it's made to facilitate users transiting away from irc to XMPP."}; | 263 session.send{from = muc_server, "372", nick, "- it's made to facilitate users transiting away from irc to XMPP."}; |
| 267 session.send{from = muc_server, "372", nick, "-"}; | 264 session.send{from = muc_server, "372", nick, "-"}; |
| 268 session.send{from = muc_server, "372", nick, "- Prosody is _NOT_ an IRC Server and it never will."}; | 265 session.send{from = muc_server, "372", nick, "- Prosody is _NOT_ an IRC Server and it never will."}; |
| 269 session.send{from = muc_server, "372", nick, "- We also would like to remind you that this plugin is provided as is,"}; | 266 session.send{from = muc_server, "372", nick, "- We also would like to remind you that this plugin is provided as is,"}; |
| 270 session.send{from = muc_server, "372", nick, "- it's still an Alpha and it's still a work in progress, use it at your sole"}; | 267 session.send{from = muc_server, "372", nick, "- it's still an Alpha and it's still a work in progress, use it at your sole"}; |
| 271 session.send{from = muc_server, "372", nick, "- risk as there's a not so little chance something will break."}; | 268 session.send{from = muc_server, "372", nick, "- risk as there's a not so little chance something will break."}; |
| 272 | 269 |
| 273 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, | 270 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, |
| 274 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") | 271 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") |
| 275 end | 272 end |
| 276 | 273 |
| 277 function commands.NICK(session, args) | 274 function commands.NICK(session, args) |
| 278 local nick = args[1]; | 275 local nick = args[1]; |
| 279 nick = nick:gsub("[^%w_]",""); | 276 nick = nick:gsub("[^%w_]",""); |
| 280 | 277 |
| 281 if session.nick and not nick_inuse(nick) then -- changing nick | 278 if session.nick and not nick_inuse(nick) then -- changing nick |
| 282 local oldnick = session.nick; | 279 local oldnick = session.nick; |
| 283 | 280 |
| 284 -- update and replace session data | 281 -- update and replace session data |
| 285 session.nick = nick; | 282 session.nick = nick; |
| 286 nicks[oldnick] = nil; | 283 nicks[oldnick] = nil; |
| 287 nicks[nick] = session; | 284 nicks[nick] = session; |
| 288 | 285 |
| 289 session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; | 286 session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; |
| 290 | 287 |
| 291 -- broadcast changes if required | 288 -- broadcast changes if required |
| 292 if session.rooms then | 289 if session.rooms then |
| 293 for id, room in pairs(session.rooms) do | 290 session.nicks_changing[nick] = { oldnick, session.username }; |
| 294 session.nicks_changing[session.nick] = { oldnick, session.username }; | 291 |
| 295 | 292 for id, room in pairs(session.rooms) do room:change_nick(nick); end |
| 296 local node = jid.split(room.jid); | 293 |
| 297 local oldjid = jid.join(node, muc_server, session.nick); | 294 session.nicks_changing[nick] = nil; |
| 298 local room_name = room.jid | 295 end |
| 299 | 296 |
| 300 room:send(change_nick_st(session.full_jid, jid.join(node, muc_server, oldnick), session.nick)); | 297 return; |
| 301 local room, err = c:join_room(room_name, session.nick, { source = session.full_jid } ); | 298 elseif nick_inuse(nick) then |
| 302 if not room then | 299 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; |
| 303 session.send{from=nick.nick.."!"..session.username, "PART", id}; | 300 end |
| 304 return ":"..muc_server.." ERR :Failed to change nick and rejoin: "..err | 301 |
| 305 end | 302 session.nick = nick; |
| 306 end | 303 session.type = "c2s"; |
| 307 end | 304 nicks[nick] = session; |
| 308 | 305 |
| 309 return; | 306 -- Some choppy clients send in NICK before USER, that needs to be handled |
| 310 elseif nick_inuse(nick) then | 307 if session.username then |
| 311 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; | 308 set_t_data(session, jid.join(session.username, component_jid, "ircd")); |
| 312 end | 309 end |
| 313 | 310 |
| 314 session.nick = nick; | 311 if session.username and session.nick then -- send MOTD |
| 315 session.type = "c2s"; | 312 send_motd(session); |
| 316 nicks[nick] = session; | 313 end |
| 317 | |
| 318 -- Some choppy clients send in NICK before USER, that needs to be handled | |
| 319 if session.username then | |
| 320 set_t_data(session, jid.join(session.username, component_jid, "ircd")); | |
| 321 end | |
| 322 | |
| 323 if session.username and session.nick then -- send MOTD | |
| 324 send_motd(session); | |
| 325 end | |
| 326 end | 314 end |
| 327 | 315 |
| 328 function commands.USER(session, params) | 316 function commands.USER(session, params) |
| 329 local username = params[1]; | 317 local username = params[1]; |
| 330 | 318 |
| 331 if not session.has_un then | 319 if not session.has_un then |
| 332 local un_checked = check_username(username); | 320 local un_checked = check_username(username); |
| 333 | 321 |
| 334 usernames[un_checked] = username; | 322 usernames[un_checked] = username; |
| 335 session.username = un_checked; | 323 session.username = un_checked; |
| 336 session.has_un = true; | 324 session.has_un = true; |
| 337 | 325 |
| 338 if not session.full_jid then | 326 if not session.full_jid then |
| 339 set_t_data(session, jid.join(session.username, component_jid, "ircd")); | 327 set_t_data(session, jid.join(session.username, component_jid, "ircd")); |
| 340 end | 328 end |
| 341 else | 329 else |
| 342 return session.send{from=muc_server, "462", "USER", "You may not re-register."} | 330 return session.send{from=muc_server, "462", "USER", "You may not re-register."} |
| 343 end | 331 end |
| 344 | 332 |
| 345 if session.username and session.nick then -- send MOTD | 333 if session.username and session.nick then -- send MOTD |
| 346 send_motd(session); | 334 send_motd(session); |
| 347 end | 335 end |
| 348 end | 336 end |
| 349 | 337 |
| 350 local function mode_map(am, rm, nicks) | 338 local function mode_map(am, rm, nicks) |
| 351 local rnick; | 339 local rnick; |
| 352 local c_modes; | 340 local c_modes; |
| 353 c_modes = aff_modemap[am]..role_modemap[rm] | 341 c_modes = aff_modemap[am]..role_modemap[rm] |
| 354 rnick = string.rep(nicks.." ", c_modes:len()) | 342 rnick = string.rep(nicks.." ", c_modes:len()) |
| 355 if c_modes == "" then return nil, nil end | 343 if c_modes == "" then return nil, nil end |
| 356 return c_modes, rnick | 344 return c_modes, rnick |
| 357 end | 345 end |
| 358 | 346 |
| 359 function commands.JOIN(session, args) | 347 function commands.JOIN(session, args) |
| 360 local channel = args[1]; | 348 local channel = args[1]; |
| 361 if not channel then return end | 349 if not channel then return end |
| 362 local room_jid = irc2muc(channel); | 350 local room_jid = irc2muc(channel); |
| 363 | 351 |
| 364 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end | 352 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end |
| 365 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); | 353 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); |
| 366 if not room then | 354 if not room then |
| 367 return ":"..muc_server.." ERR :Could not join room: "..err | 355 return ":"..muc_server.." ERR :Could not join room: "..err |
| 368 end | 356 end |
| 369 | 357 |
| 370 session.rooms[channel] = room; | 358 session.rooms[channel] = room; |
| 371 room.session = session; | 359 room.session = session; |
| 372 | 360 |
| 373 if session.nicks_changing[session.nick] then -- my own nick is changing | 361 if session.nicks_changing[session.nick] then -- my own nick is changing |
| 374 commands.NAMES(session, channel); | 362 commands.NAMES(session, channel); |
| 375 else | 363 else |
| 376 session.send{from=session.nick.."!"..session.username, "JOIN", channel}; | 364 session.send{from=session.nick.."!"..session.username, "JOIN", channel}; |
| 377 if room.subject then | 365 if room.subject then |
| 378 session.send{from=muc_server, 332, session.nick, channel, room.subject}; | 366 session.send{from=muc_server, 332, session.nick, channel, room.subject}; |
| 379 end | 367 end |
| 380 commands.NAMES(session, channel); | 368 commands.NAMES(session, channel); |
| 381 end | 369 end |
| 382 | 370 |
| 383 room:hook("subject-changed", function(changed) | 371 room:hook("subject-changed", function(changed) |
| 384 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); | 372 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); |
| 385 end); | 373 end); |
| 386 | 374 |
| 387 room:hook("message", function(event) | 375 room:hook("message", function(event) |
| 388 if not event.body then return end | 376 if not event.body then return end |
| 389 local nick, body = event.nick, event.body; | 377 local nick, body = event.nick, event.body; |
| 390 if nick ~= session.nick then | 378 if nick ~= session.nick then |
| 391 if body:sub(1,4) == "/me " then | 379 if body:sub(1,4) == "/me " then |
| 392 body = "\1ACTION ".. body:sub(5) .. "\1" | 380 body = "\1ACTION ".. body:sub(5) .. "\1" |
| 393 end | 381 end |
| 394 local type = event.stanza.attr.type; | 382 local type = event.stanza.attr.type; |
| 395 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body}; | 383 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body}; |
| 396 --FIXME PM's probably won't work | 384 --FIXME PM's probably won't work |
| 397 end | 385 end |
| 398 end); | 386 end); |
| 399 | 387 |
| 400 room:hook("presence", function(ar) | 388 room:hook("presence", function(ar) |
| 401 local c_modes; | 389 local c_modes; |
| 402 local rnick; | 390 local rnick; |
| 403 if ar.nick and not jids[session.full_jid].ar_last[ar.room_jid][ar.nick] then jids[session.full_jid].ar_last[ar.room_jid][ar.nick] = {} end | 391 if ar.nick and not jids[session.full_jid].ar_last[ar.room_jid][ar.nick] then jids[session.full_jid].ar_last[ar.room_jid][ar.nick] = {} end |
| 404 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") | 392 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") |
| 405 if x_ar then | 393 if x_ar then |
| 406 local xar_item = x_ar:get_child("item") | 394 local xar_item = x_ar:get_child("item") |
| 407 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then | 395 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then |
| 408 if xar_item.attr.affiliation and xar_item.attr.role then | 396 if xar_item.attr.affiliation and xar_item.attr.role then |
| 409 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and | 397 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and |
| 410 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then | 398 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then |
| 411 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation | 399 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation |
| 412 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role | 400 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role |
| 413 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); | 401 n_self_changing = nicks[ar.nick] and nicks[ar.nick].nicks_changing and nicks[ar.nick].nicks_changing[ar.nick] |
| 414 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 402 if n_self_changing then return; end |
| 415 else | 403 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); |
| 416 c_modes, rnick = mode_map(jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"], jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"], ar.nick); | 404 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
| 417 if c_modes and rnick then session.send((":%s MODE %s -%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 405 else |
| 418 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation | 406 c_modes, rnick = mode_map(jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"], jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"], ar.nick); |
| 419 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role | 407 if c_modes and rnick then session.send((":%s MODE %s -%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
| 420 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); | 408 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation |
| 421 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 409 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role |
| 422 end | 410 n_self_changing = nicks[ar.nick] and nicks[ar.nick].nicks_changing and nicks[ar.nick].nicks_changing[ar.nick] |
| 423 end | 411 if n_self_changing then return; end |
| 424 end | 412 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); |
| 425 end | 413 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
| 426 end, -1); | 414 end |
| 415 end | |
| 416 end | |
| 417 end | |
| 418 end, -1); | |
| 427 end | 419 end |
| 428 | 420 |
| 429 c:hook("groupchat/joined", function(room) | 421 c:hook("groupchat/joined", function(room) |
| 430 local session = room.session or jids[room.opts.source]; | 422 local session = room.session or jids[room.opts.source]; |
| 431 local channel = "#"..room.jid:match("^(.*)@"); | 423 local channel = "#"..room.jid:match("^(.*)@"); |
| 432 | 424 |
| 433 room:hook("occupant-joined", function(nick) | 425 room:hook("occupant-joined", function(nick) |
| 434 if session.nicks_changing[nick.nick] then | 426 if session.nicks_changing[nick.nick] then |
| 435 session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; | 427 session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; |
| 436 session.nicks_changing[nick.nick] = nil; | 428 session.nicks_changing[nick.nick] = nil; |
| 437 else | 429 else |
| 438 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; | 430 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; |
| 439 end | 431 end |
| 440 end); | 432 end); |
| 441 room:hook("occupant-left", function(nick) | 433 room:hook("occupant-left", function(nick) |
| 442 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end | 434 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end |
| 443 local status_code = | 435 local status_code = |
| 444 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 436 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and |
| 445 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and | 437 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and |
| 446 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; | 438 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; |
| 447 | 439 |
| 448 | 440 |
| 449 if status_code == "303" then | 441 if status_code == "303" then |
| 450 local newnick = | 442 local newnick = |
| 451 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 443 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and |
| 452 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and | 444 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and |
| 453 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; | 445 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; |
| 454 | 446 |
| 455 session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; | 447 session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; |
| 456 end | 448 end |
| 457 | 449 |
| 458 local self_change = false; | 450 for id, data in pairs(session.nicks_changing) do |
| 459 for _, data in pairs(session.nicks_changing) do | 451 if data[1] == nick.nick then return; end |
| 460 if data[1] == nick.nick then self_change = true; break; end | 452 end |
| 461 end | 453 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "PART", channel}; |
| 462 if self_change then return; end | 454 end); |
| 463 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "PART", channel}; | |
| 464 end); | |
| 465 end); | 455 end); |
| 466 | 456 |
| 467 function commands.NAMES(session, channel) | 457 function commands.NAMES(session, channel) |
| 468 local nicks = { }; | 458 local nicks = { }; |
| 469 if type(channel) == "table" then channel = channel[1] end | 459 if type(channel) == "table" then channel = channel[1] end |
| 470 | 460 |
| 471 local room = session.rooms[channel]; | 461 local room = session.rooms[channel]; |
| 472 | 462 |
| 473 local symbols_map = { | 463 local symbols_map = { |
| 474 owner = "~", | 464 owner = "~", |
| 475 administrator = "&", | 465 administrator = "&", |
| 476 moderator = "@", | 466 moderator = "@", |
| 477 member = "+" | 467 member = "+" |
| 478 } | 468 } |
| 479 | 469 |
| 480 if not room then return end | 470 if not room then return end |
| 481 -- TODO Break this out into commands.NAMES | 471 -- TODO Break this out into commands.NAMES |
| 482 for nick, n in pairs(room.occupants) do | 472 for nick, n in pairs(room.occupants) do |
| 483 if n.affiliation == "owner" and n.role == "moderator" then | 473 if n.affiliation == "owner" and n.role == "moderator" then |
| 484 nick = symbols_map[n.affiliation]..nick; | 474 nick = symbols_map[n.affiliation]..nick; |
| 485 elseif n.affiliation == "administrator" and n.role == "moderator" then | 475 elseif n.affiliation == "administrator" and n.role == "moderator" then |
| 486 nick = symbols_map[n.affiliation]..nick; | 476 nick = symbols_map[n.affiliation]..nick; |
| 487 elseif n.affiliation == "member" and n.role == "moderator" then | 477 elseif n.affiliation == "member" and n.role == "moderator" then |
| 488 nick = symbols_map[n.role]..nick; | 478 nick = symbols_map[n.role]..nick; |
| 489 elseif n.affiliation == "member" and n.role == "partecipant" then | 479 elseif n.affiliation == "member" and n.role == "partecipant" then |
| 490 nick = symbols_map[n.affiliation]..nick; | 480 nick = symbols_map[n.affiliation]..nick; |
| 491 elseif n.affiliation == "none" and n.role == "moderator" then | 481 elseif n.affiliation == "none" and n.role == "moderator" then |
| 492 nick = symbols_map[n.role]..nick; | 482 nick = symbols_map[n.role]..nick; |
| 493 end | 483 end |
| 494 table.insert(nicks, nick); | 484 table.insert(nicks, nick); |
| 495 end | 485 end |
| 496 nicks = table.concat(nicks, " "); | 486 nicks = table.concat(nicks, " "); |
| 497 session.send((":%s 353 %s = %s :%s"):format(muc_server, session.nick, channel, nicks)); | 487 session.send((":%s 353 %s = %s :%s"):format(muc_server, session.nick, channel, nicks)); |
| 498 session.send((":%s 366 %s %s :End of /NAMES list."):format(muc_server, session.nick, channel)); | 488 session.send((":%s 366 %s %s :End of /NAMES list."):format(muc_server, session.nick, channel)); |
| 499 session.send(":"..muc_server.." 353 "..session.nick.." = "..channel.." :"..nicks); | 489 session.send(":"..muc_server.." 353 "..session.nick.." = "..channel.." :"..nicks); |
| 500 end | 490 end |
| 501 | 491 |
| 502 function commands.PART(session, args) | 492 function commands.PART(session, args) |
| 503 local channel, part_message = unpack(args); | 493 local channel, part_message = unpack(args); |
| 504 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; | 494 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; |
| 505 if not room then return end | 495 if not room then return end |
| 506 channel = channel:match("^([%S]*)"); | 496 channel = channel:match("^([%S]*)"); |
| 507 session.rooms[channel]:leave(part_message); | 497 session.rooms[channel]:leave(part_message); |
| 508 jids[session.full_jid].ar_last[room.."@"..muc_server] = nil; | 498 jids[session.full_jid].ar_last[room.."@"..muc_server] = nil; |
| 509 session.send(":"..session.nick.." PART :"..channel); | 499 session.send(":"..session.nick.." PART :"..channel); |
| 510 end | 500 end |
| 511 | 501 |
| 512 function commands.PRIVMSG(session, args) | 502 function commands.PRIVMSG(session, args) |
| 513 local channel, message = unpack(args); | 503 local channel, message = unpack(args); |
| 514 if message and #message > 0 then | 504 if message and #message > 0 then |
| 515 if message:sub(1,8) == "\1ACTION " then | 505 if message:sub(1,8) == "\1ACTION " then |
| 516 message = "/me ".. message:sub(9,-2) | 506 message = "/me ".. message:sub(9,-2) |
| 517 end | 507 end |
| 518 message = utf8_clean(message); | 508 message = utf8_clean(message); |
| 519 if channel:sub(1,1) == "#" then | 509 if channel:sub(1,1) == "#" then |
| 520 if session.rooms[channel] then | 510 if session.rooms[channel] then |
| 521 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); | 511 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); |
| 522 session.rooms[channel]:send_message(message); | 512 session.rooms[channel]:send_message(message); |
| 523 end | 513 end |
| 524 else -- private message | 514 else -- private message |
| 525 local nick = channel; | 515 local nick = channel; |
| 526 module:log("debug", "PM to %s", nick); | 516 module:log("debug", "PM to %s", nick); |
| 527 for channel, room in pairs(session.rooms) do | 517 for channel, room in pairs(session.rooms) do |
| 528 module:log("debug", "looking for %s in %s", nick, channel); | 518 module:log("debug", "looking for %s in %s", nick, channel); |
| 529 if room.occupants[nick] then | 519 if room.occupants[nick] then |
| 530 module:log("debug", "found %s in %s", nick, channel); | 520 module:log("debug", "found %s in %s", nick, channel); |
| 531 local who = room.occupants[nick]; | 521 local who = room.occupants[nick]; |
| 532 -- FIXME PMs in verse | 522 -- FIXME PMs in verse |
| 533 --room:send_private_message(nick, message); | 523 --room:send_private_message(nick, message); |
| 534 local pm = st.message({type="chat",to=who.jid}, message); | 524 local pm = st.message({type="chat",to=who.jid}, message); |
| 535 module:log("debug", "sending PM to %s: %s", nick, tostring(pm)); | 525 module:log("debug", "sending PM to %s: %s", nick, tostring(pm)); |
| 536 room:send(pm) | 526 room:send(pm) |
| 537 break | 527 break |
| 538 end | 528 end |
| 539 end | 529 end |
| 540 end | 530 end |
| 541 end | 531 end |
| 542 end | 532 end |
| 543 | 533 |
| 544 function commands.PING(session, args) | 534 function commands.PING(session, args) |
| 545 session.send{from=muc_server, "PONG", args[1]}; | 535 session.send{from=muc_server, "PONG", args[1]}; |
| 546 end | 536 end |
| 547 | 537 |
| 548 function commands.TOPIC(session, message) | 538 function commands.TOPIC(session, message) |
| 549 if not message then return end | 539 if not message then return end |
| 550 local channel, topic = message[1], message[2]; | 540 local channel, topic = message[1], message[2]; |
| 551 channel = utf8_clean(channel); | 541 channel = utf8_clean(channel); |
| 552 topic = utf8_clean(topic); | 542 topic = utf8_clean(topic); |
| 553 if not channel then return end | 543 if not channel then return end |
| 554 local room = session.rooms[channel]; | 544 local room = session.rooms[channel]; |
| 555 | 545 |
| 556 if topic then room:set_subject(topic); end | 546 if topic then room:set_subject(topic); end |
| 557 end | 547 end |
| 558 | 548 |
| 559 function commands.WHO(session, args) | 549 function commands.WHO(session, args) |
| 560 local channel = args[1]; | 550 local channel = args[1]; |
| 561 if session.rooms[channel] then | 551 if session.rooms[channel] then |
| 562 local room = session.rooms[channel] | 552 local room = session.rooms[channel] |
| 563 for nick in pairs(room.occupants) do | 553 for nick in pairs(room.occupants) do |
| 564 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} | 554 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} |
| 565 end | 555 end |
| 566 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; | 556 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; |
| 567 end | 557 end |
| 568 end | 558 end |
| 569 | 559 |
| 570 function commands.MODE(session, args) -- Empty command | 560 function commands.MODE(session, args) -- Empty command |
| 571 end | 561 end |
| 572 | 562 |
| 573 function commands.QUIT(session, args) | 563 function commands.QUIT(session, args) |
| 574 session.send{"ERROR", "Closing Link: "..session.nick}; | 564 session.send{"ERROR", "Closing Link: "..session.nick}; |
| 575 for _, room in pairs(session.rooms) do | 565 for _, room in pairs(session.rooms) do |
| 576 room:leave(args[1]); | 566 room:leave(args[1]); |
| 577 end | 567 end |
| 578 jids[session.full_jid] = nil; | 568 jids[session.full_jid] = nil; |
| 579 nicks[session.nick] = nil; | 569 nicks[session.nick] = nil; |
| 580 usernames[session.username] = nil; | 570 usernames[session.username] = nil; |
| 581 sessions[session.conn] = nil; | 571 sessions[session.conn] = nil; |
| 582 session:close(); | 572 session:close(); |
| 583 end | 573 end |
| 584 | 574 |
| 585 function commands.RAW(session, data) -- Empty command | 575 function commands.RAW(session, data) -- Empty command |
| 586 end | 576 end |
| 587 | 577 |
| 588 local function desetup() | 578 local function desetup() |
| 589 require "net.connlisteners".deregister("irc"); | 579 require "net.connlisteners".deregister("irc"); |
| 590 end | 580 end |
| 591 | 581 |
| 592 require "net.connlisteners".register("irc", irc_listener); | 582 require "net.connlisteners".register("irc", irc_listener); |
| 593 require "net.connlisteners".start("irc"); | 583 require "net.connlisteners".start("irc"); |
| 594 | 584 |
| 595 module:hook("module-unloaded", desetup) | 585 module:hook("module-unloaded", desetup) |
| 596 |
