Mercurial > prosody-modules
comparison mod_ircd/mod_ircd.in.lua @ 488:4885ca74515c
mod_ircd: corrected an issue within the nick change logic, and improved it by implementing the USER command.
| author | Marco Cirillo <maranda@lightwitch.org> |
|---|---|
| date | Fri, 02 Dec 2011 04:24:16 +0000 |
| parents | 8bdab5489653 |
| children | 067bbff6e5bd |
comparison
equal
deleted
inserted
replaced
| 487:8bdab5489653 | 488:4885ca74515c |
|---|---|
| 147 local sessions = {}; | 147 local sessions = {}; |
| 148 local jids = {}; | 148 local jids = {}; |
| 149 local commands = {}; | 149 local commands = {}; |
| 150 | 150 |
| 151 local nicks = {}; | 151 local nicks = {}; |
| 152 local usernames = {}; | |
| 152 | 153 |
| 153 local st = require "util.stanza"; | 154 local st = require "util.stanza"; |
| 154 | 155 |
| 155 local conference_server = muc_server; | 156 local conference_server = muc_server; |
| 156 | 157 |
| 161 function irc_listener.onincoming(conn, data) | 162 function irc_listener.onincoming(conn, data) |
| 162 local session = sessions[conn]; | 163 local session = sessions[conn]; |
| 163 if not session then | 164 if not session then |
| 164 session = { conn = conn, host = component_jid, reset_stream = function () end, | 165 session = { conn = conn, host = component_jid, reset_stream = function () end, |
| 165 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")), |
| 166 rooms = {}, | 167 rooms = {}, roster = {}, has_un = false }; |
| 167 roster = {} }; | |
| 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.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 registered"} | 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 |
| 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 | |
| 225 usernames[session.username] = nil; | |
| 226 end | |
| 224 end | 227 end |
| 225 sessions[conn] = nil; | 228 sessions[conn] = nil; |
| 226 end | 229 end |
| 227 | 230 |
| 228 local function nick_inuse(nick) | 231 local function nick_inuse(nick) |
| 229 if nicks[nick] then return true else return false end | 232 if nicks[nick] then return true else return false end |
| 230 end | 233 end |
| 231 local function change_nick_st(jid, room_jid, newnick) | 234 local function check_username(un) |
| 232 return st.presence({ xmlns = xmlns_client, from = jid, to = room_jid, type = "unavailable" }):tag("status"):text("Changing nickname to "..newnick):up(); | 235 local count = 0; |
| 233 end | 236 local result; |
| 234 | 237 |
| 235 function commands.NICK(session, args) | 238 for name, given in pairs(usernames) do |
| 236 local nick = args[1]; | 239 if un == given then count = count + 1; end |
| 237 nick = nick:gsub("[^%w_]",""); | 240 end |
| 238 local full_jid = jid.join(nick, component_jid, "ircd"); | 241 |
| 239 | 242 result = count + 1; |
| 240 if session.nick and not nick_inuse(nick) then -- changing nick | 243 |
| 241 local oldnick = session.nick; | 244 if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end |
| 242 local old_full_jid = session.full_jid; | 245 end |
| 243 local old_ar_last = jids[old_full_jid]["ar_last"]; | 246 local function change_nick_st(fulljid, roomjid, tonick) |
| 244 local old_nicks_changing = jids[old_full_jid]["nicks_changing"]; | 247 return st.presence({ from = fulljid, to = newjid, type = "unavailable" }):tag("status"):text("Changing nickname to: "..tonick):up(); |
| 245 | 248 end |
| 246 -- update and replace session data | 249 local function set_t_data(session, full_jid) |
| 247 session.nick = nick; | 250 session.full_jid = full_jid; |
| 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 | |
| 267 return; | |
| 268 elseif nick_inuse(nick) then | |
| 269 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; | |
| 270 end | |
| 271 | |
| 272 jids[full_jid] = session; | 251 jids[full_jid] = session; |
| 273 jids[full_jid]["ar_last"] = {}; | 252 jids[full_jid]["ar_last"] = {}; |
| 274 jids[full_jid]["nicks_changing"] = {}; | 253 jids[full_jid]["nicks_changing"] = {}; |
| 275 nicks[nick] = session; | 254 |
| 276 session.nick = nick; | 255 if session.nick then nicks[session.nick] = session; end |
| 277 session.full_jid = full_jid; | 256 end |
| 278 session.type = "c2s"; | 257 local function send_motd(session) |
| 279 | 258 local nick = session.nick; |
| 280 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; | 259 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; |
| 281 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; | 260 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; |
| 282 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} | 261 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} |
| 283 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; | 262 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; |
| 284 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; | 263 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; |
| 293 | 272 |
| 294 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, | 273 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, |
| 295 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") | 274 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") |
| 296 end | 275 end |
| 297 | 276 |
| 298 function commands.USER(session, params) -- To be done. | 277 function commands.NICK(session, args) |
| 278 local nick = args[1]; | |
| 279 nick = nick:gsub("[^%w_]",""); | |
| 280 | |
| 281 if session.nick and not nick_inuse(nick) then -- changing nick | |
| 282 local oldnick = session.nick; | |
| 283 | |
| 284 -- update and replace session data | |
| 285 session.nick = nick; | |
| 286 nicks[oldnick] = nil; | |
| 287 nicks[nick] = session; | |
| 288 | |
| 289 session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; | |
| 290 | |
| 291 -- broadcast changes if required | |
| 292 if session.rooms then | |
| 293 for id, room in pairs(session.rooms) do | |
| 294 session.nicks_changing[session.nick] = { oldnick, session.username }; | |
| 295 | |
| 296 local node = jid.split(room.jid); | |
| 297 local oldjid = jid.join(node, muc_server, session.nick); | |
| 298 local room_name = room.jid | |
| 299 | |
| 300 room:send(change_nick_st(session.full_jid, jid.join(node, muc_server, oldnick), session.nick)); | |
| 301 local room, err = c:join_room(room_name, session.nick, { source = session.full_jid } ); | |
| 302 if not room then | |
| 303 session.send{from=nick.nick.."!"..session.username, "PART", id}; | |
| 304 return ":"..muc_server.." ERR :Failed to change nick and rejoin: "..err | |
| 305 end | |
| 306 end | |
| 307 end | |
| 308 | |
| 309 return; | |
| 310 elseif nick_inuse(nick) then | |
| 311 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; | |
| 312 end | |
| 313 | |
| 314 session.nick = nick; | |
| 315 session.type = "c2s"; | |
| 316 nicks[nick] = session; | |
| 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 | |
| 327 | |
| 328 function commands.USER(session, params) | |
| 329 local username = params[1]; | |
| 330 | |
| 331 if not session.has_un then | |
| 332 local un_checked = check_username(username); | |
| 333 | |
| 334 usernames[un_checked] = username; | |
| 335 session.username = un_checked; | |
| 336 session.has_un = true; | |
| 337 | |
| 338 if not session.full_jid then | |
| 339 set_t_data(session, jid.join(session.username, component_jid, "ircd")); | |
| 340 end | |
| 341 else | |
| 342 return session.send{from=muc_server, "462", "USER", "You may not re-register."} | |
| 343 end | |
| 344 | |
| 345 if session.username and session.nick then -- send MOTD | |
| 346 send_motd(session); | |
| 347 end | |
| 299 end | 348 end |
| 300 | 349 |
| 301 local function mode_map(am, rm, nicks) | 350 local function mode_map(am, rm, nicks) |
| 302 local rnick; | 351 local rnick; |
| 303 local c_modes; | 352 local c_modes; |
| 322 room.session = session; | 371 room.session = session; |
| 323 | 372 |
| 324 if session.nicks_changing[session.nick] then -- my own nick is changing | 373 if session.nicks_changing[session.nick] then -- my own nick is changing |
| 325 commands.NAMES(session, channel); | 374 commands.NAMES(session, channel); |
| 326 else | 375 else |
| 327 session.send{from=session.nick, "JOIN", channel}; | 376 session.send{from=session.nick.."!"..session.username, "JOIN", channel}; |
| 328 if room.subject then | 377 if room.subject then |
| 329 session.send{from=muc_server, 332, session.nick, channel, room.subject}; | 378 session.send{from=muc_server, 332, session.nick, channel, room.subject}; |
| 330 end | 379 end |
| 331 commands.NAMES(session, channel); | 380 commands.NAMES(session, channel); |
| 332 end | 381 end |
| 381 local session = room.session or jids[room.opts.source]; | 430 local session = room.session or jids[room.opts.source]; |
| 382 local channel = "#"..room.jid:match("^(.*)@"); | 431 local channel = "#"..room.jid:match("^(.*)@"); |
| 383 | 432 |
| 384 room:hook("occupant-joined", function(nick) | 433 room:hook("occupant-joined", function(nick) |
| 385 if session.nicks_changing[nick.nick] then | 434 if session.nicks_changing[nick.nick] then |
| 386 session.send{from=session.nicks_changing[nick.nick], "NICK", nick.nick}; | 435 session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; |
| 387 session.nicks_changing[nick.nick] = nil; | 436 session.nicks_changing[nick.nick] = nil; |
| 388 else | 437 else |
| 389 session.send{from=nick.nick, "JOIN", channel}; | 438 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; |
| 390 end | 439 end |
| 391 end); | 440 end); |
| 392 room:hook("occupant-left", function(nick) | 441 room:hook("occupant-left", function(nick) |
| 393 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end | 442 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end |
| 394 local status_code = | 443 local status_code = |
| 395 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 444 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 | 445 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; | 446 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; |
| 398 | 447 |
| 448 | |
| 399 if status_code == "303" then | 449 if status_code == "303" then |
| 400 local newnick = | 450 local newnick = |
| 401 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 451 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 | 452 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and |
| 403 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item") and | 453 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; |
| 404 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status"):get_child("item").attr.nick; | |
| 405 | 454 |
| 406 session.nicks_changing[newnick] = nick.nick; return; | 455 session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; |
| 407 end | 456 end |
| 408 session.send{from=nick.nick, "PART", channel}; | 457 |
| 458 local self_change; | |
| 459 for _, data in pairs(session.nicks_changing) do | |
| 460 if data[1] == nick.nick then self_change = nick.nick; break; end | |
| 461 end | |
| 462 if self_change then return; end | |
| 463 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "PART", channel}; | |
| 409 end); | 464 end); |
| 410 end); | 465 end); |
| 411 | 466 |
| 412 function commands.NAMES(session, channel) | 467 function commands.NAMES(session, channel) |
| 413 local nicks = { }; | 468 local nicks = { }; |
| 414 if type(channel) == "table" then channel = channel[1] end | 469 if type(channel) == "table" then channel = channel[1] end |
| 470 | |
| 415 local room = session.rooms[channel]; | 471 local room = session.rooms[channel]; |
| 416 | 472 |
| 417 local symbols_map = { | 473 local symbols_map = { |
| 418 owner = "~", | 474 owner = "~", |
| 419 administrator = "&", | 475 administrator = "&", |
| 519 for _, room in pairs(session.rooms) do | 575 for _, room in pairs(session.rooms) do |
| 520 room:leave(args[1]); | 576 room:leave(args[1]); |
| 521 end | 577 end |
| 522 jids[session.full_jid] = nil; | 578 jids[session.full_jid] = nil; |
| 523 nicks[session.nick] = nil; | 579 nicks[session.nick] = nil; |
| 580 usernames[session.username] = nil; | |
| 524 sessions[session.conn] = nil; | 581 sessions[session.conn] = nil; |
| 525 session:close(); | 582 session:close(); |
| 526 end | 583 end |
| 527 | 584 |
| 528 function commands.RAW(session, data) -- Empty command | 585 function commands.RAW(session, data) -- Empty command |
