Merge with 0.6
authorMatthew Wild <mwild1@gmail.com>
Mon, 17 May 2010 10:56:36 +0000 (11:56 +0100)
committerMatthew Wild <mwild1@gmail.com>
Mon, 17 May 2010 10:56:36 +0000 (11:56 +0100)
12 files changed:
1  2 
core/configmanager.lua
core/usermanager.lua
plugins/mod_bosh.lua
plugins/mod_console.lua
plugins/mod_posix.lua
plugins/muc/muc.lib.lua
prosody
util/sasl.lua
util/sasl/anonymous.lua
util/sasl/digest-md5.lua
util/sasl/plain.lua
util/sasl/scram.lua

index 9b03e1c7ad539eed1ed6526ebef5c3ce3164b5eb,1b1a6dc5ad12be470be0d513d441658b0d96086c..54fb0a9ae82fdb61d22f4d1600f27855f84a3d46
@@@ -30,11 -30,11 +30,10 @@@ local host_mt = { __index = global_conf
  -- When key not found in section, check key in global's section
  function section_mt(section_name)
        return { __index =      function (t, k)
--                                      local section = rawget(global_config, section_name);
--                                      if not section then return nil; end
--                                      return section[k];
--                              end
--      };
++                                                                      local section = rawget(global_config, section_name);
++                                                                      if not section then return nil; end
++                                                                      return section[k];
++                                                      end };
  end
  
  function getconfig()
        function parsers.lua.load(data, filename)
                local env;
                -- The ' = true' are needed so as not to set off __newindex when we assign the functions below
--              env = setmetatable({
-                       Host = true, host = true, VirtualHost = true,
-                       Component = true, component = true,
 -                      Host = true; host = true; Component = true, component = true,
--                      Include = true, include = true, RunScript = dofile }, {
--                              __index = function (t, k)
--                                      return rawget(_G, k) or
--                                              function (settings_table)
--                                                      config[__currenthost or "*"][k] = settings_table;
--                                              end;
--                              end,
--                              __newindex = function (t, k, v)
--                                      set(env.__currenthost or "*", "core", k, v);
--                              end
--              });
++              env = setmetatable({ Host = true, host = true, VirtualHost = true, Component = true, component = true,
++                                                      Include = true, include = true, RunScript = dofile }, { __index = function (t, k)
++                                                                                              return rawget(_G, k) or
++                                                                                                              function (settings_table)
++                                                                                                                      config[__currenthost or "*"][k] = settings_table;
++                                                                                                              end;
++                                                                              end,
++                                                              __newindex = function (t, k, v)
++                                                                                      set(env.__currenthost or "*", "core", k, v);
++                                                                              end});
                
                rawset(env, "__currenthost", "*") -- Default is global
 -              function env.Host(name)
 +              function env.VirtualHost(name)
                        if rawget(config, name) and rawget(config[name].core, "component_module") then
                                error(format("Host %q clashes with previously defined %s Component %q, for services use a sub-domain like conference.%s",
                                        name, config[name].core.component_module:gsub("^%a+$", { component = "external", muc = "MUC"}), name, name), 0);
index a97e2ad701ce2551acee6c139bf6f54a9e74110f,1ee4736e3791bbba1bf56b9be8a03f0d6404e270..8d7270c2e59ee797341b87f0839931de88bbf162
@@@ -16,132 -16,117 +16,82 @@@ local jid_bare = require "util.jid".bar
  local config = require "core.configmanager";
  local hosts = hosts;
  
--local prosody = _G.prosody;
--
  module "usermanager"
  
--local new_default_provider;
--
- local function host_handler(host)
 -prosody.events.add_handler("host-activated", function (host)
--      local host_session = hosts[host];
--      host_session.events.add_handler("item-added/auth-provider", function (provider)
--              if config.get(host, "core", "authentication") == provider.name then
--                      host_session.users = provider;
--              end
--      end);
--      host_session.events.add_handler("item-removed/auth-provider", function (provider)
--              if host_session.users == provider then
--                      host_session.users = new_default_provider(host);
--              end
--      end);
--      host_session.users = new_default_provider(host); -- Start with the default usermanager provider
- end
- prosody.events.add_handler("host-activated", host_handler);
- prosody.events.add_handler("component-activated", host_handler);
 -end);
--
  local function is_cyrus(host) return config.get(host, "core", "sasl_backend") == "cyrus"; end
  
