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
parent 487:8bdab5489653
child 489: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