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() |
