Author: Saimonmoore
URL: http://www.ejabberd.im/node/2835#comment-51756

diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index 13dafa7..09c9518 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -35,6 +35,9 @@
 	 try_register/3,
 	 dirty_get_registered_users/0,
 	 get_vh_registered_users/1,
+	 get_vh_registered_users/2,
+	 get_vh_registered_users_number/1,
+	 get_vh_registered_users_number/2,
 	 get_password/2,
 	 get_password_s/2,
 	 is_user_exists/2,
@@ -43,10 +46,21 @@
 	 plain_password_required/0
 	]).
 
+-include("ejabberd.hrl").
+
+-record(passwd, {us, password}).
+
 %%%----------------------------------------------------------------------
 %%% API
 %%%----------------------------------------------------------------------
 start(Host) ->
+  mnesia:create_table(passwd, [{disc_copies, [node()]},
+			 {attributes, record_info(fields, passwd)}]),
+  update_table(),
+  ejabberd_ctl:register_commands(
+    Host,
+    [{"registered-users", "list all registered users"}],
+    ejabberd_auth, ctl_process_get_registered),  
     extauth:start(
       Host, ejabberd_config:get_local_option({extauth_program, Host})),
     ok.
@@ -63,27 +77,196 @@ check_password(User, Server, Password, _StreamID, _Digest) ->
 set_password(User, Server, Password) ->
     extauth:set_password(User, Server, Password).
 
-try_register(_User, _Server, _Password) ->
-    {error, not_allowed}.
+try_register(User, Server, Password) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    if
+	(LUser == error) or (LServer == error) ->
+	    {error, invalid_jid};
+	true ->
+	    F = fun() ->
+			case mnesia:read({passwd, US}) of
+			    [] ->
+				mnesia:write(#passwd{us = US,
+						     password = Password}),
+				extauth:try_register(User, Server, Password),		     
+				ok;
+			    [_E] ->
+				exists
+			end
+		end,
+	    mnesia:transaction(F)
+    end.
 
 dirty_get_registered_users() ->
-    [].
+    mnesia:dirty_all_keys(passwd).
+
+get_vh_registered_users(Server) ->
+    LServer = jlib:nameprep(Server),
+    mnesia:dirty_select(
+      passwd,
+      [{#passwd{us = '$1', _ = '_'}, 
+	[{'==', {element, 2, '$1'}, LServer}], 
+	['$1']}]).
+	
+get_vh_registered_users(Server, [{from, Start}, {to, End}]) 
+	when is_integer(Start) and is_integer(End) ->
+    get_vh_registered_users(Server, [{limit, End-Start+1}, {offset, Start}]);
+
+get_vh_registered_users(Server, [{limit, Limit}, {offset, Offset}]) 
+	when is_integer(Limit) and is_integer(Offset) ->
+    case get_vh_registered_users(Server) of
+    [] ->
+	[];
+    Users ->
+	Set = lists:keysort(1, Users),
+	L = length(Set),
+	Start = if Offset < 1 -> 1;
+	           Offset > L -> L;
+	           true -> Offset
+	        end,
+	lists:sublist(Set, Start, Limit)
+    end;
+
+get_vh_registered_users(Server, [{prefix, Prefix}]) 
+	when is_list(Prefix) ->
+    Set = [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
+    lists:keysort(1, Set);
+
+get_vh_registered_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}]) 
+	when is_list(Prefix) and is_integer(Start) and is_integer(End) ->
+    get_vh_registered_users(Server, [{prefix, Prefix}, {limit, End-Start+1}, {offset, Start}]);
+
+get_vh_registered_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}]) 
+	when is_list(Prefix) and is_integer(Limit) and is_integer(Offset) ->
+    case [{U,S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)] of
+    [] ->
+	[];
+    Users ->
+	Set = lists:keysort(1, Users),
+	L = length(Set),
+	Start = if Offset < 1 -> 1;
+	           Offset > L -> L;
+	           true -> Offset
+	        end,
+	lists:sublist(Set, Start, Limit)
+    end;
+
+get_vh_registered_users(Server, _) ->
+    get_vh_registered_users(Server).
+
+get_vh_registered_users_number(Server) ->
+    Set = get_vh_registered_users(Server),
+    length(Set).
 
-get_vh_registered_users(_Server) ->
-    [].
+get_vh_registered_users_number(Server, [{prefix, Prefix}]) when is_list(Prefix) ->
+    Set = [{U, S} || {U, S} <- get_vh_registered_users(Server), lists:prefix(Prefix, U)],
+    length(Set);
+    
+get_vh_registered_users_number(Server, _) ->
+    get_vh_registered_users_number(Server).	
 
