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 |