--function new_default_provider(host)
--      local provider = { name = "default" };
--      
--      function provider.test_password(username, password)
--              if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
--              local credentials = datamanager.load(username, host, "accounts") or {};
--      
++function validate_credentials(host, username, password, method)
++      log("debug", "User '%s' is being validated", username);
++      if is_cyrus(host) then return nil, "Legacy auth not supported with Cyrus SASL."; end
++      local credentials = datamanager.load(username, host, "accounts") or {};
++
++      if method == nil then method = "PLAIN"; end
++      if method == "PLAIN" and credentials.password then -- PLAIN, do directly
                if password == credentials.password then
                        return true;
                else
                        return nil, "Auth failed. Invalid username or password.";
                end
++  end
++      -- must do md5
++      -- make credentials md5
++      local pwd = credentials.password;
++      if not pwd then pwd = credentials.md5; else pwd = hashes.md5(pwd, true); end
++      -- make password md5
++      if method == "PLAIN" then
++              password = hashes.md5(password or "", true);
++      elseif method ~= "DIGEST-MD5" then
++              return nil, "Unsupported auth method";
        end
--
--      function provider.get_password(username)
--              if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
--              return (datamanager.load(username, host, "accounts") or {}).password;
-       end
-       
-       function provider.set_password(username, password)
-               if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
-               local account = datamanager.load(username, host, "accounts");
-               if account then
-                       account.password = password;
-                       return datamanager.store(username, host, "accounts", account);
-               end
-               return nil, "Account not available.";
-       end
-       function provider.user_exists(username)
-               if is_cyrus(host) then return true; end
-               return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
-       end
-       function provider.create_user(username, password)
-               if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
-               return datamanager.store(username, host, "accounts", {password = password});
-       end
-       function provider.get_supported_methods()
-               return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
++      -- compare
++      if password == pwd then
++              return true;
++      else
++              return nil, "Auth failed. Invalid username or password.";
        end
 -      
 -      function provider.set_password(username, password)
 -              if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
 -              local account = datamanager.load(username, host, "accounts");
 -              if account then
 -                      account.password = password;
 -                      return datamanager.store(username, host, "accounts", account);
 -              end
 -              return nil, "Account not available.";
 -      end
 -
 -      function provider.user_exists(username)
 -              if is_cyrus(host) then return true; end
 -              return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
 -      end
 -
 -      function provider.create_user(username, password)
 -              if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
 -              return datamanager.store(username, host, "accounts", {password = password});
 -      end
 -
 -      function provider.get_supported_methods()
 -              return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
 -      end
--
--      function provider.is_admin(jid)
 -              host = host or "*";
--              local admins = config.get(host, "core", "admins");
-               if admins ~= config.get("*", "core", "admins") then
-                       if type(admins) == "table" then
-                               jid = jid_bare(jid);
-                               for _,admin in ipairs(admins) do
-                                       if admin == jid then return true; end
-                               end
-                       elseif admins then
-                               log("error", "Option 'admins' for host '%s' is not a table", host);
 -              if host ~= "*" and admins == config.get("*", "core", "admins") then
 -                      return nil;
 -              end
 -              if type(admins) == "table" then
 -                      jid = jid_bare(jid);
 -                      for _,admin in ipairs(admins) do
 -                              if admin == jid then return true; end
--                      end
 -              elseif admins then
 -                      log("warn", "Option 'admins' for host '%s' is not a table", host);
--              end
-               return is_admin(jid); -- Test whether it's a global admin instead
 -              return nil;
--      end
--      return provider;
--end
--
--function validate_credentials(host, username, password, method)
--      return hosts[host].users.test_password(username, password);
  end
  
  function get_password(username, host)
--      return hosts[host].users.get_password(username);
++      if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
++      return (datamanager.load(username, host, "accounts") or {}).password
  end
--
  function set_password(username, host, password)
--      return hosts[host].users.set_password(username, password);
++      if is_cyrus(host) then return nil, "Passwords unavailable for Cyrus SASL."; end
++      local account = datamanager.load(username, host, "accounts");
++      if account then
++              account.password = password;
++              return datamanager.store(username, host, "accounts", account);
++      end
++      return nil, "Account not available.";
  end
  
  function user_exists(username, host)