-get_password(_User, _Server) ->
-    false.
+get_password(User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    case catch mnesia:dirty_read(passwd, US) of
+	[#passwd{password = Password}] ->
+	    Password;
+	_ ->
+	    false
+    end.
 
-get_password_s(_User, _Server) ->
-    "".
+get_password_s(User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    case catch mnesia:dirty_read(passwd, US) of
+	[#passwd{password = Password}] ->
+	    Password;
+	_ ->
+	    []
+    end.
 
 is_user_exists(User, Server) ->
     extauth:is_user_exists(User, Server).
 
-remove_user(_User, _Server) ->
-    {error, not_allowed}.
+remove_user(User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    F = fun() ->
+		mnesia:delete({passwd, US})
+        end,
+    mnesia:transaction(F),
+    ejabberd_hooks:run(remove_user, LServer, [User, Server]),
+    extauth:remove_user(User, Server).
 
-remove_user(_User, _Server, _Password) ->
-    not_allowed.
+remove_user(User, Server, Password) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    US = {LUser, LServer},
+    F = fun() ->
+		case mnesia:read({passwd, US}) of
+		    [#passwd{password = Password}] ->
+			mnesia:delete({passwd, US}),
+			ok;
+		    [_] ->
+			not_allowed;
+		    _ ->
+			not_exists
+		end
+        end,
+    case mnesia:transaction(F) of
+	{atomic, ok} ->
+	    ejabberd_hooks:run(remove_user, LServer, [User, Server]),
+	    extauth:remove_user(User, Server, Password),
+	    ok;
+	{atomic, Res} ->
+	    Res;
+	_ ->
+	    bad_request
+    end.
 
+update_table() ->
+    Fields = record_info(fields, passwd),
+    case mnesia:table_info(passwd, attributes) of
+	Fields ->
+	    ok;
+	[user, password] ->
+	    ?INFO_MSG("Converting passwd table from "
+		      "{user, password} format", []),
+	    Host = ?MYNAME,
+	    {atomic, ok} = mnesia:create_table(
+			     ejabberd_auth_internal_tmp_table,
+			     [{disc_only_copies, [node()]},
+			      {type, bag},
+			      {local_content, true},
+			      {record_name, passwd},
+			      {attributes, record_info(fields, passwd)}]),
+	    mnesia:transform_table(passwd, ignore, Fields),
+	    F1 = fun() ->
+			 mnesia:write_lock_table(ejabberd_auth_internal_tmp_table),
+			 mnesia:foldl(
+			   fun(#passwd{us = U} = R, _) ->
+				   mnesia:dirty_write(
+				     ejabberd_auth_internal_tmp_table,
+				     R#passwd{us = {U, Host}})
+			   end, ok, passwd)
+		 end,
+	    mnesia:transaction(F1),
+	    mnesia:clear_table(passwd),
+	    F2 = fun() ->
+			 mnesia:write_lock_table(passwd),
+			 mnesia:foldl(
+			   fun(R, _) ->
+				   mnesia:dirty_write(R)
+			   end, ok, ejabberd_auth_internal_tmp_table)
+		 end,
+	    mnesia:transaction(F2),
+	    mnesia:delete_table(ejabberd_auth_internal_tmp_table);
+	_ ->
+	    ?INFO_MSG("Recreating passwd table", []),
+	    mnesia:transform_table(passwd, ignore, Fields)
+    end.
\ No newline at end of file
diff --git a/src/extauth.erl b/src/extauth.erl
index b84546d..0188d4c 100644
--- a/src/extauth.erl
+++ b/src/extauth.erl
@@ -27,8 +27,15 @@
 -module(extauth).
 -author('leifj@it.su.se').
 
--export([start/2, stop/1, init/2,
-	 check_password/3, set_password/3, is_user_exists/2]).
+-export([start/2, 
+         stop/1, 
+         init/2,
+	       check_password/3, 
+	       set_password/3, 
+	       try_register/3, 
+	       remove_user/2, 
+	       remove_user/3, 
+	       is_user_exists/2]).
 
 
 start(Host, ExtPrg) ->
@@ -51,6 +58,15 @@ is_user_exists(User, Server) ->
 
 set_password(User, Server, Password) ->
     call_port(Server, ["setpass", User, Server, Password]).
+    
+try_register(User, Server, Password) ->
+    call_port(Server, ["tryregister", User, Server, Password]).
+    
+remove_user(User, Server) ->
+    call_port(Server, ["removeuser", User, Server]).
+
+remove_user(User, Server, Password) ->
+    call_port(Server, ["removeuser", User, Server, Password]).    
 
 call_port(Server, Msg) ->
     LServer = jlib:nameprep(Server),

