Software /
code /
prosody-modules
Comparison
mod_ircd/mod_ircd.in.lua @ 491:5b3db688213d
mod_ircd: Fixed nick change logic (thanks mva), so that the self nick-change "flag" is removed properly, improved the logic to use verse's room_mt:change_nick (thanks Zash) yet to be pushed into main, added squished verse with the meta method included.
author | Marco Cirillo <maranda@lightwitch.org> |
---|---|
date | Fri, 02 Dec 2011 20:53:09 +0000 |
parent | 490:00b77a9f2d5f |
child | 495:1a71e0e21a29 |
comparison
equal
deleted
inserted
replaced
490:00b77a9f2d5f | 491:5b3db688213d |
---|---|
12 | 12 |
13 local component_jid, component_secret, muc_server, port_number = | 13 local component_jid, component_secret, muc_server, port_number = |
14 module.host, nil, module:get_option_string("conference_server"), module:get_option_number("listener_port", 7000); | 14 module.host, nil, module:get_option_string("conference_server"), module:get_option_number("listener_port", 7000); |
15 | 15 |
16 if not muc_server then | 16 if not muc_server then |
17 module:log ("error", "You need to set the MUC server! halting.") | 17 module:log ("error", "You need to set the MUC server! halting.") |
18 return false; | 18 return false; |
19 end | 19 end |
20 | 20 |
21 package.loaded["util.sha1"] = require "util.encodings"; | 21 package.loaded["util.sha1"] = require "util.encodings"; |
22 local verse = require "verse" | 22 local verse = require "verse" |
23 require "verse.component" | 23 require "verse.component" |
24 require "socket" | 24 require "socket" |
25 c = verse.new(); | 25 c = verse.new(); |
26 c:add_plugin("groupchat"); | 26 c:add_plugin("groupchat"); |
27 | 27 |
28 local function verse2prosody(e) | 28 local function verse2prosody(e) |
29 return c:event("stanza", e.stanza) or true; | 29 return c:event("stanza", e.stanza) or true; |
30 end | 30 end |
31 module:hook("message/bare", verse2prosody); | 31 module:hook("message/bare", verse2prosody); |
32 module:hook("message/full", verse2prosody); | 32 module:hook("message/full", verse2prosody); |
33 module:hook("presence/bare", verse2prosody); | 33 module:hook("presence/bare", verse2prosody); |
34 module:hook("presence/full", verse2prosody); | 34 module:hook("presence/full", verse2prosody); |
37 | 37 |
38 local jid = require "util.jid"; | 38 local jid = require "util.jid"; |
39 local nodeprep = require "util.encodings".stringprep.nodeprep; | 39 local nodeprep = require "util.encodings".stringprep.nodeprep; |
40 | 40 |
41 local function utf8_clean (s) | 41 local function utf8_clean (s) |
42 local push, join = table.insert, table.concat; | 42 local push, join = table.insert, table.concat; |
43 local r, i = {}, 1; | 43 local r, i = {}, 1; |
44 if not(s and #s > 0) then | 44 if not(s and #s > 0) then |
45 return "" | 45 return "" |
46 end | 46 end |
47 while true do | 47 while true do |
48 local c = s:sub(i,i) | 48 local c = s:sub(i,i) |
49 local b = c:byte(); | 49 local b = c:byte(); |
50 local w = ( | 50 local w = ( |
51 (b >= 9 and b <= 10 and 0) or | 51 (b >= 9 and b <= 10 and 0) or |
52 (b >= 32 and b <= 126 and 0) or | 52 (b >= 32 and b <= 126 and 0) or |
53 (b >= 192 and b <= 223 and 1) or | 53 (b >= 192 and b <= 223 and 1) or |
54 (b >= 224 and b <= 239 and 2) or | 54 (b >= 224 and b <= 239 and 2) or |
55 (b >= 240 and b <= 247 and 3) or | 55 (b >= 240 and b <= 247 and 3) or |
56 (b >= 248 and b <= 251 and 4) or | 56 (b >= 248 and b <= 251 and 4) or |
57 (b >= 251 and b <= 252 and 5) or nil | 57 (b >= 251 and b <= 252 and 5) or nil |
58 ) | 58 ) |
59 if not w then | 59 if not w then |
60 push(r, "?") | 60 push(r, "?") |
61 else | 61 else |
62 local n = i + w; | 62 local n = i + w; |
63 if w == 0 then | 63 if w == 0 then |
64 push(r, c); | 64 push(r, c); |
65 elseif n > #s then | 65 elseif n > #s then |
66 push(r, ("?"):format(b)); | 66 push(r, ("?"):format(b)); |
67 else | 67 else |
68 local e = s:sub(i+1,n); | 68 local e = s:sub(i+1,n); |
69 if e:match('^[\128-\191]*$') then | 69 if e:match('^[\128-\191]*$') then |
70 push(r, c); | 70 push(r, c); |
71 push(r, e); | 71 push(r, e); |
72 i = n; | 72 i = n; |
73 else | 73 else |
74 push(r, ("?"):format(b)); | 74 push(r, ("?"):format(b)); |
75 end | 75 end |
76 end | 76 end |
77 end | 77 end |
78 i = i + 1; | 78 i = i + 1; |
79 if i > #s then | 79 if i > #s then |
80 break | 80 break |
81 end | 81 end |
82 end | 82 end |
83 return join(r); | 83 return join(r); |
84 end | 84 end |
85 | 85 |
86 local function parse_line(line) | 86 local function parse_line(line) |
87 local ret = {}; | 87 local ret = {}; |
88 if line:sub(1,1) == ":" then | 88 if line:sub(1,1) == ":" then |
89 ret.from, line = line:match("^:(%w+)%s+(.*)$"); | 89 ret.from, line = line:match("^:(%w+)%s+(.*)$"); |
90 end | 90 end |
91 for part in line:gmatch("%S+") do | 91 for part in line:gmatch("%S+") do |
92 if part:sub(1,1) == ":" then | 92 if part:sub(1,1) == ":" then |
93 ret[#ret+1] = line:match(":(.*)$"); | 93 ret[#ret+1] = line:match(":(.*)$"); |
94 break | 94 break |
95 end | 95 end |
96 ret[#ret+1]=part; | 96 ret[#ret+1]=part; |
97 end | 97 end |
98 return ret; | 98 return ret; |
99 end | 99 end |
100 | 100 |
101 local function build_line(parts) | 101 local function build_line(parts) |
102 if #parts > 1 then | 102 if #parts > 1 then |
103 parts[#parts] = ":" .. parts[#parts]; | 103 parts[#parts] = ":" .. parts[#parts]; |
104 end | 104 end |
105 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); | 105 return (parts.from and ":"..parts.from.." " or "")..table.concat(parts, " "); |
106 end | 106 end |
107 | 107 |
108 local function irc2muc(channel, nick) | 108 local function irc2muc(channel, nick) |
109 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; | 109 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; |
110 if not nick then | 110 if not nick then |
111 return jid.join(room, muc_server); | 111 return jid.join(room, muc_server); |
112 else | 112 else |
113 return jid.join(room, muc_server, nick); | 113 return jid.join(room, muc_server, nick); |
114 end | 114 end |
115 end | 115 end |
116 local function muc2irc(room) | 116 local function muc2irc(room) |
117 local channel, _, nick = jid.split(room); | 117 local channel, _, nick = jid.split(room); |
118 return "#"..channel, nick; | 118 return "#"..channel, nick; |
119 end | 119 end |
120 local role_map = { | 120 local role_map = { |
121 moderator = "@", | 121 moderator = "@", |
122 participant = "", | 122 participant = "", |
123 visitor = "", | 123 visitor = "", |
124 none = "" | 124 none = "" |
125 } | 125 } |
126 local aff_map = { | 126 local aff_map = { |
127 owner = "~", | 127 owner = "~", |
128 administrator = "&", | 128 administrator = "&", |
129 member = "+", | 129 member = "+", |
130 none = "" | 130 none = "" |
131 } | 131 } |
132 local role_modemap = { | 132 local role_modemap = { |
133 moderator = "o", | 133 moderator = "o", |
134 participant = "", | 134 participant = "", |
135 visitor = "", | 135 visitor = "", |
136 none = "" | 136 none = "" |
137 } | 137 } |
138 local aff_modemap = { | 138 local aff_modemap = { |
139 owner = "q", | 139 owner = "q", |
140 administrator = "a", | 140 administrator = "a", |
141 member = "v", | 141 member = "v", |
142 none = "" | 142 none = "" |
143 } | 143 } |
144 | 144 |
145 local irc_listener = { default_port = port_number, default_mode = "*l" }; | 145 local irc_listener = { default_port = port_number, default_mode = "*l" }; |
146 | 146 |
147 local sessions = {}; | 147 local sessions = {}; |
154 local st = require "util.stanza"; | 154 local st = require "util.stanza"; |
155 | 155 |
156 local conference_server = muc_server; | 156 local conference_server = muc_server; |
157 | 157 |
158 local function irc_close_session(session) | 158 local function irc_close_session(session) |
159 session.conn:close(); | 159 session.conn:close(); |
160 end | 160 end |
161 | 161 |
162 function irc_listener.onincoming(conn, data) | 162 function irc_listener.onincoming(conn, data) |
163 local session = sessions[conn]; | 163 local session = sessions[conn]; |
164 if not session then | 164 if not session then |
165 session = { conn = conn, host = component_jid, reset_stream = function () end, | 165 session = { conn = conn, host = component_jid, reset_stream = function () end, |
166 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")), |
167 rooms = {}, roster = {}, has_un = false }; | 167 rooms = {}, roster = {}, has_un = false }; |
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.username and 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 completed the registration."} | 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 |
187 return session.send(ret); | 187 return session.send(ret); |
188 end | 188 end |
189 else | 189 else |
190 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; | 190 session.send{from=muc_server, "421", session.nick, command, "Unknown command"}; |
191 return module:log("debug", "Unknown command: %s", command); | 191 return module:log("debug", "Unknown command: %s", command); |
192 end | 192 end |
193 end | 193 end |
194 | 194 |
195 function session.send(data) | 195 function session.send(data) |
196 if type(data) == "string" then | 196 if type(data) == "string" then |
197 return conn:write(data.."\r\n"); | 197 return conn:write(data.."\r\n"); |
198 elseif type(data) == "table" then | 198 elseif type(data) == "table" then |
199 local line = build_line(data); | 199 local line = build_line(data); |
200 module:log("debug", line); | 200 module:log("debug", line); |
201 conn:write(line.."\r\n"); | 201 conn:write(line.."\r\n"); |
202 end | 202 end |
203 end | 203 end |
204 end | 204 end |
205 | 205 |
206 if data then | 206 if data then |
207 session.data(data); | 207 session.data(data); |
208 end | 208 end |
209 end | 209 end |
210 | 210 |
211 function irc_listener.ondisconnect(conn, error) | 211 function irc_listener.ondisconnect(conn, error) |
212 local session = sessions[conn]; | 212 local session = sessions[conn]; |
213 | 213 |
214 if session then | 214 if session then |
215 for _, room in pairs(session.rooms) do | 215 for _, room in pairs(session.rooms) do |
216 room:leave("Disconnected"); | 216 room:leave("Disconnected"); |
217 end | 217 end |
218 if session.nick then | 218 if session.nick 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 | 224 if session.username then |
225 usernames[session.username] = nil; | 225 usernames[session.username] = nil; |
226 end | 226 end |
227 end | 227 end |
228 sessions[conn] = nil; | 228 sessions[conn] = nil; |
229 end | 229 end |
230 | 230 |
231 local function nick_inuse(nick) | 231 local function nick_inuse(nick) |
232 if nicks[nick] then return true else return false end | 232 if nicks[nick] then return true else return false end |
233 end | 233 end |
234 local function check_username(un) | 234 local function check_username(un) |
235 local count = 0; | 235 local count = 0; |
236 local result; | 236 local result; |
237 | 237 |
238 for name, given in pairs(usernames) do | 238 for name, given in pairs(usernames) do |
239 if un == given then count = count + 1; end | 239 if un == given then count = count + 1; end |
240 end | 240 end |
241 | 241 |
242 result = count + 1; | 242 result = count + 1; |
243 | 243 |
244 if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end | 244 if count > 0 then return tostring(un)..tostring(result); else return tostring(un); end |
245 end | |
246 local function change_nick_st(fulljid, roomjid, tonick) | |
247 return st.presence({ from = fulljid, to = roomjid, type = "unavailable" }):tag("status"):text("Changing nickname to: "..tonick):up(); | |
248 end | 245 end |
249 local function set_t_data(session, full_jid) | 246 local function set_t_data(session, full_jid) |
250 session.full_jid = full_jid; | 247 session.full_jid = full_jid; |
251 jids[full_jid] = session; | 248 jids[full_jid] = session; |
252 jids[full_jid]["ar_last"] = {}; | 249 jids[full_jid]["ar_last"] = {}; |
253 jids[full_jid]["nicks_changing"] = {}; | 250 jids[full_jid]["nicks_changing"] = {}; |
254 | 251 |
255 if session.nick then nicks[session.nick] = session; end | 252 if session.nick then nicks[session.nick] = session; end |
256 end | 253 end |
257 local function send_motd(session) | 254 local function send_motd(session) |
258 local nick = session.nick; | 255 local nick = session.nick; |
259 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; | 256 session.send{from = muc_server, "001", nick, "Welcome in the IRC to MUC XMPP Gateway, "..nick}; |
260 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; | 257 session.send{from = muc_server, "002", nick, "Your host is "..muc_server.." running Prosody "..prosody.version}; |
261 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} | 258 session.send{from = muc_server, "003", nick, "This server was created the "..os.date(nil, prosody.start_time)} |
262 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; | 259 session.send{from = muc_server, "004", nick, table.concat({muc_server, "mod_ircd(alpha-0.8)", "i", "aoqv"}, " ")}; |
263 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; | 260 session.send{from = muc_server, "375", nick, "- "..muc_server.." Message of the day -"}; |
264 session.send{from = muc_server, "372", nick, "-"}; | 261 session.send{from = muc_server, "372", nick, "-"}; |
265 session.send{from = muc_server, "372", nick, "- Please be warned that this is only a partial irc implementation,"}; | 262 session.send{from = muc_server, "372", nick, "- Please be warned that this is only a partial irc implementation,"}; |
266 session.send{from = muc_server, "372", nick, "- it's made to facilitate users transiting away from irc to XMPP."}; | 263 session.send{from = muc_server, "372", nick, "- it's made to facilitate users transiting away from irc to XMPP."}; |
267 session.send{from = muc_server, "372", nick, "-"}; | 264 session.send{from = muc_server, "372", nick, "-"}; |
268 session.send{from = muc_server, "372", nick, "- Prosody is _NOT_ an IRC Server and it never will."}; | 265 session.send{from = muc_server, "372", nick, "- Prosody is _NOT_ an IRC Server and it never will."}; |
269 session.send{from = muc_server, "372", nick, "- We also would like to remind you that this plugin is provided as is,"}; | 266 session.send{from = muc_server, "372", nick, "- We also would like to remind you that this plugin is provided as is,"}; |
270 session.send{from = muc_server, "372", nick, "- it's still an Alpha and it's still a work in progress, use it at your sole"}; | 267 session.send{from = muc_server, "372", nick, "- it's still an Alpha and it's still a work in progress, use it at your sole"}; |
271 session.send{from = muc_server, "372", nick, "- risk as there's a not so little chance something will break."}; | 268 session.send{from = muc_server, "372", nick, "- risk as there's a not so little chance something will break."}; |
272 | 269 |
273 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, | 270 session.send{from = nick, "MODE", nick, "+i"}; -- why -> Invisible mode setting, |
274 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") | 271 -- enforce by default on most servers (since the source host doesn't show it's sensible to have it "set") |
275 end | 272 end |
276 | 273 |
277 function commands.NICK(session, args) | 274 function commands.NICK(session, args) |
278 local nick = args[1]; | 275 local nick = args[1]; |
279 nick = nick:gsub("[^%w_]",""); | 276 nick = nick:gsub("[^%w_]",""); |
280 | 277 |
281 if session.nick and not nick_inuse(nick) then -- changing nick | 278 if session.nick and not nick_inuse(nick) then -- changing nick |
282 local oldnick = session.nick; | 279 local oldnick = session.nick; |
283 | 280 |
284 -- update and replace session data | 281 -- update and replace session data |
285 session.nick = nick; | 282 session.nick = nick; |
286 nicks[oldnick] = nil; | 283 nicks[oldnick] = nil; |
287 nicks[nick] = session; | 284 nicks[nick] = session; |
288 | 285 |
289 session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; | 286 session.send{from=oldnick.."!"..nicks[nick].username, "NICK", nick}; |
290 | 287 |
291 -- broadcast changes if required | 288 -- broadcast changes if required |
292 if session.rooms then | 289 if session.rooms then |
293 for id, room in pairs(session.rooms) do | 290 session.nicks_changing[nick] = { oldnick, session.username }; |
294 session.nicks_changing[session.nick] = { oldnick, session.username }; | 291 |
295 | 292 for id, room in pairs(session.rooms) do room:change_nick(nick); end |
296 local node = jid.split(room.jid); | 293 |
297 local oldjid = jid.join(node, muc_server, session.nick); | 294 session.nicks_changing[nick] = nil; |
298 local room_name = room.jid | 295 end |
299 | 296 |
300 room:send(change_nick_st(session.full_jid, jid.join(node, muc_server, oldnick), session.nick)); | 297 return; |
301 local room, err = c:join_room(room_name, session.nick, { source = session.full_jid } ); | 298 elseif nick_inuse(nick) then |
302 if not room then | 299 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; |
303 session.send{from=nick.nick.."!"..session.username, "PART", id}; | 300 end |
304 return ":"..muc_server.." ERR :Failed to change nick and rejoin: "..err | 301 |
305 end | 302 session.nick = nick; |
306 end | 303 session.type = "c2s"; |
307 end | 304 nicks[nick] = session; |
308 | 305 |
309 return; | 306 -- Some choppy clients send in NICK before USER, that needs to be handled |
310 elseif nick_inuse(nick) then | 307 if session.username then |
311 session.send{from=muc_server, "433", nick, "The nickname "..nick.." is already in use"}; return; | 308 set_t_data(session, jid.join(session.username, component_jid, "ircd")); |
312 end | 309 end |
313 | 310 |
314 session.nick = nick; | 311 if session.username and session.nick then -- send MOTD |
315 session.type = "c2s"; | 312 send_motd(session); |
316 nicks[nick] = session; | 313 end |
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 | 314 end |
327 | 315 |
328 function commands.USER(session, params) | 316 function commands.USER(session, params) |
329 local username = params[1]; | 317 local username = params[1]; |
330 | 318 |
331 if not session.has_un then | 319 if not session.has_un then |
332 local un_checked = check_username(username); | 320 local un_checked = check_username(username); |
333 | 321 |
334 usernames[un_checked] = username; | 322 usernames[un_checked] = username; |
335 session.username = un_checked; | 323 session.username = un_checked; |
336 session.has_un = true; | 324 session.has_un = true; |
337 | 325 |
338 if not session.full_jid then | 326 if not session.full_jid then |
339 set_t_data(session, jid.join(session.username, component_jid, "ircd")); | 327 set_t_data(session, jid.join(session.username, component_jid, "ircd")); |
340 end | 328 end |
341 else | 329 else |
342 return session.send{from=muc_server, "462", "USER", "You may not re-register."} | 330 return session.send{from=muc_server, "462", "USER", "You may not re-register."} |
343 end | 331 end |
344 | 332 |
345 if session.username and session.nick then -- send MOTD | 333 if session.username and session.nick then -- send MOTD |
346 send_motd(session); | 334 send_motd(session); |
347 end | 335 end |
348 end | 336 end |
349 | 337 |
350 local function mode_map(am, rm, nicks) | 338 local function mode_map(am, rm, nicks) |
351 local rnick; | 339 local rnick; |
352 local c_modes; | 340 local c_modes; |
353 c_modes = aff_modemap[am]..role_modemap[rm] | 341 c_modes = aff_modemap[am]..role_modemap[rm] |
354 rnick = string.rep(nicks.." ", c_modes:len()) | 342 rnick = string.rep(nicks.." ", c_modes:len()) |
355 if c_modes == "" then return nil, nil end | 343 if c_modes == "" then return nil, nil end |
356 return c_modes, rnick | 344 return c_modes, rnick |
357 end | 345 end |
358 | 346 |
359 function commands.JOIN(session, args) | 347 function commands.JOIN(session, args) |
360 local channel = args[1]; | 348 local channel = args[1]; |
361 if not channel then return end | 349 if not channel then return end |
362 local room_jid = irc2muc(channel); | 350 local room_jid = irc2muc(channel); |
363 | 351 |
364 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end | 352 if not jids[session.full_jid].ar_last[room_jid] then jids[session.full_jid].ar_last[room_jid] = {}; end |
365 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); | 353 local room, err = c:join_room(room_jid, session.nick, { source = session.full_jid } ); |
366 if not room then | 354 if not room then |
367 return ":"..muc_server.." ERR :Could not join room: "..err | 355 return ":"..muc_server.." ERR :Could not join room: "..err |
368 end | 356 end |
369 | 357 |
370 session.rooms[channel] = room; | 358 session.rooms[channel] = room; |
371 room.session = session; | 359 room.session = session; |
372 | 360 |
373 if session.nicks_changing[session.nick] then -- my own nick is changing | 361 if session.nicks_changing[session.nick] then -- my own nick is changing |
374 commands.NAMES(session, channel); | 362 commands.NAMES(session, channel); |
375 else | 363 else |
376 session.send{from=session.nick.."!"..session.username, "JOIN", channel}; | 364 session.send{from=session.nick.."!"..session.username, "JOIN", channel}; |
377 if room.subject then | 365 if room.subject then |
378 session.send{from=muc_server, 332, session.nick, channel, room.subject}; | 366 session.send{from=muc_server, 332, session.nick, channel, room.subject}; |
379 end | 367 end |
380 commands.NAMES(session, channel); | 368 commands.NAMES(session, channel); |
381 end | 369 end |
382 | 370 |
383 room:hook("subject-changed", function(changed) | 371 room:hook("subject-changed", function(changed) |
384 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); | 372 session.send((":%s TOPIC %s :%s"):format(changed.by.nick, channel, changed.to or "")); |
385 end); | 373 end); |
386 | 374 |
387 room:hook("message", function(event) | 375 room:hook("message", function(event) |
388 if not event.body then return end | 376 if not event.body then return end |
389 local nick, body = event.nick, event.body; | 377 local nick, body = event.nick, event.body; |
390 if nick ~= session.nick then | 378 if nick ~= session.nick then |
391 if body:sub(1,4) == "/me " then | 379 if body:sub(1,4) == "/me " then |
392 body = "\1ACTION ".. body:sub(5) .. "\1" | 380 body = "\1ACTION ".. body:sub(5) .. "\1" |
393 end | 381 end |
394 local type = event.stanza.attr.type; | 382 local type = event.stanza.attr.type; |
395 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body}; | 383 session.send{from=nick, "PRIVMSG", type == "groupchat" and channel or nick, body}; |
396 --FIXME PM's probably won't work | 384 --FIXME PM's probably won't work |
397 end | 385 end |
398 end); | 386 end); |
399 | 387 |
400 room:hook("presence", function(ar) | 388 room:hook("presence", function(ar) |
401 local c_modes; | 389 local c_modes; |
402 local rnick; | 390 local rnick; |
403 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 | 391 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 |
404 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") | 392 local x_ar = ar.stanza:get_child("x", "http://jabber.org/protocol/muc#user") |
405 if x_ar then | 393 if x_ar then |
406 local xar_item = x_ar:get_child("item") | 394 local xar_item = x_ar:get_child("item") |
407 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then | 395 if xar_item and xar_item.attr and ar.stanza.attr.type ~= "unavailable" then |
408 if xar_item.attr.affiliation and xar_item.attr.role then | 396 if xar_item.attr.affiliation and xar_item.attr.role then |
409 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and | 397 if not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] and |
410 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then | 398 not jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] then |
411 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation | 399 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation |
412 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role | 400 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role |
413 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); | 401 n_self_changing = nicks[ar.nick] and nicks[ar.nick].nicks_changing and nicks[ar.nick].nicks_changing[ar.nick] |
414 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 402 if n_self_changing then return; end |
415 else | 403 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); |
416 c_modes, rnick = mode_map(jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"], jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"], ar.nick); | 404 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
417 if c_modes and rnick then session.send((":%s MODE %s -%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 405 else |
418 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation | 406 c_modes, rnick = mode_map(jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"], jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"], ar.nick); |
419 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role | 407 if c_modes and rnick then session.send((":%s MODE %s -%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
420 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); | 408 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["affiliation"] = xar_item.attr.affiliation |
421 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end | 409 jids[session.full_jid].ar_last[ar.room_jid][ar.nick]["role"] = xar_item.attr.role |
422 end | 410 n_self_changing = nicks[ar.nick] and nicks[ar.nick].nicks_changing and nicks[ar.nick].nicks_changing[ar.nick] |
423 end | 411 if n_self_changing then return; end |
424 end | 412 c_modes, rnick = mode_map(xar_item.attr.affiliation, xar_item.attr.role, ar.nick); |
425 end | 413 if c_modes and rnick then session.send((":%s MODE %s +%s"):format(muc_server, channel, c_modes.." "..rnick)); end |
426 end, -1); | 414 end |
415 end | |
416 end | |
417 end | |
418 end, -1); | |
427 end | 419 end |
428 | 420 |
429 c:hook("groupchat/joined", function(room) | 421 c:hook("groupchat/joined", function(room) |
430 local session = room.session or jids[room.opts.source]; | 422 local session = room.session or jids[room.opts.source]; |
431 local channel = "#"..room.jid:match("^(.*)@"); | 423 local channel = "#"..room.jid:match("^(.*)@"); |
432 | 424 |
433 room:hook("occupant-joined", function(nick) | 425 room:hook("occupant-joined", function(nick) |
434 if session.nicks_changing[nick.nick] then | 426 if session.nicks_changing[nick.nick] then |
435 session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; | 427 session.send{from=session.nicks_changing[nick.nick][1].."!"..(session.nicks_changing[nick.nick][2] or "xmpp"), "NICK", nick.nick}; |
436 session.nicks_changing[nick.nick] = nil; | 428 session.nicks_changing[nick.nick] = nil; |
437 else | 429 else |
438 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; | 430 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "JOIN", channel}; |
439 end | 431 end |
440 end); | 432 end); |
441 room:hook("occupant-left", function(nick) | 433 room:hook("occupant-left", function(nick) |
442 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end | 434 if jids[session.full_jid] then jids[session.full_jid].ar_last[nick.jid:match("^(.*)/")][nick.nick] = nil; end |
443 local status_code = | 435 local status_code = |
444 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 436 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and |
445 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and | 437 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status") and |
446 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; | 438 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("status").attr.code; |
447 | 439 |
448 | 440 |
449 if status_code == "303" then | 441 if status_code == "303" then |
450 local newnick = | 442 local newnick = |
451 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and | 443 nick.presence:get_child("x","http://jabber.org/protocol/muc#user") and |
452 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and | 444 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item") and |
453 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; | 445 nick.presence:get_child("x","http://jabber.org/protocol/muc#user"):get_child("item").attr.nick; |
454 | 446 |
455 session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; | 447 session.nicks_changing[newnick] = { nick.nick, (nicks[nick.nick] and nicks[nick.nick].username or "xmpp") }; return; |
456 end | 448 end |
457 | 449 |
458 local self_change = false; | 450 for id, data in pairs(session.nicks_changing) do |
459 for _, data in pairs(session.nicks_changing) do | 451 if data[1] == nick.nick then return; end |
460 if data[1] == nick.nick then self_change = true; break; end | 452 end |
461 end | 453 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "PART", channel}; |
462 if self_change then return; end | 454 end); |
463 session.send{from=nick.nick.."!"..(nicks[nick.nick] and nicks[nick.nick].username or "xmpp"), "PART", channel}; | |
464 end); | |
465 end); | 455 end); |
466 | 456 |
467 function commands.NAMES(session, channel) | 457 function commands.NAMES(session, channel) |
468 local nicks = { }; | 458 local nicks = { }; |
469 if type(channel) == "table" then channel = channel[1] end | 459 if type(channel) == "table" then channel = channel[1] end |
470 | 460 |
471 local room = session.rooms[channel]; | 461 local room = session.rooms[channel]; |
472 | 462 |
473 local symbols_map = { | 463 local symbols_map = { |
474 owner = "~", | 464 owner = "~", |
475 administrator = "&", | 465 administrator = "&", |
476 moderator = "@", | 466 moderator = "@", |
477 member = "+" | 467 member = "+" |
478 } | 468 } |
479 | 469 |
480 if not room then return end | 470 if not room then return end |
481 -- TODO Break this out into commands.NAMES | 471 -- TODO Break this out into commands.NAMES |
482 for nick, n in pairs(room.occupants) do | 472 for nick, n in pairs(room.occupants) do |
483 if n.affiliation == "owner" and n.role == "moderator" then | 473 if n.affiliation == "owner" and n.role == "moderator" then |
484 nick = symbols_map[n.affiliation]..nick; | 474 nick = symbols_map[n.affiliation]..nick; |
485 elseif n.affiliation == "administrator" and n.role == "moderator" then | 475 elseif n.affiliation == "administrator" and n.role == "moderator" then |
486 nick = symbols_map[n.affiliation]..nick; | 476 nick = symbols_map[n.affiliation]..nick; |
487 elseif n.affiliation == "member" and n.role == "moderator" then | 477 elseif n.affiliation == "member" and n.role == "moderator" then |
488 nick = symbols_map[n.role]..nick; | 478 nick = symbols_map[n.role]..nick; |
489 elseif n.affiliation == "member" and n.role == "partecipant" then | 479 elseif n.affiliation == "member" and n.role == "partecipant" then |
490 nick = symbols_map[n.affiliation]..nick; | 480 nick = symbols_map[n.affiliation]..nick; |
491 elseif n.affiliation == "none" and n.role == "moderator" then | 481 elseif n.affiliation == "none" and n.role == "moderator" then |
492 nick = symbols_map[n.role]..nick; | 482 nick = symbols_map[n.role]..nick; |
493 end | 483 end |
494 table.insert(nicks, nick); | 484 table.insert(nicks, nick); |
495 end | 485 end |
496 nicks = table.concat(nicks, " "); | 486 nicks = table.concat(nicks, " "); |
497 session.send((":%s 353 %s = %s :%s"):format(muc_server, session.nick, channel, nicks)); | 487 session.send((":%s 353 %s = %s :%s"):format(muc_server, session.nick, channel, nicks)); |
498 session.send((":%s 366 %s %s :End of /NAMES list."):format(muc_server, session.nick, channel)); | 488 session.send((":%s 366 %s %s :End of /NAMES list."):format(muc_server, session.nick, channel)); |
499 session.send(":"..muc_server.." 353 "..session.nick.." = "..channel.." :"..nicks); | 489 session.send(":"..muc_server.." 353 "..session.nick.." = "..channel.." :"..nicks); |
500 end | 490 end |
501 | 491 |
502 function commands.PART(session, args) | 492 function commands.PART(session, args) |
503 local channel, part_message = unpack(args); | 493 local channel, part_message = unpack(args); |
504 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; | 494 local room = channel and nodeprep(channel:match("^#(%w+)")) or nil; |
505 if not room then return end | 495 if not room then return end |
506 channel = channel:match("^([%S]*)"); | 496 channel = channel:match("^([%S]*)"); |
507 session.rooms[channel]:leave(part_message); | 497 session.rooms[channel]:leave(part_message); |
508 jids[session.full_jid].ar_last[room.."@"..muc_server] = nil; | 498 jids[session.full_jid].ar_last[room.."@"..muc_server] = nil; |
509 session.send(":"..session.nick.." PART :"..channel); | 499 session.send(":"..session.nick.." PART :"..channel); |
510 end | 500 end |
511 | 501 |
512 function commands.PRIVMSG(session, args) | 502 function commands.PRIVMSG(session, args) |
513 local channel, message = unpack(args); | 503 local channel, message = unpack(args); |
514 if message and #message > 0 then | 504 if message and #message > 0 then |
515 if message:sub(1,8) == "\1ACTION " then | 505 if message:sub(1,8) == "\1ACTION " then |
516 message = "/me ".. message:sub(9,-2) | 506 message = "/me ".. message:sub(9,-2) |
517 end | 507 end |
518 message = utf8_clean(message); | 508 message = utf8_clean(message); |
519 if channel:sub(1,1) == "#" then | 509 if channel:sub(1,1) == "#" then |
520 if session.rooms[channel] then | 510 if session.rooms[channel] then |
521 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); | 511 module:log("debug", "%s sending PRIVMSG \"%s\" to %s", session.nick, message, channel); |
522 session.rooms[channel]:send_message(message); | 512 session.rooms[channel]:send_message(message); |
523 end | 513 end |
524 else -- private message | 514 else -- private message |
525 local nick = channel; | 515 local nick = channel; |
526 module:log("debug", "PM to %s", nick); | 516 module:log("debug", "PM to %s", nick); |
527 for channel, room in pairs(session.rooms) do | 517 for channel, room in pairs(session.rooms) do |
528 module:log("debug", "looking for %s in %s", nick, channel); | 518 module:log("debug", "looking for %s in %s", nick, channel); |
529 if room.occupants[nick] then | 519 if room.occupants[nick] then |
530 module:log("debug", "found %s in %s", nick, channel); | 520 module:log("debug", "found %s in %s", nick, channel); |
531 local who = room.occupants[nick]; | 521 local who = room.occupants[nick]; |
532 -- FIXME PMs in verse | 522 -- FIXME PMs in verse |
533 --room:send_private_message(nick, message); | 523 --room:send_private_message(nick, message); |
534 local pm = st.message({type="chat",to=who.jid}, message); | 524 local pm = st.message({type="chat",to=who.jid}, message); |
535 module:log("debug", "sending PM to %s: %s", nick, tostring(pm)); | 525 module:log("debug", "sending PM to %s: %s", nick, tostring(pm)); |
536 room:send(pm) | 526 room:send(pm) |
537 break | 527 break |
538 end | 528 end |
539 end | 529 end |
540 end | 530 end |
541 end | 531 end |
542 end | 532 end |
543 | 533 |
544 function commands.PING(session, args) | 534 function commands.PING(session, args) |
545 session.send{from=muc_server, "PONG", args[1]}; | 535 session.send{from=muc_server, "PONG", args[1]}; |
546 end | 536 end |
547 | 537 |
548 function commands.TOPIC(session, message) | 538 function commands.TOPIC(session, message) |
549 if not message then return end | 539 if not message then return end |
550 local channel, topic = message[1], message[2]; | 540 local channel, topic = message[1], message[2]; |
551 channel = utf8_clean(channel); | 541 channel = utf8_clean(channel); |
552 topic = utf8_clean(topic); | 542 topic = utf8_clean(topic); |
553 if not channel then return end | 543 if not channel then return end |
554 local room = session.rooms[channel]; | 544 local room = session.rooms[channel]; |
555 | 545 |
556 if topic then room:set_subject(topic); end | 546 if topic then room:set_subject(topic); end |
557 end | 547 end |
558 | 548 |
559 function commands.WHO(session, args) | 549 function commands.WHO(session, args) |
560 local channel = args[1]; | 550 local channel = args[1]; |
561 if session.rooms[channel] then | 551 if session.rooms[channel] then |
562 local room = session.rooms[channel] | 552 local room = session.rooms[channel] |
563 for nick in pairs(room.occupants) do | 553 for nick in pairs(room.occupants) do |
564 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} | 554 session.send{from=muc_server, 352, session.nick, channel, nick, nick, muc_server, nick, "H", "0 "..nick} |
565 end | 555 end |
566 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; | 556 session.send{from=muc_server, 315, session.nick, channel, "End of /WHO list"}; |
567 end | 557 end |
568 end | 558 end |
569 | 559 |
570 function commands.MODE(session, args) -- Empty command | 560 function commands.MODE(session, args) -- Empty command |
571 end | 561 end |
572 | 562 |
573 function commands.QUIT(session, args) | 563 function commands.QUIT(session, args) |
574 session.send{"ERROR", "Closing Link: "..session.nick}; | 564 session.send{"ERROR", "Closing Link: "..session.nick}; |
575 for _, room in pairs(session.rooms) do | 565 for _, room in pairs(session.rooms) do |
576 room:leave(args[1]); | 566 room:leave(args[1]); |
577 end | 567 end |
578 jids[session.full_jid] = nil; | 568 jids[session.full_jid] = nil; |
579 nicks[session.nick] = nil; | 569 nicks[session.nick] = nil; |
580 usernames[session.username] = nil; | 570 usernames[session.username] = nil; |
581 sessions[session.conn] = nil; | 571 sessions[session.conn] = nil; |
582 session:close(); | 572 session:close(); |
583 end | 573 end |
584 | 574 |
585 function commands.RAW(session, data) -- Empty command | 575 function commands.RAW(session, data) -- Empty command |
586 end | 576 end |
587 | 577 |
588 local function desetup() | 578 local function desetup() |
589 require "net.connlisteners".deregister("irc"); | 579 require "net.connlisteners".deregister("irc"); |
590 end | 580 end |
591 | 581 |
592 require "net.connlisteners".register("irc", irc_listener); | 582 require "net.connlisteners".register("irc", irc_listener); |
593 require "net.connlisteners".start("irc"); | 583 require "net.connlisteners".start("irc"); |
594 | 584 |
595 module:hook("module-unloaded", desetup) | 585 module:hook("module-unloaded", desetup) |
596 |