Software /
code /
prosody-modules
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 |
child | 132: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") }); |