comparison mod_ircd/mod_ircd.lua @ 111:3de60860adca

mod_ircd: Initial commit of a wonderfully hacky but working IRC->XMPP interface for Prosody
author Matthew Wild <mwild1@gmail.com>
date Wed, 23 Dec 2009 14:22:02 +0000
parents
children d4ff1cd414e5
comparison
equal deleted inserted replaced
110:e02281edc273 111:3de60860adca
1 local irc_listener = { default_port = 6667, default_mode = "*l" };
2
3 local sessions = {};
4 local commands = {};
5
6 local nicks = {};
7
8 local st = require "util.stanza";
9
10 local conference_server = module:get_option("conference_server") or "conference.jabber.org";
11
12 local function irc_close_session(session)
13 session.conn:close();
14 end
15
16 function irc_listener.onincoming(conn, data)
17 local session = sessions[conn];
18 if not session then
19 session = { conn = conn, host = module.host, reset_stream = function () end,
20 close = irc_close_session, log = logger.init("irc"..(conn.id or "1")),
21 roster = {} };
22 sessions[conn] = session;
23 function session.data(data)
24 module:log("debug", "Received: %s", data);
25 local command, args = data:match("^%s*([^ ]+) *(.*)%s*$");
26 if not command then
27 module:log("warn", "Invalid command: %s", data);
28 return;
29 end
30 command = command:upper();
31 module:log("debug", "Received command: %s", command);
32 if commands[command] then
33 local ret = commands[command](session, args);
34 if ret then
35 session.send(ret.."\r\n");
36 end
37 end
38 end
39 function session.send(data)
40 module:log("debug", "sending: %s", data);
41 return conn:write(data.."\r\n");
42 end
43 end
44 if data then
45 session.data(data);
46 end
47 end
48
49 function irc_listener.ondisconnect(conn, error)
50 module:log("debug", "Client disconnected");
51 sessions[conn] = nil;
52 end
53
54 function commands.NICK(session, nick)
55 nick = nick:match("^[%w_]+");
56 if nicks[nick] then
57 session.send(":"..session.host.." 433 * The nickname "..nick.." is already in use");
58 return;
59 end
60 nicks[nick] = session;
61 session.nick = nick;
62 session.full_jid = nick.."@"..module.host.."/ircd";
63 session.type = "c2s";
64 module:log("debug", "Client bound to %s", session.full_jid);
65 session.send(":"..session.host.." 001 "..session.nick.." :Welcome to XMPP via the "..session.host.." gateway "..session.nick);
66 end
67
68 local joined_mucs = {};
69 function commands.JOIN(session, channel)
70 if not joined_mucs[channel] then
71 joined_mucs[channel] = { occupants = {}, sessions = {} };
72 end
73 joined_mucs[channel].sessions[session] = true;
74 local join_stanza = st.presence({ from = session.full_jid, to = channel:gsub("^#", "").."@"..conference_server.."/"..session.nick });
75 core_process_stanza(session, join_stanza);
76 session.send(":"..session.nick.." JOIN :"..channel);
77 session.send(":"..session.host.." 332 "..session.nick.." "..channel.." :Connection in progress...");
78 session.send(":"..session.host.." 353 "..session.nick.." = "..channel.." :"..session.nick);
79 session.send(":"..session.host.." 366 "..session.nick.." "..channel.." :End of /NAMES list.");
80 end
81
82 function commands.PART(session, channel)
83 local channel, part_message = channel:match("^(%S+) :(.+)$");
84 core_process_stanza(session, st.presence{ type = "unavailable", from = session.full_jid,
85 to = channel:gsub("^#", "").."@"..conference_server.."/"..session.nick }:tag("status"):text(part_message));
86 end
87
88 function commands.PRIVMSG(session, message)
89 local who, message = message:match("^(%S+) :(.+)$");
90 if joined_mucs[who] then
91 core_process_stanza(session, st.message{to=who:gsub("^#", "").."@"..conference_server, type="groupchat"}:tag("body"):text(message));
92 end
93 end
94
95 function commands.WHO(session, channel)
96 if joined_mucs[channel] then
97 for nick in pairs(joined_mucs[channel].occupants) do
98 --n=MattJ 91.85.191.50 irc.freenode.net MattJ H :0 Matthew Wild
99 session.send(":"..session.host.." 352 "..session.nick.." "..channel.." "..nick.." "..nick.." "..session.host.." "..nick.." H :0 "..nick);
100 end
101 session.send(":"..session.host.." 315 "..session.nick.." "..channel.. " :End of /WHO list");
102 end
103 end
104
105 function commands.MODE(session, channel)
106 session.send(":"..session.host.." 324 "..session.nick.." "..channel.." +J");
107 end
108
109 --- Component (handle stanzas from the server for IRC clients)
110 function irc_component(origin, stanza)
111 local from, from_bare = stanza.attr.from, jid.bare(stanza.attr.from);
112 local from_node = "#"..jid.split(stanza.attr.from);
113
114 if joined_mucs[from_node] and from_bare == from then
115 -- From room itself
116 local joined_muc = joined_mucs[from_node];
117 if stanza.name == "message" then
118 local subject = stanza:get_child("subject");
119 if subject then
120 local subject_text = subject:get_text();
121 for session in pairs(joined_muc.sessions) do
122 session.send(":"..session.host.." 332 "..session.nick.." "..from_node.." :"..subject_text);
123 end
124 end
125 end
126 elseif joined_mucs[from_node] then
127 -- From room occupant
128 local joined_muc = joined_mucs[from_node];
129 local nick = select(3, jid.split(from)):gsub(" ", "_");
130 if stanza.name == "presence" then
131 local what;
132 if not stanza.attr.type then
133 if joined_muc.occupants[nick] then
134 return;
135 end
136 joined_muc.occupants[nick] = true;
137 what = "JOIN";
138 else
139 joined_muc.occupants[nick] = nil;
140 what = "PART";
141 end
142 for session in pairs(joined_muc.sessions) do
143 if nick ~= session.nick then
144 session.send(":"..nick.."!"..nick.." "..what.." :"..from_node);
145 end
146 end
147 elseif stanza.name == "message" then
148 local body = stanza:get_child("body");
149 local hasdelay = stanza:get_child("delay", "urn:xmpp:delay");
150 if body then
151 for session in pairs(joined_muc.sessions) do
152 if nick ~= session.nick or hasdelay then
153 session.send(":"..nick.." PRIVMSG "..from_node.." :"..body:get_text());
154 end
155 end
156 end
157 end
158 end
159 end
160
161 require "core.componentmanager".register_component(module.host, irc_component);
162
163 prosody.events.add_handler("server-stopping", function (shutdown)
164 module:log("debug", "Closing IRC connections prior to shutdown");
165 for channel, joined_muc in pairs(joined_mucs) do
166 for session in pairs(joined_muc.sessions) do
167 core_process_stanza(session,
168 st.presence{ type = "unavailable", from = session.full_jid,
169 to = channel:gsub("^#", "").."@"..conference_server.."/"..session.nick }
170 :tag("status")
171 :text("Connection closed: Server is shutting down"..(shutdown.reason and (": "..shutdown.reason) or "")));
172 session:close();
173 end
174 end
175 end);
176
177 require "net.connlisteners".register("irc", irc_listener);
178 require "net.connlisteners".start("irc", { port = module:get_option("port") });