Mercurial > prosody-modules
comparison mod_ircd/mod_ircd.in.lua @ 487:8bdab5489653
mod_ircd: code cleanup, added full logic for changing nicks (locally it works no traces), removed many comment lines (there was an over abundance of 'em they're in the .old_annotate file)
author | Marco Cirillo <maranda@lightwitch.org> |
---|---|
date | Fri, 02 Dec 2011 01:03:06 +0000 |
parents | f8cc2be7e16a |
children | 4885ca74515c |
comparison
equal
deleted
inserted
replaced
486:b84493ef1d1d | 487:8bdab5489653 |
---|---|
2 -- Squish verse into this dir, then squish them into one, which you move | 2 -- Squish verse into this dir, then squish them into one, which you move |
3 -- and rename to mod_ircd.lua in your prosody modules/plugins dir. | 3 -- and rename to mod_ircd.lua in your prosody modules/plugins dir. |
4 -- | 4 -- |
5 -- IRC spec: | 5 -- IRC spec: |
6 -- http://tools.ietf.org/html/rfc2812 | 6 -- http://tools.ietf.org/html/rfc2812 |
7 | |
7 local _module = module | 8 local _module = module |
8 module = _G.module | 9 module = _G.module |
9 local module = _module | 10 local module = _module |
10 -- | 11 local client_xmlns = "jabber:client" |
12 | |
11 local component_jid, component_secret, muc_server, port_number = | 13 local component_jid, component_secret, muc_server, port_number = |
12 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); |
13 | 15 |
14 if not muc_server then | 16 if not muc_server then |
15 module:log ("error", "You need to set the MUC server! halting.") | 17 module:log ("error", "You need to set the MUC server! halting.") |
18 | 20 |
19 package.loaded["util.sha1"] = require "util.encodings"; | 21 package.loaded["util.sha1"] = require "util.encodings"; |
20 local verse = require "verse" | 22 local verse = require "verse" |
21 require "verse.component" | 23 require "verse.component" |
22 require "socket" | 24 require "socket" |
23 c = verse.new();--verse.logger()) | 25 c = verse.new(); |
24 c:add_plugin("groupchat"); | 26 c:add_plugin("groupchat"); |
25 | 27 |
26 local function verse2prosody(e) | 28 local function verse2prosody(e) |
27 return c:event("stanza", e.stanza) or true; | 29 return c:event("stanza", e.stanza) or true; |
28 end | 30 end |
30 module:hook("message/full", verse2prosody); | 32 module:hook("message/full", verse2prosody); |
31 module:hook("presence/bare", verse2prosody); | 33 module:hook("presence/bare", verse2prosody); |
32 module:hook("presence/full", verse2prosody); | 34 module:hook("presence/full", verse2prosody); |
33 c.type = "component"; | 35 c.type = "component"; |
34 c.send = core_post_stanza; | 36 c.send = core_post_stanza; |
35 | |
36 -- This plugin is actually a verse based component, but that mode is currently commented out | |
37 | |
38 -- Add some hooks for debugging | |
39 --c:hook("opened", function () print("Stream opened!") end); | |
40 --c:hook("closed", function () print("Stream closed!") end); | |
41 --c:hook("stanza", function (stanza) print("Stanza:", stanza) end); | |
42 | |
43 -- This one prints all received data | |
44 --c:hook("incoming-raw", print, 1000); | |
45 --c:hook("stanza", print, 1000); | |
46 --c:hook("outgoing-raw", print, 1000); | |
47 | |
48 -- Print a message after authentication | |
49 --c:hook("authentication-success", function () print("Logged in!"); end); | |
50 --c:hook("authentication-failure", function (err) print("Failed to log in! Error: "..tostring(err.condition)); end); | |
51 | |
52 -- Print a message and exit when disconnected | |
53 --c:hook("disconnected", function () print("Disconnected!"); os.exit(); end); | |
54 | |
55 -- Now, actually start the connection: | |
56 --c.connect_host = "127.0.0.1" | |
57 --c:connect_component(component_jid, component_secret); | |
58 | 37 |
59 local jid = require "util.jid"; | 38 local jid = require "util.jid"; |
60 local nodeprep = require "util.encodings".stringprep.nodeprep; | 39 local nodeprep = require "util.encodings".stringprep.nodeprep; |
61 | 40 |
62 local function utf8_clean (s) | 41 local function utf8_clean (s) |
126 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); | 105 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); |
127 end | 106 end |
128 | 107 |
129 local function irc2muc(channel, nick) | 108 local function irc2muc(channel, nick) |
130 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; | 109 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; |
131 return jid.join(room, muc_server, nick) | 110 if not nick then |
111 return jid.join(room, muc_server); | |
112 else | |
113 return jid.join(room, muc_server, nick); | |
114 end | |
132 end | 115 end |
133 local function muc2irc(room) | 116 local function muc2irc(room) |
134 local channel, _, nick = jid.split(room); | 117 local channel, _, nick = jid.split(room); |
135 return "#"..channel, nick; | 118 return "#"..channel, nick; |
136 end | 119 end |
181 session = { conn = conn, host = component_jid, reset_stream = function () end, | 164 session = { conn = conn, host = component_jid, reset_stream = function () end, |
182 close = irc_close_session, log = logger.init("irc"..(conn.id or "1")), | 165 close = irc_close_session, log = logger.init("irc"..(conn.id or "1")), |
183 rooms = {}, | 166 rooms = {}, |
184 roster = {} }; | 167 roster = {} }; |
185 sessions[conn] = session; | 168 sessions[conn] = session; |
169 | |
186 function session.data(data) | 170 function session.data(data) |
187 local parts = parse_line(data); | 171 local parts = parse_line(data); |
188 module:log("debug", require"util.serialization".serialize(parts)); | 172 module:log("debug", require"util.serialization".serialize(parts)); |
189 local command = table.remove(parts, 1); | 173 local command = table.remove(parts, 1); |
190 if not command then | 174 if not command then |
205 else | 189 else |
206 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; | 190 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; |
207 return module:log("debug", "Unknown command: %s", command); | 191 return module:log("debug", "Unknown command: %s", command); |
208 end | 192 end |
209 end | 193 end |
194 | |
210 function session.send(data) | 195 function session.send(data) |
211 if type(data) == "string" then | 196 if type(data) == "string" then |
212 return conn:write(data.."\r\n"); | 197 return conn:write(data.."\r\n"); |
213 elseif type(data) == "table" then | 198 elseif type(data) == "table" then |
214 local line = build_line(data); | 199 local line = build_line(data); |
215 module:log("debug", line); | 200 module:log("debug", line); |
216 conn:write(line.."\r\n"); | 201 conn:write(line.."\r\n"); |
217 end | 202 end |
218 end | 203 end |
219 end | 204 end |
205 | |
220 if data then | 206 if data then |
221 session.data(data); | 207 session.data(data); |
222 end | 208 end |
223 end | 209 end |
224 | 210 |
225 function irc_listener.ondisconnect(conn, error) | 211 function irc_listener.ondisconnect(conn, error) |
226 local session = sessions[conn]; | 212 local session = sessions[conn]; |
213 | |
227 if session then | 214 if session then |
228 for _, room in pairs(session.rooms) do | 215 for _, room in pairs(session.rooms) do |
229 room:leave("Disconnected"); | 216 room:leave("Disconnected"); |
230 end | 217 end |
231 if session.nick then | 218 if session.nick then |
236 end | 223 end |
237 end | 224 end |
238 sessions[conn] = nil; | 225 sessions[conn] = nil; |
239 end | 226 end |
240 | 227 |
228 local function nick_inuse(nick) | |
229 if nicks[nick] then return true else return false end | |
230 end | |
231 local function change_nick_st(jid, room_jid, newnick) | |
232 return st.presence({ xmlns = xmlns_client, from = jid, to = room_jid, type = "unavailable" }):tag("status"):text("Changing nickname to "..newnick):up(); | |
233 end | |
234 | |
241 function commands.NICK(session, args) | 235 function commands.NICK(session, args) |
242 if session.nick then | |
243 session.send{from = muc_server, "484", "*", nick, "I'm afraid I can't let you do that"}; | |
244 --TODO Loop throug all rooms and change nick, with help from Verse. | |
245 return; | |
246 end | |
247 local nick = args[1]; | 236 local nick = args[1]; |
248 nick = nick:gsub("[^%w_]",""); | 237 nick = nick:gsub("[^%w_]",""); |
249 if nicks[nick] then | 238 local full_jid = jid.join(nick, component_jid, "ircd"); |
250 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; | 239 |
240 if session.nick and not nick_inuse(nick) then -- changing nick | |
241 local oldnick = session.nick; | |
242 local old_full_jid = session.full_jid; | |
243 local old_ar_last = jids[old_full_jid]["ar_last"]; | |
244 local old_nicks_changing = jids[old_full_jid]["nicks_changing"]; | |
245 | |
246 -- update and replace session data | |
247 session.nick = nick; | |
248 session.full_jid = full_jid; | |
249 jids[old_full_jid] = nil; nicks[oldnick] = nil; | |
250 nicks[nick] = session; | |
251 jids[full_jid] = session; | |
252 jids[full_jid]["ar_last"] = old_ar_last; | |
253 jids[full_jid]["nicks_changing"] = old_nicks_changing; | |
254 | |
255 session.send{from=oldnick, "NICK", nick}; | |
256 | |
257 -- broadcast changes if required, todo: something better then forcing parting and rejoining | |
258 if session.rooms then | |
259 for id, room in pairs(session.rooms) do | |
260 session.nicks_changing[session.nick] = oldnick; | |
261 room:send(change_nick_st(old_full_jid, room.jid.."/"..oldnick, session.nick)); | |
262 commands.JOIN(session, { id }); | |
263 end | |
264 session.nicks_changing[session.nick] = nil; | |
265 end | |
266 | |
251 return; | 267 return; |
252 end | 268 elseif nick_inuse(nick) then |
253 local full_jid = jid.join(nick, component_jid, "ircd"); | 269 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; |
270 end | |
271 | |
254 jids[full_jid] = session; | 272 jids[full_jid] = session; |
255 jids[full_jid]["ar_last"] = {}; | 273 jids[full_jid]["ar_last"] = {}; |
274 jids[full_jid]["nicks_changing"] = {}; | |
256 nicks[nick] = session; | 275 nicks[nick] = session; |
257 session.nick = nick; | 276 session.nick = nick; |
258 session.full_jid = full_jid; | 277 session.full_jid = full_jid; |
259 session.type = "c2s"; | 278 session.type = "c2s"; |
260 | 279 |
274 | 293 |
275 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, | 294 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, |
276 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") | 295 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") |
277 end | 296 end |
278 | 297 |
279 function commands.USER(session, params) | 298 function commands.USER(session, params) -- To be done. |
280 -- FIXME | |
281 -- Empty command for now | |
282 end | 299 end |
283 | 300 |
284 local function mode_map(am, rm, nicks) | 301 local function mode_map(am, rm, nicks) |
285 local rnick; | 302 local rnick; |
286 local c_modes; | 303 local c_modes; |
292 | 309 |
293 function commands.JOIN(session, args) | 310 function commands.JOIN(session, args) |
294 local channel = args[1]; | 311 local channel = args[1]; |
295 if not channel then return end | 312 if not channel then return end |
296 local room_jid = irc2muc(channel); | 313 local room_jid = irc2muc(channel); |
297 print(session.full_jid); | 314 |
298 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end | 315 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end |
299 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); | 316 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); |
300 if not room then | 317 if not room then |
301 return ":"..muc_server.." ERR :Could not join room: "..err | 318 return ":"..muc_server.." ERR :Could not join room: "..err |
302 end | 319 end |
320 | |
303 session.rooms[channel] = room; | 321 session.rooms[channel] = room; |
304 room.channel = channel; | |
305 room.session = session; | 322 room.session = session; |
306 session.send{from=session.nick, "JOIN", channel}; | 323 |
307 if room.subject then | 324 if session.nicks_changing[session.nick] then -- my own nick is changing |
308 session.send{from=muc_server, 332, session.nick, channel ,room.subject}; | 325 commands.NAMES(session, channel); |
326 else | |
327 session.send{from=session.nick, "JOIN", channel}; | |
328 if room.subject then | |
329 session.send{from=muc_server, 332, session.nick, channel, room.subject}; | |
330 end | |
331 commands.NAMES(session, channel); | |
309 end | 332 end |
310 commands.NAMES(session, channel); | |
311 | 333 |
312 room:hook("subject-changed", function(changed) | 334 room:hook("subject-changed", function(changed) |
313 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); | 335 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); |
314 end); | 336 end); |
315 | 337 |
330 local c_modes; | 352 local c_modes; |
331 local rnick; | 353 local rnick; |
332 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 | 354 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 |
333 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") | 355 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") |
334 if x_ar then | 356 if x_ar then |
335 local xar_item = x_ar:get_child("item") | 357 local xar_item = x_ar:get_child("item") |
336 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then | 358 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then |
337 if xar_item.attr.affiliation and xar_item.attr.role then | 359 if xar_item.attr.affiliation and xar_item.attr.role then |
338 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and | 360 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and |
339 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then | 361 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then |
340 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation | 362 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation |
356 end | 378 end |
357 | 379 |
358 c:hook("groupchat/joined", function(room) | 380 c:hook("groupchat/joined", function(room) |
359 local session = room.session or jids[room.opts.source]; | 381 local session = room.session or jids[room.opts.source]; |
360 local channel = "#"..room.jid:match("^(.*)@"); | 382 local channel = "#"..room.jid:match("^(.*)@"); |
361 session.send{from=session.nick.."!"..session.nick, "JOIN", channel}; | 383 |
362 if room.topic then | |
363 session.send{from=muc_server, 332, room.topic}; | |
364 end | |
365 commands.NAMES(session, channel) | |
366 room:hook("occupant-joined", function(nick) | 384 room:hook("occupant-joined", function(nick) |
367 session.send{from=nick.nick.."!"..nick.nick, "JOIN", channel}; | 385 if session.nicks_changing[nick.nick] then |
386 session.send{from=session.nicks_changing[nick.nick], "NICK", nick.nick}; | |
387 session.nicks_changing[nick.nick] = nil; | |
388 else | |
389 session.send{from=nick.nick, "JOIN", channel}; | |
390 end | |
368 end); | 391 end); |
369 room:hook("occupant-left", function(nick) | 392 room:hook("occupant-left", function(nick) |
370 jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; -- ugly | 393 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end |
371 session.send{from=nick.nick.."!"..nick.nick, "PART", channel}; | 394 local status_code = |
395 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | |
396 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and | |
397 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; | |
398 | |
399 if status_code == "303" then | |
400 local newnick = | |
401 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | |
402 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and | |
403 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item") and | |
404 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item").attr.nick; | |
405 | |
406 session.nicks_changing[newnick] = nick.nick; return; | |
407 end | |
408 session.send{from=nick.nick, "PART", channel}; | |
372 end); | 409 end); |
373 end); | 410 end); |
374 | 411 |
375 function commands.NAMES(session, channel) | 412 function commands.NAMES(session, channel) |
376 local nicks = { }; | 413 local nicks = { }; |
414 if type(channel) == "table" then channel = channel[1] end | |
377 local room = session.rooms[channel]; | 415 local room = session.rooms[channel]; |
416 | |
378 local symbols_map = { | 417 local symbols_map = { |
379 owner = "~", | 418 owner = "~", |
380 administrator = "&", | 419 administrator = "&", |
381 moderator = "@", | 420 moderator = "@", |
382 member = "+" | 421 member = "+" |
464 function commands.WHO(session, args) | 503 function commands.WHO(session, args) |
465 local channel = args[1]; | 504 local channel = args[1]; |
466 if session.rooms[channel] then | 505 if session.rooms[channel] then |
467 local room = session.rooms[channel] | 506 local room = session.rooms[channel] |
468 for nick in pairs(room.occupants) do | 507 for nick in pairs(room.occupants) do |
469 --n=MattJ 91.85.191.50 irc.freenode.net MattJ H :0 Matthew Wild | |
470 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} | 508 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} |
471 end | 509 end |
472 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; | 510 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; |
473 end | 511 end |
474 end | 512 end |
475 | 513 |
476 function commands.MODE(session, args) -- FIXME | 514 function commands.MODE(session, args) -- Empty command |
477 -- emptied for the time being, until something sane which works is available. | |
478 end | 515 end |
479 | 516 |
480 function commands.QUIT(session, args) | 517 function commands.QUIT(session, args) |
481 session.send{"ERROR", "Closing Link: "..session.nick}; | 518 session.send{"ERROR", "Closing Link: "..session.nick}; |
482 for _, room in pairs(session.rooms) do | 519 for _, room in pairs(session.rooms) do |
486 nicks[session.nick] = nil; | 523 nicks[session.nick] = nil; |
487 sessions[session.conn] = nil; | 524 sessions[session.conn] = nil; |
488 session:close(); | 525 session:close(); |
489 end | 526 end |
490 | 527 |
491 function commands.RAW(session, data) | 528 function commands.RAW(session, data) -- Empty command |
492 --c:send(data) | |
493 end | 529 end |
494 | 530 |
495 local function desetup() | 531 local function desetup() |
496 require "net.connlisteners".deregister("irc"); | 532 require "net.connlisteners".deregister("irc"); |
497 end | 533 end |
498 | 534 |
499 --c:hook("ready", function () | 535 require "net.connlisteners".register("irc", irc_listener); |
500 require "net.connlisteners".register("irc", irc_listener); | 536 require "net.connlisteners".start("irc"); |
501 require "net.connlisteners".start("irc"); | |
502 --end); | |
503 | 537 |
504 module:hook("module-unloaded", desetup) | 538 module:hook("module-unloaded", desetup) |
505 | 539 |
506 | |
507 --print("Starting loop...") | |
508 --verse.loop() |