Mercurial > prosody-modules
view mod_net_dovecotauth/mod_net_dovecotauth.lua @ 3532:85c357b69eec
mod_csi_muc_priorities: Reduce importance of group chat messages
This helps if you are in more noisy public channels than small private
group chats.
The short term plan is to give users the ability to set MUC JIDs as
either high or low priority and use that. Long term it would be great to
be able to automatically classify MUCs as public channels vs private
group chats.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Mon, 01 Apr 2019 00:15:13 +0200 |
parents | 8e686bf63441 |
children |
line wrap: on
line source
-- mod_net_dovecotauth.lua -- -- Protocol spec: -- http://dovecot.org/doc/auth-protocol.txt -- -- Example postfix config: -- sudo postconf smtpd_sasl_path=inet:127.0.0.1:28484 -- sudo postconf smtpd_sasl_type=dovecot -- sudo postconf smtpd_sasl_auth_enable=yes module:set_global(); -- Imports local new_sasl = require "core.usermanager".get_sasl_handler; local user_exists = require "core.usermanager".user_exists; local base64 = require"util.encodings".base64; local dump = require"util.serialization".serialize; local pposix = require "util.pposix"; -- Config local default_vhost = module:get_option_string("dovecotauth_host", (next(hosts))); -- TODO Is there a better solution? local allow_master = module:get_option_boolean("dovecotauth_allow_master", false); -- Active sessions local sessions = {}; -- Session methods local new_session; do local sess = { }; local sess_mt = { __index = sess }; function new_session(conn) local s = { type = "?", conn = conn, buf = "", sasl = {} } function s:log(l, m, ...) return module:log(l, self.type..tonumber(tostring(self):match("%x+$"), 16)..": "..m, ...); end return setmetatable(s, sess_mt); end function sess:send(...) local data = table.concat({...}, "\t") .. "\n" -- self:log("debug", "SEND: %s", dump(ret)); return self.conn:write(data); end local mech_params = { ANONYMOUS = "anonymous"; PLAIN = "plaintext"; ["DIGEST-MD5"] = "mutual-auth"; ["SCRAM-SHA-1"] = "mutual-auth"; ["SCRAM-SHA-1-PLUS"] = "mutual-auth"; } function sess:handshake() self:send("VERSION", 1, 1); self:send("SPID", pposix.getpid()); self:send("CUID", tonumber(tostring(self):match"%x+$", 16)); for mech in pairs(self.g_sasl:mechanisms()) do self:send("MECH", mech, mech_params[mech]); end self:send("DONE"); end function sess:feed(data) -- TODO break this up a bit -- module:log("debug", "sess = %s", dump(self)); local buf = self.buf; buf = buf .. data; local line, eol = buf:match("(.-)\r?\n()") while line and line ~= "" do buf = buf:sub(eol); self.buf = buf; local part = line:gmatch("[^\t]+"); local command = part(); if command == "VERSION" then local major = tonumber(part()); local minor = tonumber(part()); if major ~= 1 then self:log("warn", "Wrong version, expected 1.1, got %s.%s", tostring(major), tostring(minor)); self.conn:close(); break; end elseif command == "CPID" then self.type = "C"; self.pid = part(); elseif command == "SPID" and allow_master then self.type = "M"; self.pid = part(); elseif command == "AUTH" and self.type ~= "?" then -- C: "AUTH" TAB <id> TAB <mechanism> TAB service=<service> [TAB <parameters>] local id = part() -- <id> local sasl = self.sasl[id]; local mech = part(); if not sasl then -- TODO Should maybe initialize SASL handler after parsing the line? sasl = self.g_sasl:clean_clone(); self.sasl[id] = sasl; if not sasl:select(mech) then self:send("FAIL", id, "reason=invalid-mechanism"); self.sasl[id] = nil; sasl = false end end if sasl then local params = {}; -- Not used for anything yet for p in part do local k,v = p:match("^([^=]*)=(.*)$"); if k == "resp" then self:log("debug", "params = %s", dump(params)); v = base64.decode(v); local status, ret, err = sasl:process(v); self:log("debug", status); if status == "challenge" then self:send("CONT", id, base64.encode(ret)); elseif status == "failure" then self.sasl[id] = nil; self:send("FAIL", id, "reason="..tostring(err)); elseif status == "success" then self.sasl[id] = nil; self:send("OK", id, "user="..sasl.username, ret and "resp="..base64.encode(ret)); end break; -- resp MUST be the last param else params[k or p] = v or true; end end end elseif command == "USER" and self.type == "M" then -- FIXME Should this be on a separate listener? local id = part(); local user = part(); if user and user_exists(user, default_vhost) then self:send("USER", id); else self:send("NOTFOUND", id); end else self:log("warn", "Unhandled command %s", tostring(command)); self.conn:close(); break; end line, eol = buf:match("(.-)\r?\n()") end end end local listener = {} function listener.onconnect(conn) local s = new_session(conn); sessions[conn] = s; local g_sasl = new_sasl(default_vhost, s); s.g_sasl = g_sasl; s:handshake(); end function listener.onincoming(conn, data) local s = sessions[conn]; -- s:log("debug", "RECV %s", dump(data)); return s:feed(data); end function listener.ondisconnect(conn) sessions[conn] = nil; end function module.unload() for c in pairs(sessions) do c:close(); end end module:provides("net", { default_port = 28484; listener = listener; });