Mercurial > 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 |
| 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") }); |