--      return hosts[host].users.user_exists(username);
++      if is_cyrus(host) then return true; end
++      return datamanager.load(username, host, "accounts") ~= nil; -- FIXME also check for empty credentials
  end
  
  function create_user(username, password, host)
--      return hosts[host].users.create_user(username, password);
++      if is_cyrus(host) then return nil, "Account creation/modification not available with Cyrus SASL."; end
++      return datamanager.store(username, host, "accounts", {password = password});
  end
  
  function get_supported_methods(host)
--      return hosts[host].users.get_supported_methods();
++      return {["PLAIN"] = true, ["DIGEST-MD5"] = true}; -- TODO this should be taken from the config
  end
  
  function is_admin(jid, host)
-       if host and host ~= "*" then
-               return hosts[host].users.is_admin(jid);
-       else -- Test only whether this JID is a global admin
-               local admins = config.get("*", "core", "admins");
-               if type(admins) == "table" then
-                       jid = jid_bare(jid);
-                       for _,admin in ipairs(admins) do
-                               if admin == jid then return true; end
-                       end
-               elseif admins then
-                       log("error", "Option 'admins' for host '%s' is not a table", host);
-               end
 -      return hosts[host].users.is_admin(jid);
++      host = host or "*";
++      local admins = config.get(host, "core", "admins");
++      if host ~= "*" and admins == config.get("*", "core", "admins") then
 +              return nil;
 +      end
++      if type(admins) == "table" then
++              jid = jid_bare(jid);
++              for _,admin in ipairs(admins) do
++                      if admin == jid then return true; end
++              end
++      elseif admins then log("warn", "Option 'admins' for host '%s' is not a table", host); end
++      return nil;
  end
  
- _M.new_default_provider = new_default_provider;
  return _M;
index 04887c2932ffcb54634f3b14334cf9b7d994ed13,02f3ce38457eefc7e9bcddb3debeac80603aa3aa..b11de6c6d7c29ead7b59492f6be8178272dbc1ee
@@@ -236,8 -250,15 +257,9 @@@ function stream_callbacks.streamopened(
                return;
        end
        
 -      -- If session was inactive, make sure it is now marked as not
 -      if #session.requests == 0 then
 -              (session.log or log)("debug", "BOSH client now active again at %d", os_time());
 -              inactive_sessions[session] = nil;
 -      end
 -      
        if session.notopen then
                local features = st.stanza("stream:features");
+               hosts[session.host].events.fire_event("stream-features", { origin = session, features = features });
                fire_event("stream-features", session, features);
                session.send(features);
                session.notopen = nil;
Simple merge
Simple merge
index 273e21ce94214b585d54580b60eb658b6d59f5af,273e21ce94214b585d54580b60eb658b6d59f5af..18c80325bbd1bce5db492b36643407d4d1b4bf65
@@@ -122,13 -122,13 +122,9 @@@ function room_mt:broadcast_message(stan
                local history = self._data['history'];
                if not history then history = {}; self._data['history'] = history; end
                stanza = st.clone(stanza);
--              stanza.attr.to = "";
--              local stamp = datetime.datetime();
--              local chars = #tostring(stanza);
--              stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = stamp}):up(); -- XEP-0203
++              stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203
                stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated)
--              local entry = { stanza = stanza, stamp = stamp };
--              t_insert(history, entry);
++              t_insert(history, st.preserialize(stanza));
                while #history > history_length do t_remove(history, 1) end
        end
  end
