# HG changeset patch # User Marco Cirillo # Date 1322799856 0 # Node ID 4885ca74515c248e00b2c05da7c78e271bd2fec1 # Parent 8bdab548965385bb9aaa333d1d8968d2296f2a33 mod_ircd: corrected an issue within the nick change logic, and improved it by implementing the USER command. diff -r 8bdab5489653 -r 4885ca74515c mod_ircd/mod_ircd.in.lua --- a/mod_ircd/mod_ircd.in.lua Fri Dec 02 01:03:06 2011 +0000 +++ b/mod_ircd/mod_ircd.in.lua Fri Dec 02 04:24:16 2011 +0000 @@ -149,6 +149,7 @@ local commands = {}; local nicks = {}; +local usernames = {}; local st = require "util.stanza"; @@ -163,8 +164,7 @@ if not session then session = { conn = conn, host = component_jid, reset_stream = function () end, close = irc_close_session, log = logger.init("irc"..(conn.id or "1")), - rooms = {}, - roster = {} }; + rooms = {}, roster = {}, has_un = false }; sessions[conn] = session; function session.data(data) @@ -175,10 +175,10 @@ return; end command = command:upper(); - if not session.nick then + if not session.username and not session.nick then if not (command == "USER" or command == "NICK") then module:log("debug", "Client tried to send command %s before registering", command); - return session.send{from=muc_server, "451", command, "You have not registered"} + return session.send{from=muc_server, "451", command, "You have not completed the registration."} end end if commands[command] then @@ -221,6 +221,9 @@ if session.full_jid then jids[session.full_jid] = nil; end + if session.username then + usernames[session.username] = nil; + end end sessions[conn] = nil; end @@ -228,55 +231,31 @@ 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 +local function check_username(un) + local count = 0; + local result; -function commands.NICK(session, args) - local nick = args[1]; - nick = nick:gsub("[^%w_]",""); - 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; + for name, given in pairs(usernames) do + if un == given then count = count + 1; end end + result = count + 1; + + if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end +end +local function change_nick_st(fulljid, roomjid, tonick) + return st.presence({ from = fulljid, to = newjid, type = "unavailable" }):tag("status"):text("Changing nickname to: "..tonick):up(); +end +local function set_t_data(session, full_jid) + session.full_jid = full_jid; 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; - session.type = "c2s"; - + + if session.nick then nicks[session.nick] = session; end +end +local function send_motd(session) + local nick = session.nick; session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} @@ -295,7 +274,77 @@ -- 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) -- To be done. +function commands.NICK(session, args) + local nick = args[1]; + nick = nick:gsub("[^%w_]",""); + + if session.nick and not nick_inuse(nick) then -- changing nick + local oldnick = session.nick; + + -- update and replace session data + session.nick = nick; + nicks[oldnick] = nil; + nicks[nick] = session; + + session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; + + -- broadcast changes if required + if session.rooms then + for id, room in pairs(session.rooms) do + session.nicks_changing[session.nick] = { oldnick, session.username }; + + local node = jid.split(room.jid); + local oldjid = jid.join(node, muc_server, session.nick); + local room_name = room.jid + + room:send(change_nick_st(session.full_jid, jid.join(node, muc_server, oldnick), session.nick)); + local room, err = c:join_room(room_name, session.nick, { source = session.full_jid } ); + if not room then + session.send{from=nick.nick.."!"..session.username, "PART", id}; + return ":"..muc_server.." ERR :Failed to change nick and rejoin: "..err + end + end + end + + return; + elseif nick_inuse(nick) then + session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; + end + + session.nick = nick; + session.type = "c2s"; + nicks[nick] = session; + + -- Some choppy clients send in NICK before USER, that needs to be handled + if session.username then + set_t_data(session, jid.join(session.username, component_jid, "ircd")); + end + + if session.username and session.nick then -- send MOTD + send_motd(session); + end +end + +function commands.USER(session, params) + local username = params[1]; + + if not session.has_un then + local un_checked = check_username(username); + + usernames[un_checked] = username; + session.username = un_checked; + session.has_un = true; + + if not session.full_jid then + set_t_data(session, jid.join(session.username, component_jid, "ircd")); + end + else + return session.send{from=muc_server, "462", "USER", "You may not re-register."} + end + + if session.username and session.nick then -- send MOTD + send_motd(session); + end end local function mode_map(am, rm, nicks) @@ -324,7 +373,7 @@ if session.nicks_changing[session.nick] then -- my own nick is changing commands.NAMES(session, channel); else - session.send{from=session.nick, "JOIN", channel}; + session.send{from=session.nick.."!"..session.username, "JOIN", channel}; if room.subject then session.send{from=muc_server, 332, session.nick, channel, room.subject}; end @@ -383,10 +432,10 @@ room:hook("occupant-joined", function(nick) if session.nicks_changing[nick.nick] then - session.send{from=session.nicks_changing[nick.nick], "NICK", nick.nick}; + session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; session.nicks_changing[nick.nick] = nil; else - session.send{from=nick.nick, "JOIN", channel}; + session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; end end); room:hook("occupant-left", function(nick) @@ -396,22 +445,29 @@ 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; + nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and + nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; - session.nicks_changing[newnick] = nick.nick; return; + session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; end - session.send{from=nick.nick, "PART", channel}; + + local self_change; + for _, data in pairs(session.nicks_changing) do + if data[1] == nick.nick then self_change = nick.nick; break; end + end + if self_change then return; end + session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "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 = { @@ -521,6 +577,7 @@ end jids[session.full_jid] = nil; nicks[session.nick] = nil; + usernames[session.username] = nil; sessions[session.conn] = nil; session:close(); end