--- a/mod_ircd/mod_ircd.in.lua Mon Nov 28 18:14:22 2011 +0100
+++ b/mod_ircd/mod_ircd.in.lua Fri Dec 02 01:03:06 2011 +0000
@@ -4,10 +4,12 @@
--
-- IRC spec:
-- http://tools.ietf.org/html/rfc2812
+
local _module = module
module = _G.module
local module = _module
---
+local client_xmlns = "jabber:client"
+
local component_jid, component_secret, muc_server, port_number =
module.host, nil, module:get_option_string("conference_server"), module:get_option_number("listener_port", 7000);
@@ -20,7 +22,7 @@
local verse = require "verse"
require "verse.component"
require "socket"
-c = verse.new();--verse.logger())
+c = verse.new();
c:add_plugin("groupchat");
local function verse2prosody(e)
@@ -33,29 +35,6 @@
c.type = "component";
c.send = core_post_stanza;
--- This plugin is actually a verse based component, but that mode is currently commented out
-
--- Add some hooks for debugging
---c:hook("opened", function () print("Stream opened!") end);
---c:hook("closed", function () print("Stream closed!") end);
---c:hook("stanza", function (stanza) print("Stanza:", stanza) end);
-
--- This one prints all received data
---c:hook("incoming-raw", print, 1000);
---c:hook("stanza", print, 1000);
---c:hook("outgoing-raw", print, 1000);
-
--- Print a message after authentication
---c:hook("authentication-success", function () print("Logged in!"); end);
---c:hook("authentication-failure", function (err) print("Failed to log in! Error: "..tostring(err.condition)); end);
-
--- Print a message and exit when disconnected
---c:hook("disconnected", function () print("Disconnected!"); os.exit(); end);
-
--- Now, actually start the connection:
---c.connect_host = "127.0.0.1"
---c:connect_component(component_jid, component_secret);
-
local jid = require "util.jid";
local nodeprep = require "util.encodings".stringprep.nodeprep;
@@ -128,7 +107,11 @@
local function irc2muc(channel, nick)
local room = channel and nodeprep(channel:match("^#(%w+)")) or nil;
- return jid.join(room, muc_server, nick)
+ if not nick then
+ return jid.join(room, muc_server);
+ else
+ return jid.join(room, muc_server, nick);
+ end
end
local function muc2irc(room)
local channel, _, nick = jid.split(room);
@@ -183,6 +166,7 @@
rooms = {},
roster = {} };
sessions[conn] = session;
+
function session.data(data)
local parts = parse_line(data);
module:log("debug", require"util.serialization".serialize(parts));
@@ -207,6 +191,7 @@
return module:log("debug", "Unknown command: %s", command);
end
end
+
function session.send(data)
if type(data) == "string" then
return conn:write(data.."\r\n");
@@ -217,6 +202,7 @@
end
end
end
+
if data then
session.data(data);
end
@@ -224,6 +210,7 @@
function irc_listener.ondisconnect(conn, error)
local session = sessions[conn];
+
if session then
for _, room in pairs(session.rooms) do
room:leave("Disconnected");
@@ -238,21 +225,53 @@
sessions[conn] = nil;
end
+local function nick_inuse(nick)
+ if nicks[nick] then return true else return false end
+end
+local function change_nick_st(jid, room_jid, newnick)
+ return st.presence({ xmlns = xmlns_client, from = jid, to = room_jid, type = "unavailable" }):tag("status"):text("Changing nickname to "..newnick):up();
+end
+
function commands.NICK(session, args)
- if session.nick then
- session.send{from = muc_server, "484", "*", nick, "I'm afraid I can't let you do that"};
- --TODO Loop throug all rooms and change nick, with help from Verse.
- return;
- end
local nick = args[1];
nick = nick:gsub("[^%w_]","");
- if nicks[nick] then
- session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"};
+ local full_jid = jid.join(nick, component_jid, "ircd");
+
+ if session.nick and not nick_inuse(nick) then -- changing nick
+ local oldnick = session.nick;
+ local old_full_jid = session.full_jid;
+ local old_ar_last = jids[old_full_jid]["ar_last"];
+ local old_nicks_changing = jids[old_full_jid]["nicks_changing"];
+
+ -- update and replace session data
+ session.nick = nick;
+ session.full_jid = full_jid;
+ jids[old_full_jid] = nil; nicks[oldnick] = nil;
+ nicks[nick] = session;
+ jids[full_jid] = session;
+ jids[full_jid]["ar_last"] = old_ar_last;
+ jids[full_jid]["nicks_changing"] = old_nicks_changing;
+
+ session.send{from=oldnick, "NICK", nick};
+
+ -- broadcast changes if required, todo: something better then forcing parting and rejoining
+ if session.rooms then
+ for id, room in pairs(session.rooms) do
+ session.nicks_changing[session.nick] = oldnick;
+ room:send(change_nick_st(old_full_jid, room.jid.."/"..oldnick, session.nick));
+ commands.JOIN(session, { id });
+ end
+ session.nicks_changing[session.nick] = nil;
+ end
+
return;
+ elseif nick_inuse(nick) then
+ session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return;
end
- local full_jid = jid.join(nick, component_jid, "ircd");
+
jids[full_jid] = session;
jids[full_jid]["ar_last"] = {};
+ jids[full_jid]["nicks_changing"] = {};
nicks[nick] = session;
session.nick = nick;
session.full_jid = full_jid;
@@ -276,9 +295,7 @@
-- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set")
end
-function commands.USER(session, params)
- -- FIXME
- -- Empty command for now
+function commands.USER(session, params) -- To be done.
end
local function mode_map(am, rm, nicks)
@@ -294,20 +311,25 @@
local channel = args[1];
if not channel then return end
local room_jid = irc2muc(channel);
- print(session.full_jid);
+
if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end
local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } );
if not room then
return ":"..muc_server.." ERR :Could not join room: "..err
end
+
session.rooms[channel] = room;
- room.channel = channel;
room.session = session;
- session.send{from=session.nick, "JOIN", channel};
- if room.subject then
- session.send{from=muc_server, 332, session.nick, channel ,room.subject};
+
+ if session.nicks_changing[session.nick] then -- my own nick is changing
+ commands.NAMES(session, channel);
+ else
+ session.send{from=session.nick, "JOIN", channel};
+ if room.subject then
+ session.send{from=muc_server, 332, session.nick, channel, room.subject};
+ end
+ commands.NAMES(session, channel);
end
- commands.NAMES(session, channel);
room:hook("subject-changed", function(changed)
session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or ""));
@@ -332,7 +354,7 @@
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
local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user")
if x_ar then
- local xar_item = x_ar:get_child("item")
+ local xar_item = x_ar:get_child("item")
if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then
if xar_item.attr.affiliation and xar_item.attr.role then
if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and
@@ -358,23 +380,40 @@
c:hook("groupchat/joined", function(room)
local session = room.session or jids[room.opts.source];
local channel = "#"..room.jid:match("^(.*)@");
- session.send{from=session.nick.."!"..session.nick, "JOIN", channel};
- if room.topic then
- session.send{from=muc_server, 332, room.topic};
- end
- commands.NAMES(session, channel)
+
room:hook("occupant-joined", function(nick)
- session.send{from=nick.nick.."!"..nick.nick, "JOIN", channel};
+ if session.nicks_changing[nick.nick] then
+ session.send{from=session.nicks_changing[nick.nick], "NICK", nick.nick};
+ session.nicks_changing[nick.nick] = nil;
+ else
+ session.send{from=nick.nick, "JOIN", channel};
+ end
end);
room:hook("occupant-left", function(nick)
- jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; -- ugly
- session.send{from=nick.nick.."!"..nick.nick, "PART", channel};
+ if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end
+ local status_code =
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code;
+
+ if status_code == "303" then
+ local newnick =
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item") and
+ nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item").attr.nick;
+
+ session.nicks_changing[newnick] = nick.nick; return;
+ end
+ session.send{from=nick.nick, "PART", channel};
end);
end);
function commands.NAMES(session, channel)
local nicks = { };
+ if type(channel) == "table" then channel = channel[1] end
local room = session.rooms[channel];
+
local symbols_map = {
owner = "~",
administrator = "&",
@@ -466,15 +505,13 @@
if session.rooms[channel] then
local room = session.rooms[channel]
for nick in pairs(room.occupants) do
- --n=MattJ 91.85.191.50 irc.freenode.net MattJ H :0 Matthew Wild
session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick}
end
session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"};
end
end
-function commands.MODE(session, args) -- FIXME
- -- emptied for the time being, until something sane which works is available.
+function commands.MODE(session, args) -- Empty command
end
function commands.QUIT(session, args)
@@ -488,21 +525,15 @@
session:close();
end
-function commands.RAW(session, data)
- --c:send(data)
+function commands.RAW(session, data) -- Empty command
end
local function desetup()
require "net.connlisteners".deregister("irc");
end
---c:hook("ready", function ()
- require "net.connlisteners".register("irc", irc_listener);
- require "net.connlisteners".start("irc");
---end);
+require "net.connlisteners".register("irc", irc_listener);
+require "net.connlisteners".start("irc");
module:hook("module-unloaded", desetup)
-
---print("Starting loop...")
---verse.loop()