@@@ -155,46 -155,46 +151,12 @@@ function room_mt:send_occupant_list(to
                end
        end
  end
--function room_mt:send_history(to, stanza)
++function room_mt:send_history(to)
        local history = self._data['history']; -- send discussion history
        if history then
--              local x_tag = stanza and stanza:get_child("x", "http://jabber.org/protocol/muc");
--              local history_tag = x_tag and x_tag:get_child("history", "http://jabber.org/protocol/muc");
--              
--              local maxchars = history_tag and tonumber(history_tag.attr.maxchars);
--              if maxchars then maxchars = math.floor(maxchars); end
--              
--              local maxstanzas = math.floor(history_tag and tonumber(history_tag.attr.maxstanzas) or #history);
--              if not history_tag then maxstanzas = 20; end
--
--              local seconds = history_tag and tonumber(history_tag.attr.seconds);
--              if seconds then seconds = datetime.datetime(os.time() - math.floor(seconds)); end
--
--              local since = history_tag and history_tag.attr.since;
--              if since and not since:match("^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d:%d%dZ$") then since = nil; end -- FIXME timezone support
--              if seconds and (not since or since < seconds) then since = seconds; end
--
--              local n = 0;
--              local charcount = 0;
--              local stanzacount = 0;
--              
--              for i=#history,1,-1 do
--                      local entry = history[i];
--                      if maxchars then
--                              if not entry.chars then
--                                      entry.stanza.attr.to = "";
--                                      entry.chars = #tostring(entry.stanza);
--                              end
--                              charcount = charcount + entry.chars + #to;
--                              if charcount > maxchars then break; end
--                      end
--                      if since and since > entry.stamp then break; end
--                      if n + 1 > maxstanzas then break; end
--                      n = n + 1;
--              end
--              for i=#history-n+1,#history do
--                      local msg = history[i].stanza;
--                      msg.attr.to = to;
++              for _, msg in ipairs(history) do
++                      msg = st.deserialize(msg);
++                      msg.attr.to=to;
                        self:_route_stanza(msg);
                end
        end
@@@ -357,7 -357,7 +319,12 @@@ function room_mt:handle_to_occupant(ori
                                                                :tag("item", {affiliation=affiliation or "none", role=role or "none"}):up()
                                                                :tag("status", {code='110'}));
                                                end
--                                              self:send_history(from, stanza);
++                                              if self._data.whois == 'anyone' then -- non-anonymous?
++                                                      self:_route_stanza(st.stanza("message", {from=to, to=from, type='groupchat'})
++                                                              :tag("x", {xmlns='http://jabber.org/protocol/muc#user'})
++                                                              :tag("status", {code='100'}));
++                                              end
++                                              self:send_history(from);
                                        else -- banned
                                                local reply = st.error_reply(stanza, "auth", "forbidden"):up();
                                                reply.tags[1].attr.code = "403";
@@@ -784,7 -784,7 +751,7 @@@ en
  function room_mt:set_role(actor, occupant_jid, role, callback, reason)
        if role == "none" then role = nil; end
        if role and role ~= "moderator" and role ~= "participant" and role ~= "visitor" then return nil, "modify", "not-acceptable"; end
--      if self:get_affiliation(actor) ~= "owner" then return nil, "cancel", "not-allowed"; end
++      if self:get_role(self._jid_nick[actor]) ~= "moderator" then return nil, "cancel", "not-allowed"; end
        local occupant = self._occupants[occupant_jid];
        if not occupant then return nil, "modify", "not-acceptable"; end
        if occupant.affiliation == "owner" or occupant.affiliation == "admin" then return nil, "cancel", "not-allowed"; end
@@@ -837,9 -837,9 +804,6 @@@ function room_mt:_route_stanza(stanza
                                end
                        end
                end
--              if self._data.whois == 'anyone' then
--                  muc_child:tag('status', { code = '100' });
--              end
        end
        self:route_stanza(stanza);
        if muc_child then
diff --cc prosody
index 517762f3c438d4d7061739f7a8f3ec95e86d3175,8e96eb46ac88d1cfdc42bf146a0570928686bd17..c7f91456d7d66f3f27b7ad16e7591bc942a76d58
+++ b/prosody
@@@ -22,6 -22,6 +22,9 @@@ if CFG_SOURCEDIR the
        package.cpath = CFG_SOURCEDIR.."/?.so;"..package.cpath;
  end
  
++package.path = package.path..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.lua";
++package.cpath = package.cpath..";"..(CFG_SOURCEDIR or ".").."/fallbacks/?.so";
++
  -- Substitute ~ with path to home directory in data path
  if CFG_DATADIR then
        if os.getenv("HOME") then
        end
  end
  
---- Global 'prosody' object
--prosody = { events = require "util.events".new(); };
--local prosody = prosody;
--
  -- Load the config-parsing module
  config = require "core.configmanager"
  
@@@ -156,6 -152,6 +151,10 @@@ function init_global_state(
        full_sessions = {};
        hosts = {};
  
++      -- Global 'prosody' object
++      prosody = {};
++      local prosody = prosody;
++      
        prosody.bare_sessions = bare_sessions;
        prosody.full_sessions = full_sessions;
        prosody.hosts = hosts;
        
        prosody.arg = _G.arg;
  
++      prosody.events = require "util.events".new();
++      
        prosody.platform = "unknown";
        if os.getenv("WINDIR") then
                prosody.platform = "windows";
        -- Function to reopen logfiles
        function prosody.reopen_logfiles()
                log("info", "Re-opening log files");
++              eventmanager.fire_event("reopen-log-files"); -- Handled by appropriate log sinks
                prosody.events.fire_event("reopen-log-files");
        end
  
@@@ -285,9 -281,8 +287,9 @@@ en
  function load_secondary_libraries()
        --- Load and initialise core modules
        require "util.import"
-       require "util.xmppstream"
        require "core.xmlhandlers"
        require "core.rostermanager"
++      require "core.eventmanager"
        require "core.hostmanager"
        require "core.modulemanager"
        require "core.usermanager"
@@@ -329,8 -324,8 +331,9 @@@ function init_data_store(
  end
  
  function prepare_to_start()
-       log("debug", "Prosody is using the %s backend for connection handling", server.get_backend());
+       log("info", "Prosody is using the %s backend for connection handling", server.get_backend());
        -- Signal to modules that we are ready to start
++      eventmanager.fire_event("server-starting");
        prosody.events.fire_event("server-starting");
  
        -- start listening on sockets
@@@ -448,12 -443,12 +451,14 @@@ init_data_store()
  init_global_protection();
  prepare_to_start();
  
++eventmanager.fire_event("server-started");
  prosody.events.fire_event("server-started");
  
  loop();
  
  log("info", "Shutting down...");
  cleanup();
++eventmanager.fire_event("server-stopped");
  prosody.events.fire_event("server-stopped");
  log("info", "Shutdown complete");
  
diff --cc util/sasl.lua
index 306acc0c590998be8eb151e3ddc7cdf13ad12058,306acc0c590998be8eb151e3ddc7cdf13ad12058..eb71956b692aa5942bf0690b4787865132f588ce
@@@ -41,6 -41,6 +41,27 @@@ Authentication Backend Prototypes
  state = false : disabled
  state = true : enabled
  state = nil : non-existant
++
++plain:
++      function(username, realm)
++              return password, state;
++      end
++
++plain-test:
++      function(username, realm, password)
++              return true or false, state;
++      end
++
++digest-md5:
++      function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken
++                                                                                              -- implementations it's not
++              return digesthash, state;
++      end
++
++digest-md5-test:
++      function(username, domain, realm, encoding, digesthash)
++              return true or false, state;
++      end
  ]]
  
  local method = {};
index f3e31a7f0d7f7783a1bc08d68c0588a864108664,f3e31a7f0d7f7783a1bc08d68c0588a864108664..656502945252d738139f4cfd2d981db3a3c4b400
@@@ -1,5 -1,5 +1,5 @@@
  -- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
  --
  --    All rights reserved.
  --
@@@ -20,16 -20,16 +20,6 @@@ module "anonymous
  
  --=========================
  --SASL ANONYMOUS according to RFC 4505
--
----[[
--Supported Authentication Backends
--
--anonymous:
--      function(username, realm)
--              return true; --for normal usage just return true; if you don't like the supplied username you can return false.
--      end
--]]
--
  local function anonymous(self, message)
        local username;
        repeat
index 8986ca4567f3884be857d7c576df1950a96c10df,8986ca4567f3884be857d7c576df1950a96c10df..04acf04dcde50bbc9735c4c16eb78df5aceff663
@@@ -1,5 -1,5 +1,5 @@@
  -- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
  --
  --    All rights reserved.
  --
@@@ -29,21 -29,21 +29,6 @@@ module "digest-md5
  --=========================
  --SASL DIGEST-MD5 according to RFC 2831
  
----[[
--Supported Authentication Backends
--
--digest-md5:
--      function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken
--                                                                                              -- implementations it's not
--              return digesthash, state;
--      end
--
--digest-md5-test:
--      function(username, domain, realm, encoding, digesthash)
--              return true or false, state;
--      end
--]]
--
  local function digest(self, message)
        --TODO complete support for authzid
  
index 2abbc53a1688dd66c2d02253676a69446a5aac85,2abbc53a1688dd66c2d02253676a69446a5aac85..ae5c777a77423f466ecd5977f696e0f9bec7e313
@@@ -1,5 -1,5 +1,5 @@@
  -- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
  --
  --    All rights reserved.
  --
@@@ -19,26 -19,26 +19,6 @@@ module "plain
  
  -- ================================
  -- SASL PLAIN according to RFC 4616
--
----[[
--Supported Authentication Backends
--
--plain:
--      function(username, realm)
--              return password, state;
--      end
--
--plain-test:
--      function(username, realm, password)
--              return true or false, state;
--      end
--      
--plain-hashed:
--      function(username, realm)
--              return hashed_password, hash_function, state;
--      end
--]]
--
  local function plain(self, message)
        if not message then
                return "failure", "malformed-request";
                if correct_password == password then correct = true; else correct = false; end
        elseif self.profile.plain_test then
                correct, state = self.profile.plain_test(authentication, self.realm, password);
--      elseif self.profile.plain_hashed then
--              local hashed_password, hash_f;
--              hashed_password, hash_f, state = self.profile.plain_hashed(authentication, self.realm);
--              if hashed_password == hash_f(password) then correct = true; else correct = false; end
        end
  
        self.username = authentication
@@@ -85,7 -85,7 +61,7 @@@
  end
  
  function init(registerMechanism)
--      registerMechanism("PLAIN", {"plain", "plain_test", "plain_hashed"}, plain);
++      registerMechanism("PLAIN", {"plain", "plain_test"}, plain);
  end
  
  return _M;
index ed7d7bc385a0de3fbfd33c8b4223c0deff1db26d,4875731f31d36dda51370d049276809f8645951e..103e8a905d65373efb062cdd5020495773760e75
@@@ -1,5 -1,5 +1,5 @@@
  -- sasl.lua v0.4
---- Copyright (C) 2008-2010 Tobias Markmann
++-- Copyright (C) 2008-2009 Tobias Markmann
  --
  --    All rights reserved.
  --
@@@ -28,16 -28,16 +28,6 @@@ module "scram
  
  --=========================
  --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10
--
----[[
--Supported Authentication Backends
--
--scram-{MECH}:
--      function(username, realm)
--              return salted_password, iteration_count, salt, state;
--      end
--]]
--
  local default_i = 4096
  
  local function bp( b )
@@@ -92,95 -92,95 +82,77 @@@ local function validate_username(userna
        return username;
  end
  
--local function scram_gen(hash_name, H_f, HMAC_f)
--      local function scram_hash(self, message)
--              if not self.state then self["state"] = {} end
++local function scram_sha_1(self, message)
++      if not self.state then self["state"] = {} end
        
--              if not self.state.name then
--                      -- we are processing client_first_message
--                      local client_first_message = message;
--                      self.state["client_first_message"] = client_first_message;
--                      self.state["name"] = client_first_message:match("n=(.+),r=")
--                      self.state["clientnonce"] = client_first_message:match("r=([^,]+)")
++      if not self.state.name then
++              -- we are processing client_first_message
++              local client_first_message = message;
++              self.state["client_first_message"] = client_first_message;
++              self.state["name"] = client_first_message:match("n=(.+),r=")
++              self.state["clientnonce"] = client_first_message:match("r=([^,]+)")
                
--                      if not self.state.name or not self.state.clientnonce then
--                              return "failure", "malformed-request";
--                      end
++              if not self.state.name or not self.state.clientnonce then
++                      return "failure", "malformed-request";
++              end
                
--                      self.state.name = validate_username(self.state.name);
--                      if not self.state.name then
--                              log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
--                              return "failure", "malformed-request", "Invalid username.";
--                      end
++              self.state.name = validate_username(self.state.name);
++              if not self.state.name then
++                      log("debug", "Username violates either SASLprep or contains forbidden character sequences.")
++                      return "failure", "malformed-request", "Invalid username.";
++              end
                
--                      self.state["servernonce"] = generate_uuid();
--                      
--                      -- retreive credentials
--                      if self.profile.plain then
-                               local password, state = self.profile.plain(self.state.name, self.realm)
 -                              password, state = self.profile.plain(self.state.name, self.realm)
--                              if state == nil then return "failure", "not-authorized"
--                              elseif state == false then return "failure", "account-disabled" end
--                              
--                              password = saslprep(password);
--                              if not password then
--                                      log("debug", "Password violates SASLprep.");
--                                      return "failure", "not-authorized", "Invalid password."
--                              end
--                              self.state.salt = generate_uuid();
--                              self.state.iteration_count = default_i;
--                              self.state.salted_password = Hi(HMAC_f, password, self.state.salt, default_i);
--                      elseif self.profile["scram_"..hash_name] then
-                               local salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm);
 -                              salted_password, iteration_count, salt, state = self.profile["scram-"..hash_name](self.state.name, self.realm);
--                              if state == nil then return "failure", "not-authorized"
--                              elseif state == false then return "failure", "account-disabled" end
--                              
--                              self.state.salted_password = salted_password;
--                              self.state.iteration_count = iteration_count;
--                              self.state.salt = salt
--                      end
++              self.state["servernonce"] = generate_uuid();
++              self.state["salt"] = generate_uuid();
                
--                      local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..self.state.iteration_count;
--                      self.state["server_first_message"] = server_first_message;
--                      return "challenge", server_first_message
--              else
--                      if type(message) ~= "string" then return "failure", "malformed-request" end
--                      -- we are processing client_final_message
--                      local client_final_message = message;
++              local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i;
++              self.state["server_first_message"] = server_first_message;
++              return "challenge", server_first_message
++      else
++              if type(message) ~= "string" then return "failure", "malformed-request" end
++              -- we are processing client_final_message
++              local client_final_message = message;
                
--                      self.state["proof"] = client_final_message:match("p=(.+)");
--                      self.state["nonce"] = client_final_message:match("r=(.+),p=");
--                      self.state["channelbinding"] = client_final_message:match("c=(.+),r=");
--                      if not self.state.proof or not self.state.nonce or not self.state.channelbinding then
--                              return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
++              self.state["proof"] = client_final_message:match("p=(.+)");
++              self.state["nonce"] = client_final_message:match("r=(.+),p=");
++              self.state["channelbinding"] = client_final_message:match("c=(.+),r=");
++              if not self.state.proof or not self.state.nonce or not self.state.channelbinding then
++                      return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message.";
++              end
++              
++              local password, state;
++              if self.profile.plain then
++                      password, state = self.profile.plain(self.state.name, self.realm)
++                      if state == nil then return "failure", "not-authorized"
++                      elseif state == false then return "failure", "account-disabled" end
++                      password = saslprep(password);
++                      if not password then
++                              log("debug", "Password violates SASLprep.");
++                              return "failure", "not-authorized", "Invalid password."
                        end
++              end
                
--                      local SaltedPassword = self.state.salted_password;
--                      local ClientKey = HMAC_f(SaltedPassword, "Client Key")
--                      local ServerKey = HMAC_f(SaltedPassword, "Server Key")
--                      local StoredKey = H_f(ClientKey)
--                      local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
--                      local ClientSignature = HMAC_f(StoredKey, AuthMessage)
--                      local ClientProof     = binaryXOR(ClientKey, ClientSignature)
--                      local ServerSignature = HMAC_f(ServerKey, AuthMessage)
++              local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i)
++              local ClientKey = hmac_sha1(SaltedPassword, "Client Key")
++              local ServerKey = hmac_sha1(SaltedPassword, "Server Key")
++              local StoredKey = sha1(ClientKey)
++              local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+")
++              local ClientSignature = hmac_sha1(StoredKey, AuthMessage)
++              local ClientProof     = binaryXOR(ClientKey, ClientSignature)
++              local ServerSignature = hmac_sha1(ServerKey, AuthMessage)
                
--                      if base64.encode(ClientProof) == self.state.proof then
--                              local server_final_message = "v="..base64.encode(ServerSignature);
--                              self["username"] = self.state.name;
--                              return "success", server_final_message;
--                      else
--                              return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
--                      end
++              if base64.encode(ClientProof) == self.state.proof then
++                      local server_final_message = "v="..base64.encode(ServerSignature);
++                      self["username"] = self.state.name;
++                      return "success", server_final_message;
++              else
++                      return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated.";
                end
        end
--      return scram_hash;
  end
  
  function init(registerMechanism)
--      local function registerSCRAMMechanism(hash_name, hash, hmac_hash)
--              registerMechanism("SCRAM-"..hash_name, {"plain", "scram_"..(hash_name:lower())}, scram_gen(hash_name:lower(), hash, hmac_hash));
--      end
--      
--      registerSCRAMMechanism("SHA-1", sha1, hmac_sha1);
++      registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1);
  end
  
  return _M;