public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ejabberd] f45: Updated mod_Admin_extra to work with R15B (see rhbz #840039)
@ 2026-09-23 19:07 Peter Lemenkov
  0 siblings, 0 replies; only message in thread
From: Peter Lemenkov @ 2026-09-23 19:07 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/ejabberd
            Branch : f45
            Commit : 17b4fc696481fd38f8f237e9ed5df1ecbd7bfe38
            Author : Peter Lemenkov <lemenkov@gmail.com>
            Date   : 2012-07-15T11:06:44+04:00
            Stats  : +2324/-4242 in 18 file(s)
            URL    : https://src.fedoraproject.org/rpms/ejabberd/c/17b4fc696481fd38f8f237e9ed5df1ecbd7bfe38?branch=f45

            Log:
            Updated mod_Admin_extra to work with R15B (see rhbz #840039)

Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>

---
diff --git a/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch b/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
index bdffb3f..a7db2a3 100644
--- a/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
+++ b/ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
@@ -1,7 +1,7 @@
 From 72718b8fcbb19b34e348efcb874fa480e83d7a02 Mon Sep 17 00:00:00 2001
 From: Peter Lemenkov <lemenkov@gmail.com>
 Date: Tue, 16 Feb 2010 16:03:38 +0300
-Subject: [PATCH 01/10] Fix PAM service example name to match actual one
+Subject: [PATCH 1/8] Fix PAM service example name to match actual one
 
 Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
 ---
@@ -22,5 +22,5 @@ index a4068ad..9b24a4a 100644
  %%
  %% Authentication using LDAP
 -- 
-1.7.10.1
+1.7.10.4
 

diff --git a/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch b/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch
deleted file mode 100644
index a03d3bb..0000000
--- a/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch
+++ /dev/null
@@ -1,936 +0,0 @@
-From 6bed76f77b83a859798d39a51969987739e4f4fe Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Tue, 16 Feb 2010 16:05:53 +0300
-Subject: [PATCH 02/10] Add mod_ctlextra as an ejabberd module
-
-See this link for the details:
-http://www.ejabberd.im/mod_ctlextra
-
-Also note that this module will be removed in the nearest future
-in favor of mod_admin_extra:
-
-svn export -r 1020 https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/ejabberd.app     |    1 +
- src/mod_ctlextra.erl |  895 ++++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 896 insertions(+)
- create mode 100644 src/mod_ctlextra.erl
-
-diff --git a/src/ejabberd.app b/src/ejabberd.app
-index 2f08601..067b928 100644
---- a/src/ejabberd.app
-+++ b/src/ejabberd.app
-@@ -71,6 +71,7 @@
- 	     mod_caps,
- 	     mod_configure2,
- 	     mod_configure,
-+	     mod_ctlextra,
- 	     mod_disco,
- 	     mod_echo,
- 	     mod_http_bind,
-diff --git a/src/mod_ctlextra.erl b/src/mod_ctlextra.erl
-new file mode 100644
-index 0000000..bc61024
---- /dev/null
-+++ b/src/mod_ctlextra.erl
-@@ -0,0 +1,895 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : mod_ctlextra.erl
-+%%% Author  : Badlop <badlop@ono.com>
-+%%% Purpose : Adds more commands to ejabberd_ctl
-+%%% Created : 30 Nov 2006 by Badlop <badlop@ono.com>
-+%%% Id      : $Id: mod_ctlextra.erl 1020 2009-08-30 10:13:34Z badlop $
-+%%%----------------------------------------------------------------------
-+
-+-module(mod_ctlextra).
-+-author('badlop@ono.com').
-+
-+-behaviour(gen_mod).
-+
-+-export([start/2,
-+	 stop/1,
-+	 ctl_process/2,
-+	 ctl_process/3
-+	]).
-+
-+-include("ejabberd.hrl").
-+-include("ejabberd_ctl.hrl").
-+-include("jlib.hrl").
-+-include("mod_roster.hrl").
-+
-+%% Copied from ejabberd_sm.erl
-+-record(session, {sid, usr, us, priority, info}).
-+
-+-compile(export_all).
-+
-+%%-------------
-+%% gen_mod
-+%%-------------
-+
-+start(Host, _Opts) ->
-+    ejabberd_ctl:register_commands(commands_global(), ?MODULE, ctl_process),
-+    ejabberd_ctl:register_commands(Host, commands_host(), ?MODULE, ctl_process).
-+
-+stop(Host) ->
-+    ejabberd_ctl:unregister_commands(commands_global(), ?MODULE, ctl_process),
-+    ejabberd_ctl:unregister_commands(Host, commands_host(), ?MODULE, ctl_process).
-+
-+commands_global() ->
-+    [
-+     {"compile file", "recompile and reload file"},
-+     {"load-config file", "load config from file"},
-+     {"remove-node nodename", "remove an ejabberd node from the database"},
-+
-+     %% ejabberd_auth
-+     {"delete-older-users days", "delete users that have not logged in the last 'days'"},
-+     {"delete-older-users-vhost host days", "delete users that not logged in last 'days' in 'host'"},
-+     {"set-password user server password", "set password to user@server"},
-+
-+     %% ejd2odbc
-+     {"export2odbc server output", "export Mnesia tables on server to files on output directory"},
-+
-+     %% mod_shared_roster
-+     {"srg-create group host name description display", "create the group with options"},
-+     {"srg-delete group host", "delete the group"},
-+     {"srg-user-add user server group host", "add user@server to group on host"},
-+     {"srg-user-del user server group host", "delete user@server from group on host"},
-+     {"srg-list-groups host", "list the shared roster groups from host"},
-+     {"srg-get-info group host", "get info of a specific group on host"},
-+
-+     %% mod_vcard
-+     {"vcard-get user host data [data2]", "get data from the vCard of the user"},
-+     {"vcard-set user host data [data2] content", "set data to content on the vCard"},
-+
-+     %% mod_announce
-+     %% announce_send_online host message
-+     %% announce_send_all host, message
-+
-+     %% mod_roster
-+     {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1's roster"},
-+     %%{"", "subs= none, from, to or both"},
-+     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
-+     %%{"", "will add mike@server.com to peter@localhost roster"},
-+     {"rem-rosteritem user1 server1 user2 server2", "Remove user2@server2 from user1@server1's roster"},
-+     {"rosteritem-purge [options]", "Purge all rosteritems that match filtering options"},
-+     {"pushroster file user server", "push template roster in file to user@server"},
-+     {"pushroster-all file", "push template roster in file to all those users"},
-+     {"push-alltoall server group", "adds all the users to all the users in Group"},
-+
-+     {"status-list status", "list the logged users with status"},
-+     {"status-num status", "number of logged users with status"},
-+
-+     {"stats registeredusers", "number of registered users"},
-+     {"stats onlineusers", "number of logged users"},
-+     {"stats onlineusersnode", "number of logged users in the ejabberd node"},
-+     {"stats uptime-seconds", "uptime of ejabberd node in seconds"},
-+
-+     %% misc
-+     {"get-cookie", "get the Erlang cookie of this node"},
-+     {"killsession user server resource", "kill a user session"}
-+    ].
-+
-+commands_host() ->
-+    [
-+     %% mod_last
-+     {"num-active-users days", "number of users active in the last 'days'"},
-+     {"status-list status", "list the logged users with status"},
-+     {"status-num status", "number of logged users with status"},
-+     {"stats registeredusers", "number of registered users"},
-+     {"stats onlineusers", "number of logged users"},
-+
-+     %% misc
-+     {"ban-account username [reason]", "ban account: kick sessions and change password"}
-+    ].
-+
-+
-+%%-------------
-+%% Commands global
-+%%-------------
-+
-+ctl_process(_Val, ["delete-older-users", Days]) ->
-+    {removed, N, UR} = delete_older_users(list_to_integer(Days)),
-+    io:format("Deleted ~p users: ~p~n", [N, UR]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["delete-older-users-vhost", Host, Days]) ->
-+    {removed, N, UR} = delete_older_users_vhost(Host, list_to_integer(Days)),
-+    io:format("Deleted ~p users: ~p~n", [N, UR]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["export2odbc", Server, Output]) ->
-+    Tables = [
-+	      {export_last, last},
-+	      {export_offline, offline},
-+	      {export_passwd, passwd},
-+	      {export_private_storage, private_storage},
-+	      {export_roster, roster},
-+	      {export_vcard, vcard},
-+	      {export_vcard_search, vcard_search}],
-+    Export = fun({TableFun, Table}) -> 
-+		     Filename = filename:join([Output, atom_to_list(Table)++".txt"]),
-+		     io:format("Trying to export Mnesia table '~p' on server '~s' to file '~s'~n", [Table, Server, Filename]),
-+		     Res = (catch ejd2odbc:TableFun(Server, Filename)),
-+		     io:format("  Result: ~p~n", [Res])
-+	     end,
-+    lists:foreach(Export, Tables),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["set-password", User, Server, Password]) ->
-+    ejabberd_auth:set_password(User, Server, Password),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
-+    {ok, Res} = vcard_get(User, Server, [Data]),
-+    io:format("~s~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
-+    {ok, Res} = vcard_get(User, Server, [Data1, Data2]),
-+    io:format("~s~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["vcard-set", User, Server, Data1, Content]) ->
-+    {ok, Res} = vcard_set(User, Server, [Data1], Content),
-+    io:format("~s~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
-+    {ok, Res} = vcard_set(User, Server, [Data1, Data2], Content),
-+    io:format("~s~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["compile", Module]) ->
-+    compile:file(Module),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["remove-node", Node]) ->
-+    mnesia:del_table_copy(schema, list_to_atom(Node)),
-+    ?STATUS_SUCCESS;
-+
-+%% The Display argument can be several groups separated with ,
-+%% Example: ejabberdctl srg-create aa localhost Name Desc Display1,Display2,Display3
-+ctl_process(_Val, ["srg-create" | Parameters]) ->
-+	[Group, Host, Name, Description, Display] = group_parameters(Parameters, "'"),
-+    DisplayList = string:tokens(Display, ","),
-+    Opts = [{name, Name}, {displayed_groups, DisplayList}, {description, Description}],
-+    {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["srg-delete", Group, Host]) ->
-+    {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
-+    {atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
-+    {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["srg-list-groups", Host]) ->
-+    lists:foreach(
-+      fun(SrgGroup) ->
-+	      io:format("~s~n",[SrgGroup])
-+      end,
-+      lists:sort(mod_shared_roster:list_groups(Host))),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["srg-get-info", Group, Host]) ->
-+    Opts = mod_shared_roster:get_group_opts(Host,Group),
-+    [io:format("~s: ~p~n", [Title, Value]) || {Title , Value} <- Opts],
-+    
-+    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
-+    Members_string = [ " " ++ jlib:jid_to_string(jlib:make_jid(MUser, MServer, "")) 
-+		       || {MUser, MServer} <- Members],
-+    io:format("members:~s~n", [Members_string]),
-+    
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
-+    case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
-+	{atomic, ok} ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Can't add ~p@~p to ~p@~p: ~p~n",
-+		      [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("Can't add roster item to user ~p: ~p~n",
-+		      [LocalUser, Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["rem-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer]) ->
-+    case rem_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer) of
-+	{atomic, ok} ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Can't remove ~p@~p to ~p@~p: ~p~n",
-+		      [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("Can't remove roster item to user ~p: ~p~n",
-+		      [LocalUser, Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["rosteritem-purge"]) ->
-+    io:format("Rosteritems that match all the filtering will be removed.~n"),
-+    io:format("Options for filtering:~n"),
-+    io:format("~n"),
-+    io:format("  -subs none|from|to|both~n"),
-+    io:format("       Subscription type. By default all values~n"),
-+    io:format("~n"),
-+    io:format("  -ask none|out|in~n"),
-+    io:format("       Pending subscription. By default all values~n"),
-+    io:format("~n"),
-+    io:format("  -user JID~n"),
-+    io:format("       The JID of the local user.~n"),
-+    io:format("       Can use these globs: *, ? and [...].~n"),
-+    io:format("       By default it is: * *@*~n"),
-+    io:format("~n"),
-+    io:format("  -contact JID~n"),
-+    io:format("       Similar to 'user', but for the contact JID.~n"),
-+    io:format("~n"),
-+    io:format("Example:~n"),
-+    io:format("  ejabberdctl rosteritem-purge -subs none from to -ask out in -contact *@*icq*~n"),
-+    io:format("~n"),
-+    ?STATUS_SUCCESS;
-+ctl_process(_Val, ["rosteritem-purge" | Options_list]) ->
-+    Options_prop_list = lists:foldl(
-+			  fun(O, R) ->
-+				  case O of
-+				      [$- | K] ->
-+					  [{K, []} | R];
-+				      V ->
-+					  [{K, Vs} | RT] = R,
-+					  [{K, [V|Vs]} | RT]
-+				  end
-+			  end,
-+			  [],
-+			  Options_list),
-+    
-+    Subs = [list_to_atom(S)
-+	    || S <- proplists:get_value("subs", 
-+					Options_prop_list, 
-+					["none", "from", "to", "both"])],
-+    Asks = [list_to_atom(S)
-+	    || S <- 
-+		   proplists:get_value("ask",
-+				       Options_prop_list, 
-+				       ["none", "out", "in"])],
-+    User = proplists:get_value("user", Options_prop_list, ["*", "*@*"]),
-+    Contact = proplists:get_value("contact", Options_prop_list, ["*", "*@*"]),
-+    
-+    case rosteritem_purge({Subs, Asks, User, Contact}) of
-+	{atomic, ok} ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Error purging rosteritems: ~p~n",
-+		      [Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("BadRPC purging rosteritems: ~p~n",
-+		      [Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["pushroster", File, User, Server]) ->
-+    case pushroster(File, User, Server) of
-+	ok ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Can't push roster ~p to ~p@~p: ~p~n",
-+		      [File, User, Server, Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("Can't push roster ~p: ~p~n",
-+		      [File, Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["pushroster-all", File]) ->
-+    case pushroster_all([File]) of
-+	ok ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Can't push roster ~p: ~p~n",
-+		      [File, Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("Can't push roster ~p: ~p~n",
-+		      [File, Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["push-alltoall", Server, Group]) ->
-+    case push_alltoall(Server, Group) of
-+	ok ->
-+	    ?STATUS_SUCCESS;
-+	{error, Reason} ->
-+	    io:format("Can't push all to all: ~p~n",
-+		      [Reason]),
-+	    ?STATUS_ERROR;
-+	{badrpc, Reason} ->
-+	    io:format("Can't push all to all: ~p~n",
-+		      [Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["load-config", Path]) ->
-+    case catch ejabberd_config:load_file(Path) of
-+        ok ->
-+            ?STATUS_SUCCESS;
-+        {'EXIT', Reason} ->
-+            io:format("Problem loading config file ~p: ~p~n",
-+                      [filename:absname(Path), Reason]),
-+	    ?STATUS_ERROR;
-+        {badrpc, Reason} ->
-+            io:format("Can't load config file ~p: ~p~n",
-+                      [filename:absname(Path), Reason]),
-+	    ?STATUS_BADRPC
-+    end;
-+
-+ctl_process(_Val, ["stats", Stat]) ->
-+    Res = case Stat of
-+	      "uptime-seconds" -> uptime_seconds();
-+	      "registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
-+	      "onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
-+	      "onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
-+	  end,
-+    io:format("~p~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["status-num", Status_required]) ->
-+    ctl_process(_Val, "all", ["status-num", Status_required]);
-+
-+ctl_process(_Val, ["status-list", Status_required]) ->
-+    ctl_process(_Val, "all", ["status-list", Status_required]);
-+
-+ctl_process(_Val, ["get-cookie"]) ->
-+    io:format("~s~n", [atom_to_list(erlang:get_cookie())]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, ["killsession", User, Server, Resource | Tail]) ->
-+    kick_session(User, Server, Resource, prepare_reason(Tail)),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(Val, _Args) ->
-+    Val.
-+
-+
-+%%-------------
-+%% Commands vhost
-+%%-------------
-+
-+ctl_process(_Val, Host, ["num-active-users", Days]) ->
-+    Number = num_active_users(Host, list_to_integer(Days)),
-+    io:format("~p~n", [Number]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, Host, ["stats", Stat]) ->
-+    Res = case Stat of
-+	      "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
-+	      "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
-+	  end,
-+    io:format("~p~n", [Res]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, Host, ["status-num", Status_required]) ->
-+    Num = length(get_status_list(Host, Status_required)),
-+    io:format("~p~n", [Num]),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, Host, ["status-list", Status_required]) ->
-+    Res = get_status_list(Host, Status_required),
-+    [ io:format("~s@~s ~s ~p \"~s\"~n", [U, S, R, P, St]) || {U, S, R, P, St} <- Res],
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(_Val, Host, ["ban-account", User | Tail]) ->
-+    ban_account(User, Host, prepare_reason(Tail)),
-+    ?STATUS_SUCCESS;
-+
-+ctl_process(Val, _Host, _Args) ->
-+    Val.
-+
-+
-+%%-------------
-+%% Utils
-+%%-------------
-+
-+uptime_seconds() ->
-+    trunc(element(1, erlang:statistics(wall_clock))/1000).
-+
-+get_status_list(Host, Status_required) ->
-+    %% Get list of all logged users
-+    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
-+    %% Reformat the list
-+    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
-+    Fhost = case Host of
-+		"all" ->
-+		    %% All hosts are requested, so dont filter at all
-+		    fun(_, _) -> true end;
-+		_ ->
-+		    %% Filter the list, only Host is interesting
-+		    fun(A, B) -> A == B end
-+	    end,
-+    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
-+    %% For each Pid, get its presence
-+    Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
-+    %% Filter by status
-+    Fstatus = case Status_required of
-+		  "all" ->
-+		      fun(_, _) -> true end;
-+		  _ ->
-+		      fun(A, B) -> A == B end
-+	      end,
-+    [{User, Server, Resource, Priority, stringize(Status_text)} 
-+     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4, 
-+	apply(Fstatus, [Status, Status_required])].
-+
-+%% Make string more print-friendly
-+stringize(String) ->
-+    %% Replace newline characters with other code
-+    element(2, regexp:gsub(String, "\n", "\\n")).
-+
-+add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
-+    subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
-+    route_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription),
-+    {atomic, ok}.
-+
-+subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
-+    R = #roster{usj = {LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
-+		us = {LocalUser,LocalServer},
-+		jid = {RemoteUser,RemoteServer,[]},
-+		name = Nick,
-+		subscription = Subscription, % none, to=you see him, from=he sees you, both
-+		ask = none, % out=send request, in=somebody requests you, none
-+		groups = [Group],
-+		askmessage = Xattrs, % example: [{"category","conference"}]
-+		xs = []},
-+    mnesia:transaction(fun() -> mnesia:write(R) end).
-+
-+rem_rosteritem(LU, LS, RU, RS) ->
-+    unsubscribe(LU, LS, RU, RS),
-+    route_rosteritem(LU, LS, RU, RS, "", "", "remove"),
-+    {atomic, ok}.
-+
-+unsubscribe(LocalUser, LocalServer, RemoteUser, RemoteServer) ->
-+    Key = {{LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
-+	   {LocalUser,LocalServer}},
-+    mnesia:transaction(fun() -> mnesia:delete(roster, Key, write) end).
-+
-+route_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription) ->
-+    LJID = jlib:make_jid(LocalUser, LocalServer, ""),
-+    RJID = jlib:make_jid(RemoteUser, RemoteServer, ""),
-+    ToS = jlib:jid_to_string(LJID),
-+    ItemJIDS = jlib:jid_to_string(RJID),
-+    GroupXML = {xmlelement, "group", [], [{xmlcdata, Group}]},
-+    Item = {xmlelement, "item", 
-+	    [{"jid", ItemJIDS},
-+	     {"name", Nick},
-+	     {"subscription", Subscription}], 
-+	    [GroupXML]},
-+    Query = {xmlelement, "query", [{"xmlns", ?NS_ROSTER}], [Item]},
-+    Packet = {xmlelement, "iq", [{"type", "set"}, {"to", ToS}], [Query]},
-+    ejabberd_router:route(LJID, LJID, Packet).
-+
-+
-+%%-----------------------------
-+%% Ban user
-+%%-----------------------------
-+
-+ban_account(User, Server, Reason) ->
-+    kick_sessions(User, Server, Reason),
-+    set_random_password(User, Server, Reason).
-+
-+kick_sessions(User, Server, Reason) ->
-+    lists:map(
-+      fun(Resource) ->
-+	      kick_session(User, Server, Resource, Reason)
-+      end,
-+      get_resources(User, Server)).
-+
-+kick_session(User, Server, Resource, Reason) ->
-+    ejabberd_router:route(
-+      jlib:make_jid("", "", ""),
-+      jlib:make_jid(User, Server, Resource),
-+      {xmlelement, "broadcast", [], [{exit, Reason}]}).
-+
-+get_resources(User, Server) ->
-+    lists:map(
-+      fun(Session) ->
-+	      element(3, Session#session.usr)
-+      end,
-+      get_sessions(User, Server)).
-+
-+get_sessions(User, Server) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
-+    Sessions =  mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
-+    true = is_list(Sessions),
-+    Sessions.
-+
-+set_random_password(User, Server, Reason) ->
-+    NewPass = build_random_password(Reason),
-+    set_password(User, Server, NewPass).
-+
-+build_random_password(Reason) ->
-+    Date = jlib:timestamp_to_iso(calendar:universal_time()),
-+    RandomString = randoms:get_string(),
-+    "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
-+
-+set_password(User, Server, Password) ->
-+    {atomic, ok} = ejabberd_auth:set_password(User, Server, Password).
-+
-+prepare_reason([]) ->
-+    "Kicked by administrator";
-+prepare_reason([Reason]) ->
-+    Reason;
-+prepare_reason(StringList) ->
-+    string:join(StringList, "_").
-+
-+
-+%%-----------------------------
-+%% Purge roster items
-+%%-----------------------------
-+
-+rosteritem_purge(Options) ->
-+    Num_rosteritems = mnesia:table_info(roster, size),
-+    io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
-+    Key = mnesia:dirty_first(roster),
-+    ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
-+    {atomic, ok}.
-+
-+rip('$end_of_table', _Options, Counters) ->
-+    print_progress_line(Counters),
-+    ok;
-+rip(Key, Options, {Pr, NT, NV, ND}) ->
-+    Key_next = mnesia:dirty_next(roster, Key),
-+    ND2 = case decide_rip(Key, Options) of
-+	      true ->
-+		  mnesia:dirty_delete(roster, Key),
-+		  ND+1;
-+	      false ->
-+		  ND
-+	  end,
-+    NV2 = NV+1,
-+    Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
-+    rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
-+
-+print_progress_line({Pr, NT, NV, ND}) ->
-+    Pr2 = trunc((NV/NT)*100),
-+    case Pr == Pr2 of
-+	true ->
-+	    ok;
-+	false ->
-+	    io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
-+    end,
-+    Pr2.
-+
-+decide_rip(Key, {Subs, Asks, User, Contact}) ->
-+    case catch mnesia:dirty_read(roster, Key) of
-+	[RI] ->
-+	    lists:member(RI#roster.subscription, Subs)
-+		andalso lists:member(RI#roster.ask, Asks)
-+		andalso decide_rip_jid(RI#roster.us, User)
-+		andalso decide_rip_jid(RI#roster.jid, Contact);
-+	_ ->
-+	    false
-+    end.
-+
-+%% Returns true if the server of the JID is included in the servers
-+decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
-+    decide_rip_jid({UName, UServer}, Match_list);
-+decide_rip_jid({UName, UServer}, Match_list) ->
-+    lists:any(
-+      fun(Match_string) ->
-+	      MJID = jlib:string_to_jid(Match_string),
-+	      MName = MJID#jid.luser,
-+	      MServer = MJID#jid.lserver,
-+	      Is_server = is_glob_match(UServer, MServer),
-+	      case MName of
-+		  [] when UName == [] ->
-+		      Is_server;
-+		  [] ->
-+		      false;
-+		  _ ->
-+		      Is_server
-+			  andalso is_glob_match(UName, MName)
-+	      end
-+      end,
-+      Match_list).
-+
-+%% Copied from ejabberd-2.0.0/src/acl.erl
-+is_regexp_match(String, RegExp) ->
-+    case regexp:first_match(String, RegExp) of
-+	nomatch ->
-+	    false;
-+	{match, _, _} ->
-+	    true;
-+	{error, ErrDesc} ->
-+	    io:format(
-+	      "Wrong regexp ~p in ACL: ~p",
-+	      [RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
-+	    false
-+    end.
-+is_glob_match(String, Glob) ->
-+    is_regexp_match(String, regexp:sh_to_awk(Glob)).
-+
-+
-+%%-----------------------------
-+%% Push Roster from file 
-+%%-----------------------------
-+
-+pushroster(File, User, Server) ->
-+    {ok, [Roster]} = file:consult(File),
-+    subscribe_roster({User, Server, "", User}, Roster).
-+
-+pushroster_all(File) ->
-+    {ok, [Roster]} = file:consult(File),
-+    subscribe_all(Roster).
-+
-+subscribe_all(Roster) ->
-+    subscribe_all(Roster, Roster).
-+subscribe_all([], _) ->
-+    ok;
-+subscribe_all([User1 | Users], Roster) ->
-+    subscribe_roster(User1, Roster),
-+    subscribe_all(Users, Roster).
-+
-+subscribe_roster(_, []) ->
-+    ok;
-+%% Do not subscribe a user to itself
-+subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
-+    subscribe_roster({Name, Server, Group, Nick}, Roster);
-+%% Subscribe Name2 to Name1
-+subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
-+    subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
-+    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
-+
-+push_alltoall(S, G) ->
-+    Users = ejabberd_auth:get_vh_registered_users(S),
-+    Users2 = build_list_users(G, Users, []),
-+    subscribe_all(Users2).
-+
-+build_list_users(_Group, [], Res) ->
-+    Res;
-+build_list_users(Group, [{User, Server}|Users], Res) ->
-+    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
-+
-+vcard_get(User, Server, Data) ->
-+    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+    JID = jlib:make_jid(User, Server, ""),
-+    IQ = #iq{type = get, xmlns = ?NS_VCARD},
-+    IQr = Module:Function(JID, JID, IQ),
-+    Res = case IQr#iq.sub_el of
-+	      [A1] ->
-+		  case vcard_get(Data, A1) of
-+		      false -> no_value;
-+		      Elem -> xml:get_tag_cdata(Elem)
-+		  end;
-+	      [] -> 
-+		  no_vcard
-+	  end,
-+    {ok, Res}.
-+
-+vcard_get([Data1, Data2], A1) ->
-+    case xml:get_subtag(A1, Data1) of
-+    	false -> false;
-+	A2 -> vcard_get([Data2], A2)
-+    end;
-+
-+vcard_get([Data], A1) ->
-+    xml:get_subtag(A1, Data).
-+
-+vcard_set(User, Server, Data, Content) ->
-+    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+    JID = jlib:make_jid(User, Server, ""),
-+    IQ = #iq{type = get, xmlns = ?NS_VCARD},
-+    IQr = Module:Function(JID, JID, IQ),
-+
-+    %% Get old vcard
-+    A4 = case IQr#iq.sub_el of
-+	     [A1] ->
-+		 {_, _, _, A2} = A1,
-+		 update_vcard_els(Data, Content, A2);
-+	     [] -> 
-+		 update_vcard_els(Data, Content, [])
-+	 end,
-+
-+    %% Build new vcard
-+    SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
-+    IQ2 = #iq{type=set, sub_el = SubEl},
-+
-+    Module:Function(JID, JID, IQ2),
-+    {ok, "done"}.
-+
-+update_vcard_els(Data, Content, Els1) ->
-+    Els2 = lists:keysort(2, Els1),
-+    [Data1 | Data2] = Data,
-+    NewEl = case Data2 of
-+		[] ->
-+		    {xmlelement, Data1, [], [{xmlcdata,Content}]};
-+		[D2] ->
-+		    OldEl = case lists:keysearch(Data1, 2, Els2) of
-+				{value, A} -> A;
-+				false -> {xmlelement, Data1, [], []}
-+			    end,
-+		    {xmlelement, _, _, ContentOld1} = OldEl,
-+		    Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
-+		    ContentOld2 = lists:keysort(2, ContentOld1),
-+		    ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
-+		    ContentNew = lists:keymerge(2, Content2, ContentOld3),
-+		    {xmlelement, Data1, [], ContentNew}
-+	    end,
-+    Els3 = lists:keydelete(Data1, 2, Els2),
-+    lists:keymerge(2, [NewEl], Els3).
-+
-+-record(last_activity, {us, timestamp, status}).
-+
-+delete_older_users(Days) ->
-+    %% Get the list of registered users
-+    Users = ejabberd_auth:dirty_get_registered_users(),
-+    delete_older_users(Days, Users).
-+
-+delete_older_users_vhost(Host, Days) ->
-+    %% Get the list of registered users
-+    Users = ejabberd_auth:get_vh_registered_users(Host),
-+    delete_older_users(Days, Users).
-+
-+delete_older_users(Days, Users) ->
-+    %% Convert older time
-+    SecOlder = Days*24*60*60,
-+
-+    %% Get current time
-+    {MegaSecs, Secs, _MicroSecs} = now(),
-+    TimeStamp_now = MegaSecs * 1000000 + Secs,
-+
-+    %% For a user, remove if required and answer true
-+    F = fun({LUser, LServer}) ->
-+		%% Check if the user is logged
-+		case ejabberd_sm:get_user_resources(LUser, LServer) of
-+		    %% If it isnt
-+		    [] ->
-+			%% Look for his last_activity
-+			case mnesia:dirty_read(last_activity, {LUser, LServer}) of
-+			    %% If it is
-+			    %% existent:
-+			    [#last_activity{timestamp = TimeStamp}] ->
-+				%% get his age
-+				Sec = TimeStamp_now - TimeStamp,
-+				%% If he is
-+				if 
-+				    %% younger than SecOlder: 
-+				    Sec < SecOlder ->
-+					%% do nothing
-+					false;
-+				    %% older: 
-+				    true ->
-+					%% remove the user
-+					ejabberd_auth:remove_user(LUser, LServer),
-+					true
-+				end;
-+			    %% nonexistent:
-+			    [] ->
-+				%% remove the user
-+				ejabberd_auth:remove_user(LUser, LServer),
-+				true
-+			end;
-+		    %% Else
-+		    _ ->
-+			%% do nothing
-+			false
-+		end
-+	end,
-+    %% Apply the function to every user in the list
-+    Users_removed = lists:filter(F, Users),
-+    {removed, length(Users_removed), Users_removed}.
-+
-+num_active_users(Host, Days) ->
-+    list_last_activity(Host, true, Days).
-+
-+%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
-+list_last_activity(Host, Integral, Days) ->
-+    {MegaSecs, Secs, _MicroSecs} = now(),
-+    TimeStamp = MegaSecs * 1000000 + Secs,
-+    TS = TimeStamp - Days * 86400,
-+    case catch mnesia:dirty_select(
-+		 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
-+				  [{'>', '$1', TS}],
-+				  [{'trunc', {'/',
-+					      {'-', TimeStamp, '$1'},
-+					      86400}}]}]) of
-+							      {'EXIT', _Reason} ->
-+		 [];
-+	       Vals ->
-+		 Hist = histogram(Vals, Integral),
-+		 if
-+		     Hist == [] ->
-+			 0;
-+		     true ->
-+			 Left = if
-+				    Days == infinity ->
-+					0;
-+				    true ->
-+					Days - length(Hist)
-+				end,
-+			 Tail = if
-+				    Integral ->
-+					lists:duplicate(Left, lists:last(Hist));
-+				    true ->
-+					lists:duplicate(Left, 0)
-+				end,
-+			 lists:nth(Days, Hist ++ Tail)
-+		 end
-+	 end.
-+histogram(Values, Integral) ->
-+    histogram(lists:sort(Values), Integral, 0, 0, []).
-+histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
-+    histogram(T, Integral, Current, Count + 1, Hist);
-+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
-+    if
-+	Integral ->
-+	    histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
-+	true ->
-+	    histogram(Values, Integral, Current + 1, 0, [Count | Hist])
-+    end;
-+histogram([], _Integral, _Current, Count, Hist) ->
-+    if
-+	Count > 0 ->
-+	    lists:reverse([Count | Hist]);
-+	true ->
-+	    lists:reverse(Hist)
-+    end.
-+
-+group_parameters(Ps, [Char]) ->
-+	{none, Grouped_Ps} = lists:foldl(
-+		fun(P, {State, Res}) ->
-+			case State of
-+				none -> 
-+					case P of
-+						[Char | PTail]->
-+							{building, [PTail | Res]};
-+						_ ->
-+							{none, [P | Res]}
-+					end;
-+				building -> 
-+					[ResHead | ResTail] = Res,
-+					case lists:last(P) of
-+						Char ->
-+							P2 = lists:sublist(P, length(P)-1),
-+							{none, [ResHead ++ " " ++ P2 | ResTail]};
-+						_ ->
-+							{building, [ResHead ++ " " ++ P | ResTail]}
-+					end
-+			end
-+		end,
-+		{none, []},
-+		Ps),
-+	lists:reverse(Grouped_Ps).
--- 
-1.7.10.1
-

diff --git a/ejabberd-0002-Fixed-delays-in-s2s-connections.patch b/ejabberd-0002-Fixed-delays-in-s2s-connections.patch
new file mode 100644
index 0000000..d161c21
--- /dev/null
+++ b/ejabberd-0002-Fixed-delays-in-s2s-connections.patch
@@ -0,0 +1,53 @@
+From 30037ec8a73263f0d0524b2e1409736838cbb33b Mon Sep 17 00:00:00 2001
+From: Sergei Golovan <sgolovan@nes.ru>
+Date: Tue, 16 Feb 2010 16:07:37 +0300
+Subject: [PATCH 2/8] Fixed delays in s2s connections.
+
+Patch by Sergei Golovan increases timeouts in S2S and removes horrible 5-minute
+delay between remote server connection attempts after a falure (in case of
+server is down it would be reasonable, but what happens much more often is a
+network split when the delay is inadmissible).
+---
+ src/ejabberd_s2s_in.erl  |    2 +-
+ src/ejabberd_s2s_out.erl |    5 +++--
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
+index 1597ec7..7d411cd 100644
+--- a/src/ejabberd_s2s_in.erl
++++ b/src/ejabberd_s2s_in.erl
+@@ -414,7 +414,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
+             case {ejabberd_s2s:allow_host(LTo, LFrom),
+                   lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
+                 {true, true} ->
+-		    ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
++		    %%ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
+ 		    ejabberd_s2s_out:start(LTo, LFrom,
+ 					   {verify, self(),
+ 					    Key, StateData#state.streamid}),
+diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
+index 65711fa..cab413f 100644
+--- a/src/ejabberd_s2s_out.erl
++++ b/src/ejabberd_s2s_out.erl
+@@ -93,7 +93,7 @@
+ 						 [From, Host, Type])).
+ -endif.
+ 
+--define(FSMTIMEOUT, 30000).
++-define(FSMTIMEOUT, 60000).
+ 
+ %% We do not block on send anymore.
+ -define(TCP_SEND_TIMEOUT, 15000).
+@@ -245,7 +245,8 @@ open_socket(init, StateData) ->
+ 		    NewStateData = StateData#state{bridge={Mod, Fun}},
+ 		    {next_state, relay_to_bridge, NewStateData};
+ 		_ ->
+-		    wait_before_reconnect(StateData)
++		    %%wait_before_reconnect(StateData)
++		    {stop, normal, StateData}
+ 	    end
+     end;
+ open_socket(closed, StateData) ->
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0003-Fixed-delays-in-s2s-connections.patch b/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
deleted file mode 100644
index 78ff318..0000000
--- a/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
+++ /dev/null
@@ -1,53 +0,0 @@
-From 95f2c9bb7654ea321f052bb29c905a71471d85c0 Mon Sep 17 00:00:00 2001
-From: Sergei Golovan <sgolovan@nes.ru>
-Date: Tue, 16 Feb 2010 16:07:37 +0300
-Subject: [PATCH 03/10] Fixed delays in s2s connections.
-
-Patch by Sergei Golovan increases timeouts in S2S and removes horrible 5-minute
-delay between remote server connection attempts after a falure (in case of
-server is down it would be reasonable, but what happens much more often is a
-network split when the delay is inadmissible).
----
- src/ejabberd_s2s_in.erl  |    2 +-
- src/ejabberd_s2s_out.erl |    5 +++--
- 2 files changed, 4 insertions(+), 3 deletions(-)
-
-diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
-index 1597ec7..7d411cd 100644
---- a/src/ejabberd_s2s_in.erl
-+++ b/src/ejabberd_s2s_in.erl
-@@ -414,7 +414,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
-             case {ejabberd_s2s:allow_host(LTo, LFrom),
-                   lists:member(LTo, ejabberd_router:dirty_get_all_domains())} of
-                 {true, true} ->
--		    ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
-+		    %%ejabberd_s2s_out:terminate_if_waiting_delay(LTo, LFrom),
- 		    ejabberd_s2s_out:start(LTo, LFrom,
- 					   {verify, self(),
- 					    Key, StateData#state.streamid}),
-diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
-index 65711fa..cab413f 100644
---- a/src/ejabberd_s2s_out.erl
-+++ b/src/ejabberd_s2s_out.erl
-@@ -93,7 +93,7 @@
- 						 [From, Host, Type])).
- -endif.
- 
---define(FSMTIMEOUT, 30000).
-+-define(FSMTIMEOUT, 60000).
- 
- %% We do not block on send anymore.
- -define(TCP_SEND_TIMEOUT, 15000).
-@@ -245,7 +245,8 @@ open_socket(init, StateData) ->
- 		    NewStateData = StateData#state{bridge={Mod, Fun}},
- 		    {next_state, relay_to_bridge, NewStateData};
- 		_ ->
--		    wait_before_reconnect(StateData)
-+		    %%wait_before_reconnect(StateData)
-+		    {stop, normal, StateData}
- 	    end
-     end;
- open_socket(closed, StateData) ->
--- 
-1.7.10.1
-

diff --git a/ejabberd-0003-Introducing-mod_admin_extra.patch b/ejabberd-0003-Introducing-mod_admin_extra.patch
new file mode 100644
index 0000000..c2ed978
--- /dev/null
+++ b/ejabberd-0003-Introducing-mod_admin_extra.patch
@@ -0,0 +1,1607 @@
+From 35ddb138ec297a1b5c456fdca73b2ed0cc10c1cf Mon Sep 17 00:00:00 2001
+From: Badlop <badlop@process-one.net>
+Date: Tue, 16 Feb 2010 16:12:17 +0300
+Subject: [PATCH 3/8] Introducing mod_admin_extra
+
+Adds the mod_admin_extra module to ejabberd.
+This module extends the functionality provided by ejabberdctl
+by adding several new commands.
+
+The code is taken from the ProcessOne repository:
+
+svn export -r 1125 https://svn.process-one.net/ejabberd-modules/mod_admin_extra/trunk/src/mod_admin_extra.erl
+---
+ src/ejabberd.app        |    1 +
+ src/mod_admin_extra.erl | 1568 +++++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 1569 insertions(+)
+ create mode 100644 src/mod_admin_extra.erl
+
+diff --git a/src/ejabberd.app b/src/ejabberd.app
+index 2f08601..3d7ffe4 100644
+--- a/src/ejabberd.app
++++ b/src/ejabberd.app
+@@ -105,6 +105,7 @@
+ 	     mod_vcard_ldap,
+ 	     mod_vcard_odbc,
+ 	     mod_version,
++             mod_admin_extra,
+ 	     node_buddy,
+ 	     node_club,
+ 	     node_default,
+diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
+new file mode 100644
+index 0000000..1cef25a
+--- /dev/null
++++ b/src/mod_admin_extra.erl
+@@ -0,0 +1,1568 @@
++%%%-------------------------------------------------------------------
++%%% File    : mod_admin_extra.erl
++%%% Author  : Badlop <badlop@process-one.net>
++%%% Purpose : Contributed administrative functions and commands
++%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
++%%%
++%%%
++%%% ejabberd, Copyright (C) 2002-2008   ProcessOne
++%%%
++%%% This program is free software; you can redistribute it and/or
++%%% modify it under the terms of the GNU General Public License as
++%%% published by the Free Software Foundation; either version 2 of the
++%%% License, or (at your option) any later version.
++%%%
++%%% This program is distributed in the hope that it will be useful,
++%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
++%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++%%% General Public License for more details.
++%%%
++%%% You should have received a copy of the GNU General Public License
++%%% along with this program; if not, write to the Free Software
++%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
++%%% 02111-1307 USA
++%%%
++%%%-------------------------------------------------------------------
++
++-module(mod_admin_extra).
++-author('badlop@process-one.net').
++
++-behaviour(gen_mod).
++
++-export([start/2, stop/1,
++	 %% Node
++	 compile/1,
++	 load_config/1,
++	 get_cookie/0,
++	 remove_node/1,
++	 export2odbc/2,
++	 %% Accounts
++	 set_password/3,
++	 check_password_hash/4,
++	 delete_old_users/1,
++	 delete_old_users_vhost/2,
++	 ban_account/3,
++	 num_active_users/2,
++	 %% Sessions
++	 num_resources/2,
++	 resource_num/3,
++	 kick_session/4,
++	 status_num/2, status_num/1,
++	 status_list/2, status_list/1,
++	 connected_users_info/0,
++	 connected_users_vhost/1,
++	 set_presence/7,
++	 user_sessions_info/2,
++	 %% Vcard
++	 set_nickname/3,
++	 get_vcard/3,
++	 get_vcard/4,
++	 get_vcard_multi/4,
++	 set_vcard/4,
++	 set_vcard/5,
++	 %% Roster
++	 add_rosteritem/7,
++	 delete_rosteritem/4,
++	 process_rosteritems/5,
++	 get_roster/2,
++	 push_roster/3,
++	 push_roster_all/1,
++	 push_alltoall/2,
++	 %% mod_last
++	 set_last/4,
++	 %% mod_private
++	 private_get/4,
++	 private_set/3,
++	 %% mod_shared_roster
++	 srg_create/5,
++	 srg_delete/2,
++	 srg_list/1,
++	 srg_get_info/2,
++	 srg_get_members/2,
++	 srg_user_add/4,
++	 srg_user_del/4,
++	 %% Stanza
++	 send_message_headline/4,
++	 send_message_chat/3,
++	 send_stanza_c2s/4,
++	 privacy_set/3,
++	 %% Stats
++	 stats/1, stats/2
++	]).
++
++-include("ejabberd.hrl").
++-include("ejabberd_commands.hrl").
++-include("mod_roster.hrl").
++-include("jlib.hrl").
++
++%% Copied from ejabberd_sm.erl
++-record(session, {sid, usr, us, priority, info}).
++
++
++%%%
++%%% gen_mod
++%%%
++
++start(_Host, _Opts) ->
++    ejabberd_commands:register_commands(commands()).
++
++stop(_Host) ->
++    ejabberd_commands:unregister_commands(commands()).
++
++
++%%%
++%%% Register commands
++%%%
++
++commands() ->
++    Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n"
++	" FN		- Full Name\n"
++	" NICKNAME	- Nickname\n"
++	" BDAY		- Birthday\n"
++	" TITLE		- Work: Position\n"
++	" ROLE		- Work: Role",
++
++    Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n"
++	" N FAMILY	- Family name\n"
++	" N GIVEN	- Given name\n"
++	" N MIDDLE	- Middle name\n"
++	" ADR CTRY	- Address: Country\n"
++	" ADR LOCALITY	- Address: City\n"
++	" EMAIL USERID	- E-Mail Address\n"
++	" ORG ORGNAME	- Work: Company\n"
++	" ORG ORGUNIT	- Work: Department",
++
++    VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
++	"http://www.xmpp.org/extensions/xep-0054.html",
++
++    [
++     #ejabberd_commands{name = compile, tags = [erlang],
++			desc = "Recompile and reload Erlang source code file",
++			module = ?MODULE, function = compile,
++			args = [{file, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = load_config, tags = [server],
++			desc = "Load ejabberd configuration file",
++			module = ?MODULE, function = load_config,
++			args = [{file, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = get_cookie, tags = [erlang],
++			desc = "Get the Erlang cookie of this node",
++			module = ?MODULE, function = get_cookie,
++			args = [],
++			result = {cookie, string}},
++     #ejabberd_commands{name = remove_node, tags = [erlang],
++			desc = "Remove an ejabberd node from Mnesia clustering config",
++			module = ?MODULE, function = remove_node,
++			args = [{node, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = export2odbc, tags = [mnesia],
++			desc = "Export Mnesia tables to files in directory",
++			module = ?MODULE, function = export2odbc,
++			args = [{host, string}, {path, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = num_active_users, tags = [accounts, stats],
++			desc = "Get number of users active in the last days",
++			module = ?MODULE, function = num_active_users,
++			args = [{host, string}, {days, integer}],
++			result = {users, integer}},
++     #ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
++			desc = "Delete users that didn't log in last days, or that never logged",
++			module = ?MODULE, function = delete_old_users,
++			args = [{days, integer}],
++			result = {res, restuple}},
++     #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
++			desc = "Delete users that didn't log in last days in vhost, or that never logged",
++			module = ?MODULE, function = delete_old_users_vhost,
++			args = [{host, string}, {days, integer}],
++			result = {res, restuple}},
++
++     #ejabberd_commands{name = check_account, tags = [accounts],
++			desc = "Check if an account exists or not",
++			module = ejabberd_auth, function = is_user_exists,
++			args = [{user, string}, {host, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = check_password, tags = [accounts],
++			desc = "Check if a password is correct",
++			module = ejabberd_auth, function = check_password,
++			args = [{user, string}, {host, string}, {password, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = check_password_hash, tags = [accounts],
++			desc = "Check if the password hash is correct",
++			longdesc = "Allowed hash methods: md5, sha.",
++			module = ?MODULE, function = check_password_hash,
++			args = [{user, string}, {host, string}, {passwordhash, string}, {hashmethod, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = change_password, tags = [accounts],
++			desc = "Change the password of an account",
++			module = ?MODULE, function = set_password,
++			args = [{user, string}, {host, string}, {newpass, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = ban_account, tags = [accounts],
++			desc = "Ban an account: kick sessions and set random password",
++			module = ?MODULE, function = ban_account,
++			args = [{user, string}, {host, string}, {reason, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = num_resources, tags = [session],
++			desc = "Get the number of resources of a user",
++			module = ?MODULE, function = num_resources,
++			args = [{user, string}, {host, string}],
++			result = {resources, integer}},
++     #ejabberd_commands{name = resource_num, tags = [session],
++			desc = "Resource string of a session number",
++			module = ?MODULE, function = resource_num,
++			args = [{user, string}, {host, string}, {num, integer}],
++			result = {resource, string}},
++     #ejabberd_commands{name = kick_session, tags = [session],
++			desc = "Kick a user session",
++			module = ?MODULE, function = kick_session,
++			args = [{user, string}, {host, string}, {resource, string}, {reason, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = status_num_host, tags = [session, stats],
++			desc = "Number of logged users with this status in host",
++			module = ?MODULE, function = status_num,
++			args = [{host, string}, {status, string}],
++			result = {users, integer}},
++     #ejabberd_commands{name = status_num, tags = [session, stats],
++			desc = "Number of logged users with this status",
++			module = ?MODULE, function = status_num,
++			args = [{status, string}],
++			result = {users, integer}},
++     #ejabberd_commands{name = status_list_host, tags = [session],
++			desc = "List of users logged in host with their statuses",
++			module = ?MODULE, function = status_list,
++			args = [{host, string}, {status, string}],
++			result = {users, {list,
++					  {userstatus, {tuple, [
++								{user, string},
++								{host, string},
++								{resource, string},
++								{priority, integer},
++								{status, string}
++							       ]}}
++					 }}},
++     #ejabberd_commands{name = status_list, tags = [session],
++			desc = "List of logged users with this status",
++			module = ?MODULE, function = status_list,
++			args = [{status, string}],
++			result = {users, {list,
++					  {userstatus, {tuple, [
++								{user, string},
++								{host, string},
++								{resource, string},
++								{priority, integer},
++								{status, string}
++							       ]}}
++					 }}},
++     #ejabberd_commands{name = connected_users_info,
++			tags = [session],
++			desc = "List all established sessions and their information",
++			module = ?MODULE, function = connected_users_info,
++			args = [],
++			result = {connected_users_info,
++				  {list,
++				   {sessions, {tuple,
++					       [{jid, string},
++						{connection, string},
++						{ip, string},
++						{port, integer},
++						{priority, integer},
++						{node, string},
++						{uptime, integer}
++					       ]}}
++				  }}},
++     #ejabberd_commands{name = connected_users_vhost,
++                       tags = [session],
++                       desc = "Get the list of established sessions in a vhost",
++                       module = ?MODULE, function = connected_users_vhost,
++                       args = [{host, string}],
++                       result = {connected_users_vhost, {list, {sessions, string}}}},
++     #ejabberd_commands{name = user_sessions_info,
++			tags = [session],
++			desc = "Get information about all sessions of a user",
++			module = ?MODULE, function = user_sessions_info,
++			args = [{user, string}, {host, string}],
++			result = {sessions_info,
++				  {list,
++				   {session, {tuple,
++					      [{connection, string},
++					       {ip, string},
++					       {port, integer},
++					       {priority, integer},
++					       {node, string},
++					       {uptime, integer},
++					       {status, string},
++					       {resource, string},
++					       {statustext, string}
++					      ]}}
++				  }}},
++
++     #ejabberd_commands{name = set_presence,
++			tags = [session],
++			desc = "Set presence of a session",
++			module = ?MODULE, function = set_presence,
++			args = [{user, string}, {host, string},
++				{resource, string}, {type, string},
++				{show, string}, {status, string},
++				{priority, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = set_nickname, tags = [vcard],
++			desc = "Set nickname in a user's vCard",
++			module = ?MODULE, function = set_nickname,
++			args = [{user, string}, {host, string}, {nickname, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = get_vcard, tags = [vcard],
++			desc = "Get content from a vCard field",
++			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
++			module = ?MODULE, function = get_vcard,
++			args = [{user, string}, {host, string}, {name, string}],
++			result = {content, string}},
++     #ejabberd_commands{name = get_vcard2, tags = [vcard],
++			desc = "Get content from a vCard field",
++			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
++			module = ?MODULE, function = get_vcard,
++			args = [{user, string}, {host, string}, {name, string}, {subname, string}],
++			result = {content, string}},
++     #ejabberd_commands{name = get_vcard2_multi, tags = [vcard],
++			desc = "Get multiple contents from a vCard field (requires exmpp installed)",
++			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
++			module = ?MODULE, function = get_vcard_multi,
++			args = [{user, string}, {host, string}, {name, string}, {subname, string}],
++			result = {contents, {list, string}}},
++
++     #ejabberd_commands{name = set_vcard, tags = [vcard],
++			desc = "Set content in a vCard field",
++			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
++			module = ?MODULE, function = set_vcard,
++			args = [{user, string}, {host, string}, {name, string}, {content, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = set_vcard2, tags = [vcard],
++			desc = "Set content in a vCard subfield",
++			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
++			module = ?MODULE, function = set_vcard,
++			args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = set_vcard2_multi, tags = [vcard],
++			desc = "Set multiple contents in a vCard subfield",
++			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
++			module = ?MODULE, function = set_vcard,
++			args = [{user, string}, {host, string}, {name, string}, {subname, string}, {contents, {list, string}}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = add_rosteritem, tags = [roster],
++			desc = "Add an item to a user's roster (supports ODBC)",
++			module = ?MODULE, function = add_rosteritem,
++			args = [{localuser, string}, {localserver, string},
++				{user, string}, {server, string},
++				{nick, string}, {group, string},
++				{subs, string}],
++			result = {res, rescode}},
++     %%{"", "subs= none, from, to or both"},
++     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
++     %%{"", "will add mike@server.com to peter@localhost roster"},
++     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
++			desc = "Delete an item from a user's roster (supports ODBC)",
++			module = ?MODULE, function = delete_rosteritem,
++			args = [{localuser, string}, {localserver, string},
++				{user, string}, {server, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = process_rosteritems, tags = [roster],
++			desc = "List or delete rosteritems that match filtering options",
++			longdesc = "Explanation of each argument:\n"
++			" - action: what to do with each rosteritem that "
++			"matches all the filtering options\n"
++			" - subs: subscription type\n"
++			" - asks: pending subscription\n"
++			" - users: the JIDs of the local user\n"
++			" - contacts: the JIDs of the contact in the roster\n"
++			"\n"
++			"Allowed values in the arguments:\n"
++			"  ACTION = list | delete\n"
++			"  SUBS = SUB[:SUB]* | any\n"
++			"  SUB = none | from | to | both\n"
++			"  ASKS = ASK[:ASK]* | any\n"
++			"  ASK = none | out | in\n"
++			"  USERS = JID[:JID]* | any\n"
++			"  CONTACTS = JID[:JID]* | any\n"
++			"  JID = characters valid in a JID, and can use the "
++			"globs: *, ?, ! and [...]\n"
++			"\n"
++			"This example will list roster items with subscription "
++			"'none', 'from' or 'to' that have any ask property, of "
++			"local users which JID is in the virtual host "
++			"'example.org' and that the contact JID is either a "
++			"bare server name (without user part) or that has a "
++			"user part and the server part contains the word 'icq'"
++			":\n  list none:from:to any *@example.org *:*@*icq*",
++			module = ?MODULE, function = process_rosteritems,
++			args = [{action, string}, {subs, string},
++				{asks, string}, {users, string},
++				{contacts, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = get_roster, tags = [roster],
++			desc = "Get roster of a local user",
++			module = ?MODULE, function = get_roster,
++			args = [{user, string}, {host, string}],
++			result = {contacts, {list, {contact, {tuple, [
++								      {jid, string},
++								      {nick, string},
++								      {subscription, string},
++								      {ask, string},
++								      {group, string}
++								     ]}}}}},
++     #ejabberd_commands{name = push_roster, tags = [roster],
++			desc = "Push template roster from file to a user",
++			module = ?MODULE, function = push_roster,
++			args = [{file, string}, {user, string}, {host, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = push_roster_all, tags = [roster],
++			desc = "Push template roster from file to all those users",
++			module = ?MODULE, function = push_roster_all,
++			args = [{file, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = push_alltoall, tags = [roster],
++			desc = "Add all the users to all the users of Host in Group",
++			module = ?MODULE, function = push_alltoall,
++			args = [{host, string}, {group, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = set_last, tags = [last],
++			desc = "Set last activity information",
++			longdesc = "Timestamp is the seconds since"
++			"1970-01-01 00:00:00 UTC, for example: date +%s",
++			module = ?MODULE, function = set_last,
++			args = [{user, string}, {host, string}, {timestamp, integer}, {status, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = private_get, tags = [private],
++			desc = "Get some information from a user private storage",
++			module = ?MODULE, function = private_get,
++			args = [{user, string}, {host, string}, {element, string}, {ns, string}],
++			result = {res, string}},
++     #ejabberd_commands{name = private_set, tags = [private],
++			desc = "Set to the user private storage",
++			module = ?MODULE, function = private_set,
++			args = [{user, string}, {host, string}, {element, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
++			desc = "Create a Shared Roster Group",
++			longdesc = "If you want to specify several group "
++			"identifiers in the Display argument,\n"
++			"put  \\ \" around the argument and\nseparate the "
++			"identifiers with \\ \\ n\n"
++			"For example:\n"
++			"  ejabberdctl srg_create group3 localhost "
++			"name desc \\\"group1\\\\ngroup2\\\"",
++			module = ?MODULE, function = srg_create,
++			args = [{group, string}, {host, string},
++				{name, string}, {description, string}, {display, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
++			desc = "Delete a Shared Roster Group",
++			module = ?MODULE, function = srg_delete,
++			args = [{group, string}, {host, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
++			desc = "List the Shared Roster Groups in Host",
++			module = ?MODULE, function = srg_list,
++			args = [{host, string}],
++			result = {groups, {list, {id, string}}}},
++     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
++			desc = "Get info of a Shared Roster Group",
++			module = ?MODULE, function = srg_get_info,
++			args = [{group, string}, {host, string}],
++			result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
++     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
++			desc = "Get members of a Shared Roster Group",
++			module = ?MODULE, function = srg_get_members,
++			args = [{group, string}, {host, string}],
++			result = {members, {list, {member, string}}}},
++     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
++			desc = "Add the JID user@host to the Shared Roster Group",
++			module = ?MODULE, function = srg_user_add,
++			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
++			desc = "Delete this JID user@host from the Shared Roster Group",
++			module = ?MODULE, function = srg_user_del,
++			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = send_message_chat, tags = [stanza],
++			desc = "Send a chat message to a local or remote bare of full JID",
++			module = ?MODULE, function = send_message_chat,
++			args = [{from, string}, {to, string}, {body, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = send_message_headline, tags = [stanza],
++			desc = "Send a headline message to a local or remote bare of full JID",
++			module = ?MODULE, function = send_message_headline,
++			args = [{from, string}, {to, string},
++				{subject, string}, {body, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = send_stanza_c2s, tags = [stanza],
++			desc = "Send a stanza as if sent from a c2s session",
++			module = ?MODULE, function = send_stanza_c2s,
++			args = [{user, string}, {host, string}, {resource, string}, {stanza, string}],
++			result = {res, rescode}},
++     #ejabberd_commands{name = privacy_set, tags = [stanza],
++			desc = "Send a IQ set privacy stanza for a local account",
++			module = ?MODULE, function = privacy_set,
++			args = [{user, string}, {host, string}, {xmlquery, string}],
++			result = {res, rescode}},
++
++     #ejabberd_commands{name = stats, tags = [stats],
++			desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds",
++			module = ?MODULE, function = stats,
++			args = [{name, string}],
++			result = {stat, integer}},
++     #ejabberd_commands{name = stats_host, tags = [stats],
++			desc = "Get statistical value for this host: registeredusers onlineusers",
++			module = ?MODULE, function = stats,
++			args = [{name, string}, {host, string}],
++			result = {stat, integer}}
++    ].
++
++
++%%%
++%%% Node
++%%%
++
++compile(File) ->
++    case compile:file(File) of
++	ok -> ok;
++	_ -> error
++    end.
++
++load_config(Path) ->
++    ok = ejabberd_config:load_file(Path).
++
++get_cookie() ->
++    atom_to_list(erlang:get_cookie()).
++
++remove_node(Node) ->
++    mnesia:del_table_copy(schema, list_to_atom(Node)),
++    ok.
++
++export2odbc(Host, Directory) ->
++    Tables = [
++	      {export_last, last},
++	      {export_offline, offline},
++	      {export_passwd, passwd},
++	      {export_private_storage, private_storage},
++	      {export_roster, roster},
++	      {export_vcard, vcard},
++	      {export_vcard_search, vcard_search}],
++    Export = fun({TableFun, Table}) ->
++		     Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
++		     io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
++		     Res = (catch ejd2odbc:TableFun(Host, Filename)),
++		     io:format("  Result: ~p~n", [Res])
++	     end,
++    lists:foreach(Export, Tables),
++    ok.
++
++
++%%%
++%%% Accounts
++%%%
++
++set_password(User, Host, Password) ->
++    case ejabberd_auth:set_password(User, Host, Password) of
++	ok ->
++	    ok;
++	_ ->
++	    error
++    end.
++
++%% Copied some code from ejabberd_commands.erl
++check_password_hash(User, Host, PasswordHash, HashMethod) ->
++    AccountPass = ejabberd_auth:get_password_s(User, Host),
++    AccountPassHash = case HashMethod of
++			  "md5" -> get_md5(AccountPass);
++			  "sha" -> get_sha(AccountPass);
++			  _ -> undefined
++		      end,
++    case AccountPassHash of
++	undefined -> error;
++	PasswordHash -> ok;
++	_ -> error
++    end.
++get_md5(AccountPass) ->
++    lists:flatten([io_lib:format("~.16B", [X])
++		   || X <- binary_to_list(crypto:md5(AccountPass))]).
++get_sha(AccountPass) ->
++    lists:flatten([io_lib:format("~.16B", [X])
++		   || X <- binary_to_list(crypto:sha(AccountPass))]).
++
++num_active_users(Host, Days) ->
++    list_last_activity(Host, true, Days).
++
++%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
++list_last_activity(Host, Integral, Days) ->
++    {MegaSecs, Secs, _MicroSecs} = now(),
++    TimeStamp = MegaSecs * 1000000 + Secs,
++    TS = TimeStamp - Days * 86400,
++    case catch mnesia:dirty_select(
++		 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
++				  [{'>', '$1', TS}],
++				  [{'trunc', {'/',
++					      {'-', TimeStamp, '$1'},
++					      86400}}]}]) of
++							      {'EXIT', _Reason} ->
++		 [];
++	       Vals ->
++		 Hist = histogram(Vals, Integral),
++		 if
++		     Hist == [] ->
++			 0;
++		     true ->
++			 Left = Days - length(Hist),
++			 Tail = if
++				    Integral ->
++					lists:duplicate(Left, lists:last(Hist));
++				    true ->
++					lists:duplicate(Left, 0)
++				end,
++			 lists:nth(Days, Hist ++ Tail)
++		 end
++	 end.
++histogram(Values, Integral) ->
++    histogram(lists:sort(Values), Integral, 0, 0, []).
++histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
++    histogram(T, Integral, Current, Count + 1, Hist);
++histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
++    if
++	Integral ->
++	    histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
++	true ->
++	    histogram(Values, Integral, Current + 1, 0, [Count | Hist])
++    end;
++histogram([], _Integral, _Current, Count, Hist) ->
++    if
++	Count > 0 ->
++	    lists:reverse([Count | Hist]);
++	true ->
++	    lists:reverse(Hist)
++    end.
++
++
++delete_old_users(Days) ->
++    %% Get the list of registered users
++    Users = ejabberd_auth:dirty_get_registered_users(),
++
++    {removed, N, UR} = delete_old_users(Days, Users),
++    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
++
++delete_old_users_vhost(Host, Days) ->
++    %% Get the list of registered users
++    Users = ejabberd_auth:get_vh_registered_users(Host),
++
++    {removed, N, UR} = delete_old_users(Days, Users),
++    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
++
++delete_old_users(Days, Users) ->
++    %% Convert older time
++    SecOlder = Days*24*60*60,
++
++    %% Get current time
++    {MegaSecs, Secs, _MicroSecs} = now(),
++    TimeStamp_now = MegaSecs * 1000000 + Secs,
++
++    %% For a user, remove if required and answer true
++    F = fun({LUser, LServer}) ->
++		%% Check if the user is logged
++		case ejabberd_sm:get_user_resources(LUser, LServer) of
++		    %% If it isnt
++		    [] ->
++			%% Look for his last_activity
++			case (get_lastactivity_module(LServer)):get_last_info(LUser, LServer) of
++			    %% If it is
++			    %% existent:
++			    {ok, TimeStamp, _Status} ->
++				%% get his age
++				Sec = TimeStamp_now - TimeStamp,
++				%% If he is
++				if
++				    %% younger than SecOlder:
++				    Sec < SecOlder ->
++					%% do nothing
++					false;
++				    %% older:
++				    true ->
++					%% remove the user
++					ejabberd_auth:remove_user(LUser, LServer),
++					true
++				end;
++			    %% nonexistent:
++			    not_found ->
++				%% remove the user
++				ejabberd_auth:remove_user(LUser, LServer),
++				true
++			end;
++		    %% Else
++		    _ ->
++			%% do nothing
++			false
++		end
++	end,
++    %% Apply the function to every user in the list
++    Users_removed = lists:filter(F, Users),
++    {removed, length(Users_removed), Users_removed}.
++
++get_lastactivity_module(Server) ->
++    case lists:member(mod_last, gen_mod:loaded_modules(Server)) of
++        true -> mod_last;
++        _ -> mod_last_odbc
++    end.
++
++
++%%
++%% Ban account
++
++ban_account(User, Host, ReasonText) ->
++    Reason = prepare_reason(ReasonText),
++    kick_sessions(User, Host, Reason),
++    set_random_password(User, Host, Reason),
++    ok.
++
++kick_sessions(User, Server, Reason) ->
++    lists:map(
++      fun(Resource) ->
++	      kick_this_session(User, Server, Resource, Reason)
++      end,
++      get_resources(User, Server)).
++
++get_resources(User, Server) ->
++    lists:map(
++      fun(Session) ->
++	      element(3, Session#session.usr)
++      end,
++      get_sessions(User, Server)).
++
++get_sessions(User, Server) ->
++    LUser = jlib:nodeprep(User),
++    LServer = jlib:nameprep(Server),
++    Sessions =  mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
++    true = is_list(Sessions),
++    Sessions.
++
++set_random_password(User, Server, Reason) ->
++    NewPass = build_random_password(Reason),
++    set_password_auth(User, Server, NewPass).
++
++build_random_password(Reason) ->
++    Date = jlib:timestamp_to_iso(calendar:universal_time()),
++    RandomString = randoms:get_string(),
++    "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
++
++set_password_auth(User, Server, Password) ->
++    ok = ejabberd_auth:set_password(User, Server, Password).
++
++prepare_reason([]) ->
++    "Kicked by administrator";
++prepare_reason([Reason]) ->
++    Reason;
++prepare_reason(Reason) when is_list(Reason) ->
++    Reason;
++prepare_reason(StringList) ->
++    string:join(StringList, "_").
++
++
++%%%
++%%% Sessions
++%%%
++
++num_resources(User, Host) ->
++    length(ejabberd_sm:get_user_resources(User, Host)).
++
++resource_num(User, Host, Num) ->
++    Resources = ejabberd_sm:get_user_resources(User, Host),
++    case (0<Num) and (Num=<length(Resources)) of
++	true ->
++	    lists:nth(Num, Resources);
++	false ->
++	    lists:flatten(io_lib:format("Error: Wrong resource number: ~p", [Num]))
++    end.
++
++kick_session(User, Server, Resource, ReasonText) ->
++    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
++    ok.
++
++kick_this_session(User, Server, Resource, Reason) ->
++    ejabberd_router:route(
++      jlib:make_jid("", "", ""),
++      jlib:make_jid(User, Server, Resource),
++      {xmlelement, "broadcast", [], [{exit, Reason}]}).
++
++
++status_num(Host, Status) ->
++    length(get_status_list(Host, Status)).
++status_num(Status) ->
++    status_num("all", Status).
++status_list(Host, Status) ->
++    Res = get_status_list(Host, Status),
++    [{U, S, R, P, St} || {U, S, R, P, St} <- Res].
++status_list(Status) ->
++    status_list("all", Status).
++
++
++get_status_list(Host, Status_required) ->
++    %% Get list of all logged users
++    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
++    %% Reformat the list
++    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
++    Fhost = case Host of
++		"all" ->
++		    %% All hosts are requested, so dont filter at all
++		    fun(_, _) -> true end;
++		_ ->
++		    %% Filter the list, only Host is interesting
++		    fun(A, B) -> A == B end
++	    end,
++    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
++    %% For each Pid, get its presence
++    Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
++    %% Filter by status
++    Fstatus = case Status_required of
++		  "all" ->
++		      fun(_, _) -> true end;
++		  _ ->
++		      fun(A, B) -> A == B end
++	      end,
++    [{User, Server, Resource, Priority, stringize(Status_text)}
++     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
++	apply(Fstatus, [Status, Status_required])].
++
++connected_users_info() ->
++    USRIs = dirty_get_sessions_list2(),
++    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
++    lists:map(
++      fun([{U, S, R}, {Now, Pid}, Priority, Info]) ->
++	      Conn = proplists:get_value(conn, Info),
++	      {Ip, Port} = proplists:get_value(ip, Info),
++	      IPS = inet_parse:ntoa(Ip),
++	      NodeS = atom_to_list(node(Pid)),
++	      Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
++				      calendar:now_to_local_time(Now)),
++	      {[U, $@, S, $/, R], atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime}
++      end,
++      USRIs).
++
++connected_users_vhost(Host) ->
++    USRs = ejabberd_sm:get_vh_session_list(Host),
++    [ [U, $@, S, $/, R] || {U, S, R} <- USRs].
++
++%% Code copied from ejabberd_sm.erl and customized
++dirty_get_sessions_list2() ->
++    mnesia:dirty_select(
++      session,
++      [{#session{usr = '$1', sid = '$2', priority = '$3', info = '$4', _ = '_'},
++	[],
++	[['$1', '$2', '$3', '$4']]}]).
++
++%% Make string more print-friendly
++stringize(String) ->
++    %% Replace newline characters with other code
++    ejabberd_regexp:greplace(String, "\n", "\\n").
++
++set_presence(User, Host, Resource, Type, Show, Status, Priority) ->
++    Pid = ejabberd_sm:get_session_pid(User, Host, Resource),
++    USR = User ++ "@" ++ Host ++ "/" ++ Resource,
++    US = User ++ "@" ++ Host,
++    Message = {route_xmlstreamelement,
++	       {xmlelement, "presence",
++		[{"from", USR}, {"to", US}, {"type", Type}],
++		[{xmlelement, "show", [], [{xmlcdata, Show}]},
++		 {xmlelement, "status", [], [{xmlcdata, Status}]},
++		 {xmlelement, "priority", [], [{xmlcdata, Priority}]}]}},
++    Pid ! Message.
++
++user_sessions_info(User, Host) ->
++    CurrentSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
++    US = {User, Host},
++    Sessions = case catch mnesia:dirty_index_read(session, US, #session.us) of
++		   {'EXIT', _Reason} ->
++		       [];
++		   Ss ->
++		       Ss
++	       end,
++    lists:map(
++      fun(Session) ->
++	      {_U, _S, Resource} = Session#session.usr,
++	      {Now, Pid} = Session#session.sid,
++	      {_U, _Resource, Status, StatusText} = ejabberd_c2s:get_presence(Pid),
++	      Info = Session#session.info,
++	      Priority = Session#session.priority,
++	      Conn = proplists:get_value(conn, Info),
++	      {Ip, Port} = proplists:get_value(ip, Info),
++	      IPS = inet_parse:ntoa(Ip),
++	      NodeS = atom_to_list(node(Pid)),
++	      Uptime = CurrentSec - calendar:datetime_to_gregorian_seconds(
++				      calendar:now_to_local_time(Now)),
++	      {atom_to_list(Conn), IPS, Port, Priority, NodeS, Uptime, Status, Resource, StatusText}
++      end,
++      Sessions).
++
++
++%%%
++%%% Vcard
++%%%
++
++set_nickname(User, Host, Nickname) ->
++    R = mod_vcard:process_sm_iq(
++	  {jid, User, Host, "", User, Host, ""},
++	  {jid, User, Host, "", User, Host, ""},
++	  {iq, "", set, "", "en",
++	   {xmlelement, "vCard",
++	    [{"xmlns", "vcard-temp"}], [
++					{xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]}
++				       ]
++	   }}),
++    case R of
++	{iq, [], result, [], _L, []} ->
++	    ok;
++	_ ->
++	    error
++    end.
++
++get_vcard(User, Host, Name) ->
++    [Res | _] = get_vcard_content(User, Host, [Name]),
++    Res.
++
++get_vcard(User, Host, Name, Subname) ->
++    [Res | _] = get_vcard_content(User, Host, [Name, Subname]),
++    Res.
++
++get_vcard_multi(User, Host, Name, Subname) ->
++    get_vcard_content(User, Host, [Name, Subname]).
++
++set_vcard(User, Host, Name, SomeContent) ->
++    set_vcard_content(User, Host, [Name], SomeContent).
++
++set_vcard(User, Host, Name, Subname, SomeContent) ->
++    set_vcard_content(User, Host, [Name, Subname], SomeContent).
++
++
++%%
++%% Internal vcard
++
++get_module_resource(Server) ->
++    case gen_mod:get_module_opt(Server, ?MODULE, module_resource, none) of
++	none -> atom_to_list(?MODULE);
++	R when is_list(R) -> R
++    end.
++
++get_vcard_content(User, Server, Data) ->
++    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
++    JID = jlib:make_jid(User, Server, get_module_resource(Server)),
++    IQ = #iq{type = get, xmlns = ?NS_VCARD},
++    IQr = Module:Function(JID, JID, IQ),
++    case IQr#iq.sub_el of
++	[A1] ->
++	    case get_vcard(Data, A1) of
++		[] -> throw(error_no_value_found_in_vcard);
++		ElemList -> [xml:get_tag_cdata(Elem) || Elem <- ElemList]
++	    end;
++	[] ->
++	    throw(error_no_vcard_found)
++    end.
++
++get_vcard([Data1, Data2], A1) ->
++    case get_subtag(A1, Data1) of
++    	false -> false;
++	A2List -> lists:flatten([get_vcard([Data2], A2) || A2 <- A2List])
++    end;
++
++get_vcard([Data], A1) ->
++    get_subtag(A1, Data).
++
++get_subtag(Xmlelement, Name) ->
++    case code:ensure_loaded(exmpp_xml) of
++	{error, _} ->
++	    [get_subtag_xml(Xmlelement, Name)];
++	{module, exmpp_xml} ->
++	    get_subtag_exmpp(Xmlelement, Name)
++    end.
++
++get_subtag_xml(Xmlelement, Name) ->
++    xml:get_subtag(Xmlelement, Name).
++
++get_subtag_exmpp(Xmlelement, Name) ->
++    Xmlel = exmpp_xml:xmlelement_to_xmlel(Xmlelement),
++    XmlelList = exmpp_xml:get_elements(Xmlel, Name),
++    [exmpp_xml:xmlel_to_xmlelement(Xmlel2) || Xmlel2 <- XmlelList].
++
++set_vcard_content(User, Server, Data, SomeContent) ->
++    ContentList = case SomeContent of
++	[Char | _] when not is_list(Char) -> [SomeContent];
++	[Char | _] when is_list(Char) -> SomeContent
++    end,
++    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
++    JID = jlib:make_jid(User, Server, get_module_resource(Server)),
++    IQ = #iq{type = get, xmlns = ?NS_VCARD},
++    IQr = Module:Function(JID, JID, IQ),
++
++    %% Get old vcard
++    A4 = case IQr#iq.sub_el of
++	     [A1] ->
++		 {_, _, _, A2} = A1,
++		 update_vcard_els(Data, ContentList, A2);
++	     [] ->
++		 update_vcard_els(Data, ContentList, [])
++	 end,
++
++    %% Build new vcard
++    SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
++    IQ2 = #iq{type=set, sub_el = SubEl},
++
++    Module:Function(JID, JID, IQ2),
++    ok.
++
++update_vcard_els(Data, ContentList, Els1) ->
++    Els2 = lists:keysort(2, Els1),
++    [Data1 | Data2] = Data,
++    NewEls = case Data2 of
++		[] ->
++		    [{xmlelement, Data1, [], [{xmlcdata,Content}]} || Content <- ContentList];
++		[D2] ->
++		    OldEl = case lists:keysearch(Data1, 2, Els2) of
++				{value, A} -> A;
++				false -> {xmlelement, Data1, [], []}
++			    end,
++		    {xmlelement, _, _, ContentOld1} = OldEl,
++		    Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]} || Content <- ContentList],
++		    ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2],
++		    ContentOld3 = lists:keysort(2, ContentOld2),
++		    ContentNew = lists:keymerge(2, Content2, ContentOld3),
++		    [{xmlelement, Data1, [], ContentNew}]
++	    end,
++    Els3 = lists:keydelete(Data1, 2, Els2),
++    lists:keymerge(2, NewEls, Els3).
++
++
++%%%
++%%% Roster
++%%%
++
++add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
++    case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of
++	{atomic, ok} ->
++	    push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}),
++	    ok;
++	_ ->
++	    error
++    end.
++
++add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
++    subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs).
++
++subscribe(LU, LS, User, Server, Nick, Group, Subscription, _Xattrs) ->
++    SubscriptionS = case is_atom(Subscription) of
++	true -> atom_to_list(Subscription);
++	false -> Subscription
++    end,
++    ItemEl = build_roster_item(User, Server, {add, Nick, SubscriptionS, Group}),
++    {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]),
++    M:set_items(
++	LU, LS,
++	{xmlelement,"query",
++            [{"xmlns","jabber:iq:roster"}],
++            [ItemEl]}).
++
++delete_rosteritem(LocalUser, LocalServer, User, Server) ->
++    case unsubscribe(LocalUser, LocalServer, User, Server) of
++	{atomic, ok} ->
++	    push_roster_item(LocalUser, LocalServer, User, Server, remove),
++	    ok;
++	_  ->
++	    error
++    end.
++
++unsubscribe(LU, LS, User, Server) ->
++    ItemEl = build_roster_item(User, Server, remove),
++    {ok, M} = loaded_module(LS,[mod_roster_odbc,mod_roster]),
++    M:set_items(
++	LU, LS,
++	{xmlelement,"query",
++            [{"xmlns","jabber:iq:roster"}],
++            [ItemEl]}).
++
++loaded_module(Domain,Options) ->
++    LoadedModules = gen_mod:loaded_modules(Domain),
++    case lists:filter(fun(Module) ->
++                              lists:member(Module, LoadedModules)
++                      end, Options) of
++        [M|_] -> {ok, M};
++        [] -> {error,not_found}
++    end.
++
++%% -----------------------------
++%% Get Roster
++%% -----------------------------
++
++get_roster(User, Server) ->
++    Items = ejabberd_hooks:run_fold(roster_get, Server, [], [{User, Server}]),
++    make_roster_xmlrpc(Items).
++
++%% Note: if a contact is in several groups, the contact is returned
++%% several times, each one in a different group.
++make_roster_xmlrpc(Roster) ->
++    lists:foldl(
++      fun(Item, Res) ->
++	      JIDS = jlib:jid_to_string(Item#roster.jid),
++	      Nick = Item#roster.name,
++	      Subs = atom_to_list(Item#roster.subscription),
++	      Ask = atom_to_list(Item#roster.ask),
++	      Groups = case Item#roster.groups of
++			   [] -> [""];
++			   Gs -> Gs
++		       end,
++	      ItemsX = [{JIDS, Nick, Subs, Ask, Group}
++			|| Group <- Groups],
++	      ItemsX ++ Res
++      end,
++      [],
++      Roster).
++
++
++%%-----------------------------
++%% Push Roster from file
++%%-----------------------------
++
++push_roster(File, User, Server) ->
++    {ok, [Roster]} = file:consult(File),
++    subscribe_roster({User, Server, "", User}, Roster).
++
++push_roster_all(File) ->
++    {ok, [Roster]} = file:consult(File),
++    subscribe_all(Roster).
++
++subscribe_all(Roster) ->
++    subscribe_all(Roster, Roster).
++subscribe_all([], _) ->
++    ok;
++subscribe_all([User1 | Users], Roster) ->
++    subscribe_roster(User1, Roster),
++    subscribe_all(Users, Roster).
++
++subscribe_roster(_, []) ->
++    ok;
++%% Do not subscribe a user to itself
++subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
++    subscribe_roster({Name, Server, Group, Nick}, Roster);
++%% Subscribe Name2 to Name1
++subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
++    subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
++    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
++
++push_alltoall(S, G) ->
++    Users = ejabberd_auth:get_vh_registered_users(S),
++    Users2 = build_list_users(G, Users, []),
++    subscribe_all(Users2),
++    ok.
++
++build_list_users(_Group, [], Res) ->
++    Res;
++build_list_users(Group, [{User, Server}|Users], Res) ->
++    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
++
++%% @spec(LU, LS, U, S, Action) -> ok
++%%       Action = {add, Nick, Subs, Group} | remove
++%% @doc Push to the roster of account LU@LS the contact U@S.
++%% The specific action to perform is defined in Action.
++push_roster_item(LU, LS, U, S, Action) ->
++    lists:foreach(fun(R) ->
++			  push_roster_item(LU, LS, R, U, S, Action)
++		  end, ejabberd_sm:get_user_resources(LU, LS)).
++
++push_roster_item(LU, LS, R, U, S, Action) ->
++    LJID = jlib:make_jid(LU, LS, R),
++    BroadcastEl = build_broadcast(U, S, Action),
++    ejabberd_router:route(LJID, LJID, BroadcastEl),
++    Item = build_roster_item(U, S, Action),
++    ResIQ = build_iq_roster_push(Item),
++    ejabberd_router:route(LJID, LJID, ResIQ).
++
++build_roster_item(U, S, {add, Nick, Subs, Group}) ->
++    {xmlelement, "item",
++     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
++      {"name", Nick},
++      {"subscription", Subs}],
++     [{xmlelement, "group", [], [{xmlcdata, Group}]}]
++    };
++build_roster_item(U, S, remove) ->
++    {xmlelement, "item",
++     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
++      {"subscription", "remove"}],
++     []
++    }.
++
++build_iq_roster_push(Item) ->
++    {xmlelement, "iq",
++     [{"type", "set"}, {"id", "push"}],
++     [{xmlelement, "query",
++       [{"xmlns", ?NS_ROSTER}],
++       [Item]
++      }
++     ]
++    }.
++
++build_broadcast(U, S, {add, _Nick, Subs, _Group}) ->
++    build_broadcast(U, S, list_to_atom(Subs));
++build_broadcast(U, S, remove) ->
++    build_broadcast(U, S, none);
++%% @spec (U::string(), S::string(), Subs::atom()) -> any()
++%% Subs = both | from | to | none
++build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) ->
++    {xmlelement, "broadcast", [],
++     [{item, {U, S, ""}, SubsAtom}]
++    }.
++
++%%%
++%%% Last Activity
++%%%
++
++set_last(User, Server, Timestamp, Status) ->
++    Mod = get_lastactivity_module(Server),
++    Mod:store_last_info(User, Server, Timestamp, Status).
++
++%%%
++%%% Private Storage
++%%%
++
++%% Example usage:
++%% $ ejabberdctl private_set badlop localhost "\<aa\ xmlns=\'bb\'\>Cluth\</aa\>"
++%% $ ejabberdctl private_get badlop localhost aa bb
++%% <aa xmlns='bb'>Cluth</aa>
++
++private_get(Username, Host, Element, Ns) ->
++    From = jlib:make_jid(Username, Host, ""),
++    To = jlib:make_jid(Username, Host, ""),
++    IQ = {iq, "", get, ?NS_PRIVATE, "",
++	  {xmlelement,"query",
++	   [{"xmlns",?NS_PRIVATE}],
++	   [{xmlelement, Element, [{"xmlns", Ns}], []}]}},
++    ResIq = mod_private:process_sm_iq(From, To, IQ),
++    [{xmlelement,"query",
++      [{"xmlns","jabber:iq:private"}],
++      [SubEl]}] = ResIq#iq.sub_el,
++    xml:element_to_string(SubEl).
++
++private_set(Username, Host, ElementString) ->
++    case xml_stream:parse_element(ElementString) of
++	{error, Error} ->
++	    io:format("Error found parsing the element:~n  ~p~nError: ~p~n",
++		      [ElementString, Error]),
++	    error;
++	Xml ->
++	    private_set2(Username, Host, Xml)
++    end.
++
++private_set2(Username, Host, Xml) ->
++    From = jlib:make_jid(Username, Host, ""),
++    To = jlib:make_jid(Username, Host, ""),
++    IQ = {iq, "", set, ?NS_PRIVATE, "",
++	  {xmlelement,"query",
++	   [{"xmlns",?NS_PRIVATE}],
++	   [Xml]}},
++    mod_private:process_sm_iq(From, To, IQ),
++    ok.
++
++%%%
++%%% Shared Roster Groups
++%%%
++
++srg_create(Group, Host, Name, Description, Display) ->
++    DisplayList = case Display of
++	[] -> [];
++	_ -> ejabberd_regexp:split(Display, "\\\\n")
++    end,
++    Opts = [{name, Name},
++	    {displayed_groups, DisplayList},
++	    {description, Description}],
++    {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
++    ok.
++
++srg_delete(Group, Host) ->
++    {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
++    ok.
++
++srg_list(Host) ->
++    lists:sort(mod_shared_roster:list_groups(Host)).
++
++srg_get_info(Group, Host) ->
++    Opts = mod_shared_roster:get_group_opts(Host,Group),
++    [{io_lib:format("~p", [Title]),
++      io_lib:format("~p", [Value])} || {Title, Value} <- Opts].
++
++srg_get_members(Group, Host) ->
++    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
++    [jlib:jid_to_string(jlib:make_jid(MUser, MServer, ""))
++     || {MUser, MServer} <- Members].
++
++srg_user_add(User, Host, Group, GroupHost) ->
++    {atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
++    ok.
++
++srg_user_del(User, Host, Group, GroupHost) ->
++    {atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
++    ok.
++
++
++%%%
++%%% Stanza
++%%%
++
++%% @doc Send a chat message to a Jabber account.
++%% @spec (From::string(), To::string(), Body::string()) -> ok
++send_message_chat(From, To, Body) ->
++    Packet = build_packet(message_chat, [Body]),
++    send_packet_all_resources(From, To, Packet).
++
++%% @doc Send a headline message to a Jabber account.
++%% @spec (From::string(), To::string(), Subject::string(), Body::string()) -> ok
++send_message_headline(From, To, Subject, Body) ->
++    Packet = build_packet(message_headline, [Subject, Body]),
++    send_packet_all_resources(From, To, Packet).
++
++%% @doc Send a packet to a Jabber account.
++%% If a resource was specified in the JID,
++%% the packet is sent only to that specific resource.
++%% If no resource was specified in the JID,
++%% and the user is remote or local but offline,
++%% the packet is sent to the bare JID.
++%% If the user is local and is online in several resources,
++%% the packet is sent to all its resources.
++send_packet_all_resources(FromJIDString, ToJIDString, Packet) ->
++    FromJID = jlib:string_to_jid(FromJIDString),
++    ToJID = jlib:string_to_jid(ToJIDString),
++    ToUser = ToJID#jid.user,
++    ToServer = ToJID#jid.server,
++    case ToJID#jid.resource of
++	"" ->
++	    send_packet_all_resources(FromJID, ToUser, ToServer, Packet);
++	Res ->
++	    send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet)
++    end.
++
++send_packet_all_resources(FromJID, ToUser, ToServer, Packet) ->
++    case ejabberd_sm:get_user_resources(ToUser, ToServer) of
++	[] ->
++	    send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet);
++	ToResources ->
++	    lists:foreach(
++	      fun(ToResource) ->
++		      send_packet_all_resources(FromJID, ToUser, ToServer,
++						ToResource, Packet)
++	      end,
++	      ToResources)
++    end.
++
++send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) ->
++    ToJID = jlib:make_jid(ToU, ToS, ToR),
++    ejabberd_router:route(FromJID, ToJID, Packet).
++
++
++build_packet(message_chat, [Body]) ->
++    {xmlelement, "message",
++     [{"type", "chat"}, {"id", randoms:get_string()}],
++     [{xmlelement, "body", [], [{xmlcdata, Body}]}]
++    };
++build_packet(message_headline, [Subject, Body]) ->
++    {xmlelement, "message",
++     [{"type", "headline"}, {"id", randoms:get_string()}],
++     [{xmlelement, "subject", [], [{xmlcdata, Subject}]},
++      {xmlelement, "body", [], [{xmlcdata, Body}]}
++     ]
++    }.
++
++send_stanza_c2s(Username, Host, Resource, Stanza) ->
++    C2sPid = ejabberd_sm:get_session_pid(Username, Host, Resource),
++    XmlEl = xml_stream:parse_element(Stanza),
++    p1_fsm:send_event(C2sPid, {xmlstreamelement, XmlEl}).
++
++privacy_set(Username, Host, QueryS) ->
++    From = jlib:string_to_jid(Username ++ "@" ++ Host),
++    To = jlib:string_to_jid(Host),
++    QueryEl = xml_stream:parse_element(QueryS),
++    StanzaEl = {xmlelement, "iq", [{"type", "set"}], [QueryEl]},
++    IQ = jlib:iq_query_info(StanzaEl),
++    ejabberd_hooks:run_fold(
++		     privacy_iq_set,
++		     Host,
++		     {error, ?ERR_FEATURE_NOT_IMPLEMENTED},
++		     [From, To, IQ]
++		    ),
++    ok.
++
++%%%
++%%% Stats
++%%%
++
++stats(Name) ->
++    case Name of
++	"uptimeseconds" -> trunc(element(1, erlang:statistics(wall_clock))/1000);
++	"registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
++	"onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
++	"onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
++    end.
++
++stats(Name, Host) ->
++    case Name of
++	"registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
++	"onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
++    end.
++
++
++
++%%-----------------------------
++%% Purge roster items
++%%-----------------------------
++
++process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
++    Action = case ActionS of
++		 "list" -> list;
++		 "delete" -> delete
++	     end,
++
++    Subs = lists:foldl(
++	     fun(any, _) -> [none, from, to, both];
++		(Sub, Subs) -> [Sub | Subs]
++	     end,
++	     [],
++	     [list_to_atom(S) || S <- string:tokens(SubsS, ":")]
++	    ),
++
++    Asks = lists:foldl(
++	     fun(any, _) -> [none, out, in];
++		(Ask, Asks) -> [Ask | Asks]
++	     end,
++	     [],
++	     [list_to_atom(S) || S <- string:tokens(AsksS, ":")]
++	    ),
++
++    Users = lists:foldl(
++	      fun("any", _) -> ["*", "*@*"];
++		 (U, Us) -> [U | Us]
++	      end,
++	      [],
++	      [S || S <- string:tokens(UsersS, ":")]
++	     ),
++
++    Contacts = lists:foldl(
++		 fun("any", _) -> ["*", "*@*"];
++		    (U, Us) -> [U | Us]
++		 end,
++		 [],
++		 [S || S <- string:tokens(ContactsS, ":")]
++		),
++
++    case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of
++	{atomic, ok} ->
++	    ok;
++	{error, Reason} ->
++	    io:format("Error purging rosteritems: ~p~n", [Reason]),
++	    error;
++	{badrpc, Reason} ->
++	    io:format("BadRPC purging rosteritems: ~p~n", [Reason]),
++	    error
++    end.
++
++%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok}
++rosteritem_purge(Options) ->
++    Num_rosteritems = mnesia:table_info(roster, size),
++    io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
++    Key = mnesia:dirty_first(roster),
++    ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
++    {atomic, ok}.
++
++rip('$end_of_table', _Options, Counters) ->
++    print_progress_line(Counters),
++    ok;
++rip(Key, Options, {Pr, NT, NV, ND}) ->
++    Key_next = mnesia:dirty_next(roster, Key),
++    {Action, _, _, _, _} = Options,
++    ND2 = case decide_rip(Key, Options) of
++	      true ->
++		  apply_action(Action, Key),
++		  ND+1;
++	      false ->
++		  ND
++	  end,
++    NV2 = NV+1,
++    Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
++    rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
++
++apply_action(list, Key) ->
++    {User, Server, JID} = Key,
++    {RUser, RServer, _} = JID,
++    io:format("Matches: ~s@~s ~s@~s~n", [User, Server, RUser, RServer]);
++apply_action(delete, Key) ->
++    apply_action(list, Key),
++    mnesia:dirty_delete(roster, Key).
++
++print_progress_line({Pr, NT, NV, ND}) ->
++    Pr2 = trunc((NV/NT)*100),
++    case Pr == Pr2 of
++	true ->
++	    ok;
++	false ->
++	    io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
++    end,
++    Pr2.
++
++decide_rip(Key, {_Action, Subs, Asks, User, Contact}) ->
++    case catch mnesia:dirty_read(roster, Key) of
++	[RI] ->
++	    lists:member(RI#roster.subscription, Subs)
++		andalso lists:member(RI#roster.ask, Asks)
++		andalso decide_rip_jid(RI#roster.us, User)
++		andalso decide_rip_jid(RI#roster.jid, Contact);
++	_ ->
++	    false
++    end.
++
++%% Returns true if the server of the JID is included in the servers
++decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
++    decide_rip_jid({UName, UServer}, Match_list);
++decide_rip_jid({UName, UServer}, Match_list) ->
++    lists:any(
++      fun(Match_string) ->
++	      MJID = jlib:string_to_jid(Match_string),
++	      MName = MJID#jid.luser,
++	      MServer = MJID#jid.lserver,
++	      Is_server = is_glob_match(UServer, MServer),
++	      case MName of
++		  [] when UName == [] ->
++		      Is_server;
++		  [] ->
++		      false;
++		  _ ->
++		      Is_server
++			  andalso is_glob_match(UName, MName)
++	      end
++      end,
++      Match_list).
++
++%% Copied from ejabberd-2.0.0/src/acl.erl
++is_regexp_match(String, RegExp) ->
++    case ejabberd_regexp:run(String, RegExp) of
++	nomatch ->
++	    false;
++	match ->
++	    true;
++	{error, ErrDesc} ->
++	    io:format(
++	      "Wrong regexp ~p in ACL: ~p",
++	      [RegExp, ErrDesc]),
++	    false
++    end.
++is_glob_match(String, [$! | Glob]) ->
++    not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob));
++is_glob_match(String, Glob) ->
++    is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch
new file mode 100644
index 0000000..71b6f0c
--- /dev/null
+++ b/ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch
@@ -0,0 +1,48 @@
+From 2c01b391dcfdc3cb34f5984a10a1bba3ca341c02 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Tue, 16 Feb 2010 16:30:05 +0300
+Subject: [PATCH 4/8] Fedora-specific changes to ejabberdctl
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberdctl.template |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
+index 0960f9a..c22478a 100644
+--- a/src/ejabberdctl.template
++++ b/src/ejabberdctl.template
+@@ -9,10 +9,10 @@ ERL_MAX_ETS_TABLES=1400
+ 
+ # define default environment variables
+ NODE=ejabberd
+-HOST=localhost
++HOST=`hostname -s 2>/dev/null || echo "localhost"`
+ ERLANG_NODE=$NODE@$HOST
+ ERL=@erl@
+-INSTALLUSER=@installuser@
++INSTALLUSER=ejabberd
+ 
+ # parse command line parameters
+ ARGS=
+@@ -46,7 +46,7 @@ if [ "$LOGS_DIR" = "" ] ; then
+     LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
+ fi
+ if [ "$SPOOLDIR" = "" ] ; then
+-    SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
++    SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd/spool
+ fi
+ if [ "$EJABBERD_DOC_PATH" = "" ] ; then
+     EJABBERD_DOC_PATH=@DOCDIR@
+@@ -62,7 +62,7 @@ EJID=`id -g $INSTALLUSER`
+ EXEC_CMD="false"
+ for GID in $GIDS; do
+     if [ $GID -eq 0 ] ; then
+-	EXEC_CMD="su ${INSTALLUSER} -p -c"
++	EXEC_CMD="/sbin/runuser -s /bin/bash -p ejabberd -c"
+     fi
+ done
+ if [ "$ID" -eq "$EJID" ] ; then
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0004-Introducing-mod_admin_extra.patch b/ejabberd-0004-Introducing-mod_admin_extra.patch
deleted file mode 100644
index bdc6eea..0000000
--- a/ejabberd-0004-Introducing-mod_admin_extra.patch
+++ /dev/null
@@ -1,1318 +0,0 @@
-From 35170f86c033624284a8ab85346a6fc0bfc8ed43 Mon Sep 17 00:00:00 2001
-From: Badlop <badlop@process-one.net>
-Date: Tue, 16 Feb 2010 16:12:17 +0300
-Subject: [PATCH 04/10] Introducing mod_admin_extra
-
-Adds the mod_admin_extra module to ejabberd.
-This module extends the functionality provided by ejabberdctl
-by adding several new commands.
-
-The code is taken from the ProcessOne repository:
-https://forge.process-one.net/browse/ejabberd-modules/mod_admin_extra
-(trunk, revision 976).
----
- src/ejabberd.app        |    1 +
- src/mod_admin_extra.erl | 1279 +++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 1280 insertions(+)
- create mode 100644 src/mod_admin_extra.erl
-
-diff --git a/src/ejabberd.app b/src/ejabberd.app
-index 067b928..81edc09 100644
---- a/src/ejabberd.app
-+++ b/src/ejabberd.app
-@@ -106,6 +106,7 @@
- 	     mod_vcard_ldap,
- 	     mod_vcard_odbc,
- 	     mod_version,
-+             mod_admin_extra,
- 	     node_buddy,
- 	     node_club,
- 	     node_default,
-diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
-new file mode 100644
-index 0000000..9f3ca14
---- /dev/null
-+++ b/src/mod_admin_extra.erl
-@@ -0,0 +1,1279 @@
-+%%%-------------------------------------------------------------------
-+%%% File    : mod_admin_extra.erl
-+%%% Author  : Badlop <badlop@process-one.net>
-+%%% Purpose : Contributed administrative functions and commands
-+%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
-+%%%
-+%%%
-+%%% ejabberd, Copyright (C) 2002-2008   ProcessOne
-+%%%
-+%%% This program is free software; you can redistribute it and/or
-+%%% modify it under the terms of the GNU General Public License as
-+%%% published by the Free Software Foundation; either version 2 of the
-+%%% License, or (at your option) any later version.
-+%%%
-+%%% This program is distributed in the hope that it will be useful,
-+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-+%%% General Public License for more details.
-+%%%
-+%%% You should have received a copy of the GNU General Public License
-+%%% along with this program; if not, write to the Free Software
-+%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-+%%% 02111-1307 USA
-+%%%
-+%%%-------------------------------------------------------------------
-+
-+-module(mod_admin_extra).
-+-author('badlop@process-one.net').
-+
-+-behaviour(gen_mod).
-+
-+-export([start/2, stop/1,
-+	 %% Node
-+	 compile/1,
-+	 load_config/1,
-+	 get_cookie/0,
-+	 remove_node/1,
-+	 export2odbc/2,
-+	 %% Accounts
-+	 set_password/3,
-+	 check_password_hash/4,
-+	 delete_old_users/1,
-+	 delete_old_users_vhost/2,
-+	 ban_account/3,
-+	 num_active_users/2,
-+	 %% Sessions
-+	 num_resources/2,
-+	 resource_num/3,
-+	 kick_session/4,
-+	 status_num/2, status_num/1,
-+	 status_list/2, status_list/1,
-+	 %% Vcard
-+	 set_nickname/3,
-+	 get_vcard/3,
-+	 get_vcard/4,
-+	 set_vcard/4,
-+	 set_vcard/5,
-+	 %% Roster
-+	 add_rosteritem/7,
-+	 delete_rosteritem/4,
-+	 process_rosteritems/5,
-+	 get_roster/2,
-+	 push_roster/3,
-+	 push_roster_all/1,
-+	 push_alltoall/2,
-+	 %% mod_shared_roster
-+	 srg_create/5,
-+	 srg_delete/2,
-+	 srg_list/1,
-+	 srg_get_info/2,
-+	 srg_get_members/2,
-+	 srg_user_add/4,
-+	 srg_user_del/4,
-+	 %% Stanza
-+	 send_message_headline/4,
-+	 send_message_chat/3,
-+	 %% Stats
-+	 stats/1, stats/2
-+	]).
-+
-+-include("ejabberd.hrl").
-+-include("ejabberd_commands.hrl").
-+-include("mod_roster.hrl").
-+-include("jlib.hrl").
-+
-+%% Copied from ejabberd_sm.erl
-+-record(session, {sid, usr, us, priority, info}).
-+
-+
-+%%%
-+%%% gen_mod
-+%%%
-+
-+start(_Host, _Opts) ->
-+    ejabberd_commands:register_commands(commands()).
-+
-+stop(_Host) ->
-+    ejabberd_commands:unregister_commands(commands()).
-+
-+
-+%%%
-+%%% Register commands
-+%%%
-+
-+commands() ->
-+    Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n"
-+	" FN		- Full Name\n"
-+	" NICKNAME	- Nickname\n"
-+	" BDAY		- Birthday\n"
-+	" TITLE		- Work: Position\n",
-+    " ROLE		- Work: Role",
-+
-+    Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n"
-+	" N FAMILY	- Family name\n"
-+	" N GIVEN	- Given name\n"
-+	" N MIDDLE	- Middle name\n"
-+	" ADR CTRY	- Address: Country\n"
-+	" ADR LOCALITY	- Address: City\n"
-+	" EMAIL USERID	- E-Mail Address\n"
-+	" ORG ORGNAME	- Work: Company\n"
-+	" ORG ORGUNIT	- Work: Department",
-+
-+    VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at "
-+	"http://www.xmpp.org/extensions/xep-0054.html",
-+
-+    [
-+     #ejabberd_commands{name = compile, tags = [erlang],
-+			desc = "Recompile and reload Erlang source code file",
-+			module = ?MODULE, function = compile,
-+			args = [{file, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = load_config, tags = [server],
-+			desc = "Load ejabberd configuration file",
-+			module = ?MODULE, function = load_config,
-+			args = [{file, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = get_cookie, tags = [erlang],
-+			desc = "Get the Erlang cookie of this node",
-+			module = ?MODULE, function = get_cookie,
-+			args = [],
-+			result = {cookie, string}},
-+     #ejabberd_commands{name = remove_node, tags = [erlang],
-+			desc = "Remove an ejabberd node from Mnesia clustering config",
-+			module = ?MODULE, function = remove_node,
-+			args = [{node, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = export2odbc, tags = [mnesia],
-+			desc = "Export Mnesia tables to files in directory",
-+			module = ?MODULE, function = export2odbc,
-+			args = [{host, string}, {path, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = num_active_users, tags = [accounts, stats],
-+			desc = "Get number of users active in the last days",
-+			module = ?MODULE, function = num_active_users,
-+			args = [{host, string}, {days, integer}],
-+			result = {users, integer}},
-+     #ejabberd_commands{name = delete_old_users, tags = [accounts, purge],
-+			desc = "Delete users that didn't log in last days",
-+			module = ?MODULE, function = delete_old_users,
-+			args = [{days, integer}],
-+			result = {res, restuple}},
-+     #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge],
-+			desc = "Delete users that didn't log in last days in vhost",
-+			module = ?MODULE, function = delete_old_users_vhost,
-+			args = [{host, string}, {days, integer}],
-+			result = {res, restuple}},
-+
-+     #ejabberd_commands{name = check_account, tags = [accounts],
-+			desc = "Check if an acount exists or not",
-+			module = ejabberd_auth, function = is_user_exists,
-+			args = [{user, string}, {host, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = check_password, tags = [accounts],
-+			desc = "Check if a password is correct",
-+			module = ejabberd_auth, function = check_password,
-+			args = [{user, string}, {host, string}, {password, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = check_password_hash, tags = [accounts],
-+			desc = "Check if the password hash is correct",
-+			longdesc = "Allowed hash methods: md5, sha.",
-+			module = ?MODULE, function = check_password_hash,
-+			args = [{user, string}, {host, string}, {passwordhash, string}, {hashmethod, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = change_password, tags = [accounts],
-+			desc = "Change the password of an account",
-+			module = ?MODULE, function = set_password,
-+			args = [{user, string}, {host, string}, {newpass, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = ban_account, tags = [accounts],
-+			desc = "Ban an account: kick sessions and set random password",
-+			module = ?MODULE, function = ban_account,
-+			args = [{user, string}, {host, string}, {reason, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = num_resources, tags = [session],
-+			desc = "Get the number of resources of a user",
-+			module = ?MODULE, function = num_resources,
-+			args = [{user, string}, {host, string}],
-+			result = {resources, integer}},
-+     #ejabberd_commands{name = resource_num, tags = [session],
-+			desc = "Resource string of a session number",
-+			module = ?MODULE, function = resource_num,
-+			args = [{user, string}, {host, string}, {num, integer}],
-+			result = {resource, string}},
-+     #ejabberd_commands{name = kick_session, tags = [session],
-+			desc = "Kick a user session",
-+			module = ?MODULE, function = kick_session,
-+			args = [{user, string}, {host, string}, {resource, string}, {reason, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = status_num_host, tags = [session, stats],
-+			desc = "Number of logged users with this status in host",
-+			module = ?MODULE, function = status_num,
-+			args = [{host, string}, {status, string}],
-+			result = {users, integer}},
-+     #ejabberd_commands{name = status_num, tags = [session, stats],
-+			desc = "Number of logged users with this status",
-+			module = ?MODULE, function = status_num,
-+			args = [{status, string}],
-+			result = {users, integer}},
-+     #ejabberd_commands{name = status_list_host, tags = [session],
-+			desc = "List of users logged in host with their statuses",
-+			module = ?MODULE, function = status_list,
-+			args = [{host, string}, {status, string}],
-+			result = {users, {list,
-+					  {userstatus, {tuple, [
-+								{user, string},
-+								{host, string},
-+								{resource, string},
-+								{priority, integer},
-+								{status, string}
-+							       ]}}
-+					 }}},
-+     #ejabberd_commands{name = status_list, tags = [session],
-+			desc = "List of logged users with this status",
-+			module = ?MODULE, function = status_list,
-+			args = [{status, string}],
-+			result = {users, {list,
-+					  {userstatus, {tuple, [
-+								{user, string},
-+								{host, string},
-+								{resource, string},
-+								{priority, integer},
-+								{status, string}
-+							       ]}}
-+					 }}},
-+
-+     #ejabberd_commands{name = set_nickname, tags = [vcard],
-+			desc = "Set nickname in a user's vcard",
-+			module = ?MODULE, function = set_nickname,
-+			args = [{user, string}, {host, string}, {nickname, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = get_vcard, tags = [vcard],
-+			desc = "Get content from a vCard field",
-+			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
-+			module = ?MODULE, function = get_vcard,
-+			args = [{user, string}, {host, string}, {name, string}],
-+			result = {content, string}},
-+     #ejabberd_commands{name = get_vcard2, tags = [vcard],
-+			desc = "Get content from a vCard field",
-+			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
-+			module = ?MODULE, function = get_vcard,
-+			args = [{user, string}, {host, string}, {name, string}, {subname, string}],
-+			result = {content, string}},
-+     #ejabberd_commands{name = set_vcard, tags = [vcard],
-+			desc = "Set content in a vCard field",
-+			longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" ++ VcardXEP,
-+			module = ?MODULE, function = set_vcard,
-+			args = [{user, string}, {host, string}, {name, string}, {content, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = set_vcard2, tags = [vcard],
-+			desc = "Set content in a vCard subfield",
-+			longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" ++ VcardXEP,
-+			module = ?MODULE, function = set_vcard2,
-+			args = [{user, string}, {host, string}, {name, string}, {subname, string}, {content, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = add_rosteritem, tags = [roster],
-+			desc = "Add an item to a user's roster",
-+			module = ?MODULE, function = add_rosteritem,
-+			args = [{localuser, string}, {localserver, string},
-+				{user, string}, {server, string},
-+				{nick, string}, {group, string},
-+				{subs, string}],
-+			result = {res, rescode}},
-+     %%{"", "subs= none, from, to or both"},
-+     %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
-+     %%{"", "will add mike@server.com to peter@localhost roster"},
-+     #ejabberd_commands{name = delete_rosteritem, tags = [roster],
-+			desc = "Delete an item from a user's roster",
-+			module = ?MODULE, function = delete_rosteritem,
-+			args = [{localuser, string}, {localserver, string},
-+				{user, string}, {server, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = process_rosteritems, tags = [roster],
-+			desc = "List or delete rosteritems that match filtering options",
-+			longdesc = "Explanation of each argument:\n"
-+			" - action: what to do with each rosteritem that "
-+			"matches all the filtering options\n"
-+			" - subs: subscription type\n"
-+			" - asks: pending subscription\n"
-+			" - users: the JIDs of the local user\n"
-+			" - contacts: the JIDs of the contact in the roster\n"
-+			"\n"
-+			"Allowed values in the arguments:\n"
-+			"  ACTION = list | delete\n"
-+			"  SUBS = SUB[:SUB]* | any\n"
-+			"  SUB = none | from | to | both\n"
-+			"  ASKS = ASK[:ASK]* | any\n"
-+			"  ASK = none | out | in\n"
-+			"  USERS = JID[:JID]* | any\n"
-+			"  CONTACTS = JID[:JID]* | any\n"
-+			"  JID = characters valid in a JID, and can use the "
-+			"globs: *, ? and [...]\n"
-+			"\n"
-+			"This example will list roster items with subscription "
-+			"'none', 'from' or 'to' that have any ask property, of "
-+			"local users which JID is in the virtual host "
-+			"'example.org' and that the contact JID is either a "
-+			"bare server name (without user part) or that has a "
-+			"user part and the server part contains the word 'icq'"
-+			":\n  list none:from:to any *@example.org *:*@*icq*",
-+			module = ?MODULE, function = process_rosteritems,
-+			args = [{action, string}, {subs, string},
-+				{asks, string}, {users, string},
-+				{contacts, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = get_roster, tags = [roster],
-+			desc = "Get roster of a local user",
-+			module = ?MODULE, function = get_roster,
-+			args = [{user, string}, {host, string}],
-+			result = {contacts, {list, {contact, {tuple, [
-+								      {jid, string},
-+								      {nick, string},
-+								      {group, string}
-+								     ]}}}}},
-+     #ejabberd_commands{name = push_roster, tags = [roster],
-+			desc = "Push template roster from file to a user",
-+			module = ?MODULE, function = push_roster,
-+			args = [{file, string}, {user, string}, {host, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = push_roster_all, tags = [roster],
-+			desc = "Push template roster from file to all those users",
-+			module = ?MODULE, function = push_roster_all,
-+			args = [{file, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = push_alltoall, tags = [roster],
-+			desc = "Add all the users to all the users of Host in Group",
-+			module = ?MODULE, function = push_alltoall,
-+			args = [{host, string}, {group, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = srg_create, tags = [shared_roster_group],
-+			desc = "Create a Shared Roster Group",
-+			longdesc = "If you want to specify several group "
-+			"identifiers in the Display argument,\n"
-+			"put  \\ \" around the argument and\nseparate the "
-+			"identifiers with \\ \\ n\n"
-+			"For example:\n"
-+			"  ejabberdctl srg_create group3 localhost "
-+			"name desc \\\"group1\\\\ngroup2\\\"",
-+			module = ?MODULE, function = srg_create,
-+			args = [{group, string}, {host, string},
-+				{name, string}, {description, string}, {display, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = srg_delete, tags = [shared_roster_group],
-+			desc = "Delete a Shared Roster Group",
-+			module = ?MODULE, function = srg_delete,
-+			args = [{group, string}, {host, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = srg_list, tags = [shared_roster_group],
-+			desc = "List the Shared Roster Groups in Host",
-+			module = ?MODULE, function = srg_list,
-+			args = [{host, string}],
-+			result = {groups, {list, {id, string}}}},
-+     #ejabberd_commands{name = srg_get_info, tags = [shared_roster_group],
-+			desc = "Get info of a Shared Roster Group",
-+			module = ?MODULE, function = srg_get_info,
-+			args = [{group, string}, {host, string}],
-+			result = {informations, {list, {information, {tuple, [{key, string}, {value, string}]}}}}},
-+     #ejabberd_commands{name = srg_get_members, tags = [shared_roster_group],
-+			desc = "Get members of a Shared Roster Group",
-+			module = ?MODULE, function = srg_get_members,
-+			args = [{group, string}, {host, string}],
-+			result = {members, {list, {member, string}}}},
-+     #ejabberd_commands{name = srg_user_add, tags = [shared_roster_group],
-+			desc = "Add the JID user@host to the Shared Roster Group",
-+			module = ?MODULE, function = srg_user_add,
-+			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = srg_user_del, tags = [shared_roster_group],
-+			desc = "Delete this JID user@host from the Shared Roster Group",
-+			module = ?MODULE, function = srg_user_del,
-+			args = [{user, string}, {host, string}, {group, string}, {grouphost, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = send_message_chat, tags = [stanza],
-+			desc = "Send a chat message to a local or remote bare of full JID",
-+			module = ?MODULE, function = send_message_chat,
-+			args = [{from, string}, {to, string}, {body, string}],
-+			result = {res, rescode}},
-+     #ejabberd_commands{name = send_message_headline, tags = [stanza],
-+			desc = "Send a headline message to a local or remote bare of full JID",
-+			module = ?MODULE, function = send_message_headline,
-+			args = [{from, string}, {to, string},
-+				{subject, string}, {body, string}],
-+			result = {res, rescode}},
-+
-+     #ejabberd_commands{name = stats, tags = [stats],
-+			desc = "Get statistical value: registeredusers onlineusers onlineusersnode uptimeseconds",
-+			module = ?MODULE, function = stats,
-+			args = [{name, string}],
-+			result = {stat, integer}},
-+     #ejabberd_commands{name = stats_host, tags = [stats],
-+			desc = "Get statistical value for this host: registeredusers onlineusers",
-+			module = ?MODULE, function = stats,
-+			args = [{name, string}, {host, string}],
-+			result = {stat, integer}}
-+    ].
-+
-+
-+%%%
-+%%% Node
-+%%%
-+
-+compile(File) ->
-+    case compile:file(File) of
-+	ok -> ok;
-+	_ -> error
-+    end.
-+
-+load_config(Path) ->
-+    ok = ejabberd_config:load_file(Path).
-+
-+get_cookie() ->
-+    atom_to_list(erlang:get_cookie()).
-+
-+remove_node(Node) ->
-+    mnesia:del_table_copy(schema, list_to_atom(Node)),
-+    ok.
-+
-+export2odbc(Host, Directory) ->
-+    Tables = [
-+	      {export_last, last},
-+	      {export_offline, offline},
-+	      {export_passwd, passwd},
-+	      {export_private_storage, private_storage},
-+	      {export_roster, roster},
-+	      {export_vcard, vcard},
-+	      {export_vcard_search, vcard_search}],
-+    Export = fun({TableFun, Table}) ->
-+		     Filename = filename:join([Directory, atom_to_list(Table)++".txt"]),
-+		     io:format("Trying to export Mnesia table '~p' on Host '~s' to file '~s'~n", [Table, Host, Filename]),
-+		     Res = (catch ejd2odbc:TableFun(Host, Filename)),
-+		     io:format("  Result: ~p~n", [Res])
-+	     end,
-+    lists:foreach(Export, Tables),
-+    ok.
-+
-+
-+%%%
-+%%% Accounts
-+%%%
-+
-+set_password(User, Host, Password) ->
-+    case ejabberd_auth:set_password(User, Host, Password) of
-+	ok ->
-+	    ok;
-+	_ ->
-+	    error
-+    end.
-+
-+%% Copied some code from ejabberd_commands.erl
-+check_password_hash(User, Host, PasswordHash, HashMethod) ->
-+    AccountPass = ejabberd_auth:get_password_s(User, Host),
-+    AccountPassHash = case HashMethod of
-+			  "md5" -> get_md5(AccountPass);
-+			  "sha" -> get_sha(AccountPass);
-+			  _ -> undefined
-+		      end,
-+    case AccountPassHash of
-+	undefined -> error;
-+	PasswordHash -> ok;
-+	_ -> error
-+    end.
-+get_md5(AccountPass) ->
-+    lists:flatten([io_lib:format("~.16B", [X])
-+		   || X <- binary_to_list(crypto:md5(AccountPass))]).
-+get_sha(AccountPass) ->
-+    lists:flatten([io_lib:format("~.16B", [X])
-+		   || X <- binary_to_list(crypto:sha(AccountPass))]).
-+
-+num_active_users(Host, Days) ->
-+    list_last_activity(Host, true, Days).
-+
-+%% Code based on ejabberd/src/web/ejabberd_web_admin.erl
-+list_last_activity(Host, Integral, Days) ->
-+    {MegaSecs, Secs, _MicroSecs} = now(),
-+    TimeStamp = MegaSecs * 1000000 + Secs,
-+    TS = TimeStamp - Days * 86400,
-+    case catch mnesia:dirty_select(
-+		 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
-+				  [{'>', '$1', TS}],
-+				  [{'trunc', {'/',
-+					      {'-', TimeStamp, '$1'},
-+					      86400}}]}]) of
-+							      {'EXIT', _Reason} ->
-+		 [];
-+	       Vals ->
-+		 Hist = histogram(Vals, Integral),
-+		 if
-+		     Hist == [] ->
-+			 0;
-+		     true ->
-+			 Left = Days - length(Hist),
-+			 Tail = if
-+				    Integral ->
-+					lists:duplicate(Left, lists:last(Hist));
-+				    true ->
-+					lists:duplicate(Left, 0)
-+				end,
-+			 lists:nth(Days, Hist ++ Tail)
-+		 end
-+	 end.
-+histogram(Values, Integral) ->
-+    histogram(lists:sort(Values), Integral, 0, 0, []).
-+histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
-+    histogram(T, Integral, Current, Count + 1, Hist);
-+histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
-+    if
-+	Integral ->
-+	    histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
-+	true ->
-+	    histogram(Values, Integral, Current + 1, 0, [Count | Hist])
-+    end;
-+histogram([], _Integral, _Current, Count, Hist) ->
-+    if
-+	Count > 0 ->
-+	    lists:reverse([Count | Hist]);
-+	true ->
-+	    lists:reverse(Hist)
-+    end.
-+
-+
-+-record(last_activity, {us, timestamp, status}).
-+
-+delete_old_users(Days) ->
-+    %% Get the list of registered users
-+    Users = ejabberd_auth:dirty_get_registered_users(),
-+
-+    {removed, N, UR} = delete_old_users(Days, Users),
-+    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
-+
-+delete_old_users_vhost(Host, Days) ->
-+    %% Get the list of registered users
-+    Users = ejabberd_auth:get_vh_registered_users(Host),
-+
-+    {removed, N, UR} = delete_old_users(Days, Users),
-+    {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
-+
-+delete_old_users(Days, Users) ->
-+    %% Convert older time
-+    SecOlder = Days*24*60*60,
-+
-+    %% Get current time
-+    {MegaSecs, Secs, _MicroSecs} = now(),
-+    TimeStamp_now = MegaSecs * 1000000 + Secs,
-+
-+    %% For a user, remove if required and answer true
-+    F = fun({LUser, LServer}) ->
-+		%% Check if the user is logged
-+		case ejabberd_sm:get_user_resources(LUser, LServer) of
-+		    %% If it isnt
-+		    [] ->
-+			%% Look for his last_activity
-+			case mnesia:dirty_read(last_activity, {LUser, LServer}) of
-+			    %% If it is
-+			    %% existent:
-+			    [#last_activity{timestamp = TimeStamp}] ->
-+				%% get his age
-+				Sec = TimeStamp_now - TimeStamp,
-+				%% If he is
-+				if
-+				    %% younger than SecOlder:
-+				    Sec < SecOlder ->
-+					%% do nothing
-+					false;
-+				    %% older:
-+				    true ->
-+					%% remove the user
-+					ejabberd_auth:remove_user(LUser, LServer),
-+					true
-+				end;
-+			    %% nonexistent:
-+			    [] ->
-+				%% remove the user
-+				ejabberd_auth:remove_user(LUser, LServer),
-+				true
-+			end;
-+		    %% Else
-+		    _ ->
-+			%% do nothing
-+			false
-+		end
-+	end,
-+    %% Apply the function to every user in the list
-+    Users_removed = lists:filter(F, Users),
-+    {removed, length(Users_removed), Users_removed}.
-+
-+
-+%%
-+%% Ban account
-+
-+ban_account(User, Host, ReasonText) ->
-+    Reason = prepare_reason(ReasonText),
-+    kick_sessions(User, Host, Reason),
-+    set_random_password(User, Host, Reason),
-+    ok.
-+
-+kick_sessions(User, Server, Reason) ->
-+    lists:map(
-+      fun(Resource) ->
-+	      kick_this_session(User, Server, Resource, Reason)
-+      end,
-+      get_resources(User, Server)).
-+
-+get_resources(User, Server) ->
-+    lists:map(
-+      fun(Session) ->
-+	      element(3, Session#session.usr)
-+      end,
-+      get_sessions(User, Server)).
-+
-+get_sessions(User, Server) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
-+    Sessions =  mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
-+    true = is_list(Sessions),
-+    Sessions.
-+
-+set_random_password(User, Server, Reason) ->
-+    NewPass = build_random_password(Reason),
-+    set_password_auth(User, Server, NewPass).
-+
-+build_random_password(Reason) ->
-+    Date = jlib:timestamp_to_iso(calendar:universal_time()),
-+    RandomString = randoms:get_string(),
-+    "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
-+
-+set_password_auth(User, Server, Password) ->
-+    ok = ejabberd_auth:set_password(User, Server, Password).
-+
-+prepare_reason([]) ->
-+    "Kicked by administrator";
-+prepare_reason([Reason]) ->
-+    Reason;
-+prepare_reason(Reason) when is_list(Reason) ->
-+    Reason;
-+prepare_reason(StringList) ->
-+    string:join(StringList, "_").
-+
-+
-+%%%
-+%%% Sessions
-+%%%
-+
-+num_resources(User, Host) ->
-+    length(ejabberd_sm:get_user_resources(User, Host)).
-+
-+resource_num(User, Host, Num) ->
-+    Resources = ejabberd_sm:get_user_resources(User, Host),
-+    case (0<Num) and (Num=<length(Resources)) of
-+	true ->
-+	    lists:nth(Num, Resources);
-+	false ->
-+	    lists:flatten(io_lib:format("Error: Wrong resource number: ~p", [Num]))
-+    end.
-+
-+kick_session(User, Server, Resource, ReasonText) ->
-+    kick_this_session(User, Server, Resource, prepare_reason(ReasonText)),
-+    ok.
-+
-+kick_this_session(User, Server, Resource, Reason) ->
-+    ejabberd_router:route(
-+      jlib:make_jid("", "", ""),
-+      jlib:make_jid(User, Server, Resource),
-+      {xmlelement, "broadcast", [], [{exit, Reason}]}).
-+
-+
-+status_num(Host, Status) ->
-+    length(get_status_list(Host, Status)).
-+status_num(Status) ->
-+    status_num("all", Status).
-+status_list(Host, Status) ->
-+    Res = get_status_list(Host, Status),
-+    [{U, S, R, P, St} || {U, S, R, P, St} <- Res].
-+status_list(Status) ->
-+    status_list("all", Status).
-+
-+
-+get_status_list(Host, Status_required) ->
-+    %% Get list of all logged users
-+    Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
-+    %% Reformat the list
-+    Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
-+    Fhost = case Host of
-+		"all" ->
-+		    %% All hosts are requested, so dont filter at all
-+		    fun(_, _) -> true end;
-+		_ ->
-+		    %% Filter the list, only Host is interesting
-+		    fun(A, B) -> A == B end
-+	    end,
-+    Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
-+    %% For each Pid, get its presence
-+    Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
-+    %% Filter by status
-+    Fstatus = case Status_required of
-+		  "all" ->
-+		      fun(_, _) -> true end;
-+		  _ ->
-+		      fun(A, B) -> A == B end
-+	      end,
-+    [{User, Server, Resource, Priority, stringize(Status_text)}
-+     || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
-+	apply(Fstatus, [Status, Status_required])].
-+
-+%% Make string more print-friendly
-+stringize(String) ->
-+    %% Replace newline characters with other code
-+    element(2, regexp:gsub(String, "\n", "\\n")).
-+
-+
-+%%%
-+%%% Vcard
-+%%%
-+
-+set_nickname(User, Host, Nickname) ->
-+    R = mod_vcard:process_sm_iq(
-+	  {jid, User, Host, "", User, Host, ""},
-+	  {jid, User, Host, "", User, Host, ""},
-+	  {iq, "", set, "", "en",
-+	   {xmlelement, "vCard",
-+	    [{"xmlns", "vcard-temp"}], [
-+					{xmlelement, "NICKNAME", [], [{xmlcdata, Nickname}]}
-+				       ]
-+	   }}),
-+    case R of
-+	{iq, [], result, [], _L, []} ->
-+	    ok;
-+	_ ->
-+	    error
-+    end.
-+
-+get_vcard(User, Host, Name) ->
-+    get_vcard_content(User, Host, [Name]).
-+
-+get_vcard(User, Host, Name, Subname) ->
-+    get_vcard_content(User, Host, [Name, Subname]).
-+
-+set_vcard(User, Host, Name, Content) ->
-+    set_vcard_content(User, Host, [Name], Content).
-+
-+set_vcard(User, Host, Name, Subname, Content) ->
-+    set_vcard_content(User, Host, [Name, Subname], Content).
-+
-+
-+%%
-+%% Internal vcard
-+
-+get_vcard_content(User, Server, Data) ->
-+    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+    JID = jlib:make_jid(User, Server, ""),
-+    IQ = #iq{type = get, xmlns = ?NS_VCARD},
-+    IQr = Module:Function(JID, JID, IQ),
-+    case IQr#iq.sub_el of
-+	[A1] ->
-+	    case get_vcard(Data, A1) of
-+		false -> "Error: no_value";
-+		Elem -> xml:get_tag_cdata(Elem)
-+	    end;
-+	[] ->
-+	    "Error: no_vcard"
-+    end.
-+
-+get_vcard([Data1, Data2], A1) ->
-+    case xml:get_subtag(A1, Data1) of
-+    	false -> false;
-+	A2 -> get_vcard([Data2], A2)
-+    end;
-+
-+get_vcard([Data], A1) ->
-+    xml:get_subtag(A1, Data).
-+
-+set_vcard_content(User, Server, Data, Content) ->
-+    [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
-+    JID = jlib:make_jid(User, Server, ""),
-+    IQ = #iq{type = get, xmlns = ?NS_VCARD},
-+    IQr = Module:Function(JID, JID, IQ),
-+
-+    %% Get old vcard
-+    A4 = case IQr#iq.sub_el of
-+	     [A1] ->
-+		 {_, _, _, A2} = A1,
-+		 update_vcard_els(Data, Content, A2);
-+	     [] ->
-+		 update_vcard_els(Data, Content, [])
-+	 end,
-+
-+    %% Build new vcard
-+    SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
-+    IQ2 = #iq{type=set, sub_el = SubEl},
-+
-+    Module:Function(JID, JID, IQ2),
-+    ok.
-+
-+update_vcard_els(Data, Content, Els1) ->
-+    Els2 = lists:keysort(2, Els1),
-+    [Data1 | Data2] = Data,
-+    NewEl = case Data2 of
-+		[] ->
-+		    {xmlelement, Data1, [], [{xmlcdata,Content}]};
-+		[D2] ->
-+		    OldEl = case lists:keysearch(Data1, 2, Els2) of
-+				{value, A} -> A;
-+				false -> {xmlelement, Data1, [], []}
-+			    end,
-+		    {xmlelement, _, _, ContentOld1} = OldEl,
-+		    Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
-+		    ContentOld2 = lists:keysort(2, ContentOld1),
-+		    ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
-+		    ContentNew = lists:keymerge(2, Content2, ContentOld3),
-+		    {xmlelement, Data1, [], ContentNew}
-+	    end,
-+    Els3 = lists:keydelete(Data1, 2, Els2),
-+    lists:keymerge(2, [NewEl], Els3).
-+
-+
-+%%%
-+%%% Roster
-+%%%
-+
-+add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) ->
-+    case add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, list_to_atom(Subs), []) of
-+	{atomic, ok} ->
-+	    push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}),
-+	    ok;
-+	_ ->
-+	    error
-+    end.
-+
-+add_rosteritem(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
-+    subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs).
-+
-+subscribe(LU, LS, User, Server, Nick, Group, Subscription, Xattrs) ->
-+    mnesia:transaction(
-+      fun() ->
-+	      mnesia:write({roster,
-+			    {LU,LS,{User,Server,[]}}, % uj
-+			    {LU,LS},                  % user
-+			    {User,Server,[]},      % jid
-+			    Nick,                  % name: "Mom", []
-+			    Subscription,  % subscription: none, to=you see him, from=he sees you, both
-+			    none,          % ask: out=send request, in=somebody requests you, none
-+			    [Group],       % groups: ["Family"]
-+			    Xattrs,        % xattrs: [{"category","conference"}]
-+			    []             % xs: []
-+			   })
-+      end).
-+
-+delete_rosteritem(LocalUser, LocalServer, User, Server) ->
-+    case unsubscribe(LocalUser, LocalServer, User, Server) of
-+	{atomic, ok} ->
-+	    push_roster_item(LocalUser, LocalServer, User, Server, remove),
-+	    ok;
-+	_  ->
-+	    error
-+    end.
-+
-+unsubscribe(LU, LS, User, Server) ->
-+    mnesia:transaction(
-+      fun() ->
-+              mnesia:delete({roster, {LU, LS, {User, Server, []}}})
-+      end).
-+
-+
-+%% -----------------------------
-+%% Get Roster
-+%% -----------------------------
-+
-+get_roster(User, Server) ->
-+    {ok, Roster} = get_roster2(User, Server),
-+    make_roster_xmlrpc(Roster).
-+
-+get_roster2(User, Server) ->
-+    Modules = gen_mod:loaded_modules(Server),
-+    Roster = case lists:member(mod_roster, Modules) of
-+		 true ->
-+		     mod_roster:get_user_roster([], {User, Server});
-+		 false ->
-+		     case lists:member(mod_roster_odbc, Modules) of
-+			 true ->
-+			     mod_roster_odbc:get_user_roster([], {User, Server});
-+			 false ->
-+			     {error, "Neither mod_roster or mod_roster_odbc are enabled"}
-+		     end
-+	     end,
-+    {ok, Roster}.
-+
-+%% Note: if a contact is in several groups, the contact is returned
-+%% several times, each one in a different group.
-+make_roster_xmlrpc(Roster) ->
-+    lists:foldl(
-+      fun(Item, Res) ->
-+	      JIDS = jlib:jid_to_string(Item#roster.jid),
-+	      Nick = Item#roster.name,
-+	      Groups = case Item#roster.groups of
-+			   [] -> [""];
-+			   Gs -> Gs
-+		       end,
-+	      ItemsX = [{JIDS, Nick, Group}
-+			|| Group <- Groups],
-+	      ItemsX ++ Res
-+      end,
-+      [],
-+      Roster).
-+
-+
-+%%-----------------------------
-+%% Push Roster from file
-+%%-----------------------------
-+
-+push_roster(File, User, Server) ->
-+    {ok, [Roster]} = file:consult(File),
-+    subscribe_roster({User, Server, "", User}, Roster).
-+
-+push_roster_all(File) ->
-+    {ok, [Roster]} = file:consult(File),
-+    subscribe_all(Roster).
-+
-+subscribe_all(Roster) ->
-+    subscribe_all(Roster, Roster).
-+subscribe_all([], _) ->
-+    ok;
-+subscribe_all([User1 | Users], Roster) ->
-+    subscribe_roster(User1, Roster),
-+    subscribe_all(Users, Roster).
-+
-+subscribe_roster(_, []) ->
-+    ok;
-+%% Do not subscribe a user to itself
-+subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
-+    subscribe_roster({Name, Server, Group, Nick}, Roster);
-+%% Subscribe Name2 to Name1
-+subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
-+    subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
-+    subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
-+
-+push_alltoall(S, G) ->
-+    Users = ejabberd_auth:get_vh_registered_users(S),
-+    Users2 = build_list_users(G, Users, []),
-+    subscribe_all(Users2),
-+    ok.
-+
-+build_list_users(_Group, [], Res) ->
-+    Res;
-+build_list_users(Group, [{User, Server}|Users], Res) ->
-+    build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
-+
-+%% @spec(LU, LS, U, S, Action) -> ok
-+%%       Action = {add, Nick, Subs, Group} | remove
-+%% @doc Push to the roster of account LU@LS the contact U@S.
-+%% The specific action to perform is defined in Action.
-+push_roster_item(LU, LS, U, S, Action) ->
-+    lists:foreach(fun(R) ->
-+			  push_roster_item(LU, LS, R, U, S, Action)
-+		  end, ejabberd_sm:get_user_resources(LU, LS)).
-+
-+push_roster_item(LU, LS, R, U, S, Action) ->
-+    Item = build_roster_item(U, S, Action),
-+    ResIQ = build_iq_roster_push(Item),
-+    LJID = jlib:make_jid(LU, LS, R),
-+    ejabberd_router:route(LJID, LJID, ResIQ).
-+
-+build_roster_item(U, S, {add, Nick, Subs, Group}) ->
-+    {xmlelement, "item",
-+     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
-+      {"name", Nick},
-+      {"subscription", Subs}],
-+     [{xmlelement, "group", [], [{xmlcdata, Group}]}]
-+    };
-+build_roster_item(U, S, remove) ->
-+    {xmlelement, "item",
-+     [{"jid", jlib:jid_to_string(jlib:make_jid(U, S, ""))},
-+      {"subscription", "remove"}],
-+     []
-+    }.
-+
-+build_iq_roster_push(Item) ->
-+    {xmlelement, "iq",
-+     [{"type", "set"}, {"id", "push"}],
-+     [{xmlelement, "query",
-+       [{"xmlns", ?NS_ROSTER}],
-+       [Item]
-+      }
-+     ]
-+    }.
-+
-+
-+%%%
-+%%% Shared Roster Groups
-+%%%
-+
-+srg_create(Group, Host, Name, Description, Display) ->
-+    {ok, DisplayList} = regexp:split(Display, "\\\\n"),
-+    Opts = [{name, Name},
-+	    {displayed_groups, DisplayList},
-+	    {description, Description}],
-+    {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
-+    ok.
-+
-+srg_delete(Group, Host) ->
-+    {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
-+    ok.
-+
-+srg_list(Host) ->
-+    lists:sort(mod_shared_roster:list_groups(Host)).
-+
-+srg_get_info(Group, Host) ->
-+    Opts = mod_shared_roster:get_group_opts(Host,Group),
-+    [{io_lib:format("~p", [Title]),
-+      io_lib:format("~p", [Value])} || {Title, Value} <- Opts].
-+
-+srg_get_members(Group, Host) ->
-+    Members = mod_shared_roster:get_group_explicit_users(Host,Group),
-+    [jlib:jid_to_string(jlib:make_jid(MUser, MServer, ""))
-+     || {MUser, MServer} <- Members].
-+
-+srg_user_add(User, Host, Group, GroupHost) ->
-+    {atomic, ok} = mod_shared_roster:add_user_to_group(GroupHost, {User, Host}, Group),
-+    ok.
-+
-+srg_user_del(User, Host, Group, GroupHost) ->
-+    {atomic, ok} = mod_shared_roster:remove_user_from_group(GroupHost, {User, Host}, Group),
-+    ok.
-+
-+
-+%%%
-+%%% Stanza
-+%%%
-+
-+%% @doc Send a chat message to a Jabber account.
-+%% @spec (From::string(), To::string(), Body::string()) -> ok
-+send_message_chat(From, To, Body) ->
-+    Packet = build_packet(message_chat, [Body]),
-+    send_packet_all_resources(From, To, Packet).
-+
-+%% @doc Send a headline message to a Jabber account.
-+%% @spec (From::string(), To::string(), Subject::string(), Body::string()) -> ok
-+send_message_headline(From, To, Subject, Body) ->
-+    Packet = build_packet(message_headline, [Subject, Body]),
-+    send_packet_all_resources(From, To, Packet).
-+
-+%% @doc Send a packet to a Jabber account.
-+%% If a resource was specified in the JID,
-+%% the packet is sent only to that specific resource.
-+%% If no resource was specified in the JID,
-+%% and the user is remote or local but offline,
-+%% the packet is sent to the bare JID.
-+%% If the user is local and is online in several resources,
-+%% the packet is sent to all its resources.
-+send_packet_all_resources(FromJIDString, ToJIDString, Packet) ->
-+    FromJID = jlib:string_to_jid(FromJIDString),
-+    ToJID = jlib:string_to_jid(ToJIDString),
-+    ToUser = ToJID#jid.user,
-+    ToServer = ToJID#jid.server,
-+    case ToJID#jid.resource of
-+	"" ->
-+	    send_packet_all_resources(FromJID, ToUser, ToServer, Packet);
-+	Res ->
-+	    send_packet_all_resources(FromJID, ToUser, ToServer, Res, Packet)
-+    end.
-+
-+send_packet_all_resources(FromJID, ToUser, ToServer, Packet) ->
-+    case ejabberd_sm:get_user_resources(ToUser, ToServer) of
-+	[] ->
-+	    send_packet_all_resources(FromJID, ToUser, ToServer, "", Packet);
-+	ToResources ->
-+	    lists:foreach(
-+	      fun(ToResource) ->
-+		      send_packet_all_resources(FromJID, ToUser, ToServer,
-+						ToResource, Packet)
-+	      end,
-+	      ToResources)
-+    end.
-+
-+send_packet_all_resources(FromJID, ToU, ToS, ToR, Packet) ->
-+    ToJID = jlib:make_jid(ToU, ToS, ToR),
-+    ejabberd_router:route(FromJID, ToJID, Packet).
-+
-+
-+build_packet(message_chat, [Body]) ->
-+    {xmlelement, "message",
-+     [{"type", "chat"}],
-+     [{xmlelement, "body", [], [{xmlcdata, Body}]}]
-+    };
-+build_packet(message_headline, [Subject, Body]) ->
-+    {xmlelement, "message",
-+     [{"type", "headline"}],
-+     [{xmlelement, "subject", [], [{xmlcdata, Subject}]},
-+      {xmlelement, "body", [], [{xmlcdata, Body}]}
-+     ]
-+    }.
-+
-+%%%
-+%%% Stats
-+%%%
-+
-+stats(Name) ->
-+    case Name of
-+	"uptimeseconds" -> trunc(element(1, erlang:statistics(wall_clock))/1000);
-+	"registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
-+	"onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
-+	"onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
-+    end.
-+
-+stats(Name, Host) ->
-+    case Name of
-+	"registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
-+	"onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
-+    end.
-+
-+
-+
-+%%-----------------------------
-+%% Purge roster items
-+%%-----------------------------
-+
-+process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
-+    Action = case ActionS of
-+		 "list" -> list;
-+		 "delete" -> delete
-+	     end,
-+
-+    Subs = lists:foldl(
-+	     fun(any, _) -> [none, from, to, both];
-+		(Sub, Subs) -> [Sub | Subs]
-+	     end,
-+	     [],
-+	     [list_to_atom(S) || S <- string:tokens(SubsS, ":")]
-+	    ),
-+
-+    Asks = lists:foldl(
-+	     fun(any, _) -> [none, out, in];
-+		(Ask, Asks) -> [Ask | Asks]
-+	     end,
-+	     [],
-+	     [list_to_atom(S) || S <- string:tokens(AsksS, ":")]
-+	    ),
-+
-+    Users = lists:foldl(
-+	      fun("any", _) -> ["*", "*@*"];
-+		 (U, Us) -> [U | Us]
-+	      end,
-+	      [],
-+	      [S || S <- string:tokens(UsersS, ":")]
-+	     ),
-+
-+    Contacts = lists:foldl(
-+		 fun("any", _) -> ["*", "*@*"];
-+		    (U, Us) -> [U | Us]
-+		 end,
-+		 [],
-+		 [S || S <- string:tokens(ContactsS, ":")]
-+		),
-+
-+    case rosteritem_purge({Action, Subs, Asks, Users, Contacts}) of
-+	{atomic, ok} ->
-+	    ok;
-+	{error, Reason} ->
-+	    io:format("Error purging rosteritems: ~p~n", [Reason]),
-+	    error;
-+	{badrpc, Reason} ->
-+	    io:format("BadRPC purging rosteritems: ~p~n", [Reason]),
-+	    error
-+    end.
-+
-+%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok}
-+rosteritem_purge(Options) ->
-+    Num_rosteritems = mnesia:table_info(roster, size),
-+    io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
-+    Key = mnesia:dirty_first(roster),
-+    ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
-+    {atomic, ok}.
-+
-+rip('$end_of_table', _Options, Counters) ->
-+    print_progress_line(Counters),
-+    ok;
-+rip(Key, Options, {Pr, NT, NV, ND}) ->
-+    Key_next = mnesia:dirty_next(roster, Key),
-+    {Action, _, _, _, _} = Options,
-+    ND2 = case decide_rip(Key, Options) of
-+	      true ->
-+		  apply_action(Action, Key),
-+		  ND+1;
-+	      false ->
-+		  ND
-+	  end,
-+    NV2 = NV+1,
-+    Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
-+    rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
-+
-+apply_action(list, Key) ->
-+    {User, Server, JID} = Key,
-+    {RUser, RServer, _} = JID,
-+    io:format("Matches: ~s@~s ~s@~s~n", [User, Server, RUser, RServer]);
-+apply_action(delete, Key) ->
-+    apply_action(list, Key),
-+    mnesia:dirty_delete(roster, Key).
-+
-+print_progress_line({Pr, NT, NV, ND}) ->
-+    Pr2 = trunc((NV/NT)*100),
-+    case Pr == Pr2 of
-+	true ->
-+	    ok;
-+	false ->
-+	    io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
-+    end,
-+    Pr2.
-+
-+decide_rip(Key, {_Action, Subs, Asks, User, Contact}) ->
-+    case catch mnesia:dirty_read(roster, Key) of
-+	[RI] ->
-+	    lists:member(RI#roster.subscription, Subs)
-+		andalso lists:member(RI#roster.ask, Asks)
-+		andalso decide_rip_jid(RI#roster.us, User)
-+		andalso decide_rip_jid(RI#roster.jid, Contact);
-+	_ ->
-+	    false
-+    end.
-+
-+%% Returns true if the server of the JID is included in the servers
-+decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
-+    decide_rip_jid({UName, UServer}, Match_list);
-+decide_rip_jid({UName, UServer}, Match_list) ->
-+    lists:any(
-+      fun(Match_string) ->
-+	      MJID = jlib:string_to_jid(Match_string),
-+	      MName = MJID#jid.luser,
-+	      MServer = MJID#jid.lserver,
-+	      Is_server = is_glob_match(UServer, MServer),
-+	      case MName of
-+		  [] when UName == [] ->
-+		      Is_server;
-+		  [] ->
-+		      false;
-+		  _ ->
-+		      Is_server
-+			  andalso is_glob_match(UName, MName)
-+	      end
-+      end,
-+      Match_list).
-+
-+%% Copied from ejabberd-2.0.0/src/acl.erl
-+is_regexp_match(String, RegExp) ->
-+    case regexp:first_match(String, RegExp) of
-+	nomatch ->
-+	    false;
-+	{match, _, _} ->
-+	    true;
-+	{error, ErrDesc} ->
-+	    io:format(
-+	      "Wrong regexp ~p in ACL: ~p",
-+	      [RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
-+	    false
-+    end.
-+is_glob_match(String, Glob) ->
-+    is_regexp_match(String, regexp:sh_to_awk(Glob)).
--- 
-1.7.10.1
-

diff --git a/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
deleted file mode 100644
index 1f59760..0000000
--- a/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From 0012d3c968c2aef3cd658d84936bd51bee8e33ce Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Tue, 16 Feb 2010 16:30:05 +0300
-Subject: [PATCH 05/10] Fedora-specific changes to ejabberdctl
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/ejabberdctl.template |    8 ++++----
- 1 file changed, 4 insertions(+), 4 deletions(-)
-
-diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
-index 0960f9a..c22478a 100644
---- a/src/ejabberdctl.template
-+++ b/src/ejabberdctl.template
-@@ -9,10 +9,10 @@ ERL_MAX_ETS_TABLES=1400
- 
- # define default environment variables
- NODE=ejabberd
--HOST=localhost
-+HOST=`hostname -s 2>/dev/null || echo "localhost"`
- ERLANG_NODE=$NODE@$HOST
- ERL=@erl@
--INSTALLUSER=@installuser@
-+INSTALLUSER=ejabberd
- 
- # parse command line parameters
- ARGS=
-@@ -46,7 +46,7 @@ if [ "$LOGS_DIR" = "" ] ; then
-     LOGS_DIR=@LOCALSTATEDIR@/log/ejabberd
- fi
- if [ "$SPOOLDIR" = "" ] ; then
--    SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd
-+    SPOOLDIR=@LOCALSTATEDIR@/lib/ejabberd/spool
- fi
- if [ "$EJABBERD_DOC_PATH" = "" ] ; then
-     EJABBERD_DOC_PATH=@DOCDIR@
-@@ -62,7 +62,7 @@ EJID=`id -g $INSTALLUSER`
- EXEC_CMD="false"
- for GID in $GIDS; do
-     if [ $GID -eq 0 ] ; then
--	EXEC_CMD="su ${INSTALLUSER} -p -c"
-+	EXEC_CMD="/sbin/runuser -s /bin/bash -p ejabberd -c"
-     fi
- done
- if [ "$ID" -eq "$EJID" ] ; then
--- 
-1.7.10.1
-

diff --git a/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch
new file mode 100644
index 0000000..a2d0ec9
--- /dev/null
+++ b/ejabberd-0005-Install-.so-objects-with-0755-permissions.patch
@@ -0,0 +1,26 @@
+From 924dab202126d7644a8e50e81d1b2de21875bb7f Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Sat, 12 Jun 2010 14:14:52 +0400
+Subject: [PATCH 5/8] Install *.so objects with 0755 permissions
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/Makefile.in |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 42af5b2..64ed856 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -237,7 +237,7 @@ install: all
+ 	#
+ 	# Binary system libraries
+ 	install -d $(SODIR)
+-	install -m 644 *.so $(SODIR)
++	install -m 755 *.so $(SODIR)
+ 	#
+ 	# Translated strings
+ 	install -d $(MSGSDIR)
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
deleted file mode 100644
index ac1deb7..0000000
--- a/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 8254e46cf2710edfc3c45f9da227e4613e342796 Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Sat, 12 Jun 2010 14:14:52 +0400
-Subject: [PATCH 06/10] Install *.so objects with 0755 permissions
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/Makefile.in |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index 42af5b2..64ed856 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -237,7 +237,7 @@ install: all
- 	#
- 	# Binary system libraries
- 	install -d $(SODIR)
--	install -m 644 *.so $(SODIR)
-+	install -m 755 *.so $(SODIR)
- 	#
- 	# Translated strings
- 	install -d $(MSGSDIR)
--- 
-1.7.10.1
-

diff --git a/ejabberd-0006-Use-versioned-directory-for-storing-docs.patch b/ejabberd-0006-Use-versioned-directory-for-storing-docs.patch
new file mode 100644
index 0000000..854ad81
--- /dev/null
+++ b/ejabberd-0006-Use-versioned-directory-for-storing-docs.patch
@@ -0,0 +1,35 @@
+From 889d68a28b4442cf71e8c4ad335a98718402f4a5 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Sat, 12 Jun 2010 16:12:11 +0400
+Subject: [PATCH 6/8] Use versioned directory for storing docs
+
+It's also a good idea to store doc-files under the versioned directory.
+This may greatly simplify parallel installation of different versions of
+ejabbed.
+
+Note that this may be done in the other way - by changing proper field
+in the AC_INIT macro, see the explanation of the field 'tarname':
+
+http://www.gnu.org/software/autoconf/manual/html_node/Initializing-configure.html
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/Makefile.in |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 64ed856..43b5e1f 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -98,7 +98,7 @@ SBINDIR = $(DESTDIR)@sbindir@
+ EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
+ 
+ # /share/doc/ejabberd
+-PACKAGE_TARNAME = @PACKAGE_TARNAME@
++PACKAGE_TARNAME = @PACKAGE_TARNAME@-@PACKAGE_VERSION@
+ datarootdir = @datarootdir@
+ DOCDIR = $(DESTDIR)@docdir@
+ 
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0007-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch b/ejabberd-0007-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
new file mode 100644
index 0000000..8795e99
--- /dev/null
+++ b/ejabberd-0007-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
@@ -0,0 +1,505 @@
+From f2a612dba3d6ab5dd5b522f74c493c78cdfed6c9 Mon Sep 17 00:00:00 2001
+From: Badlop <badlop@process-one.net>
+Date: Thu, 15 Apr 2010 17:20:16 +0200
+Subject: [PATCH 7/8] Support SASL GSSAPI authentication (thanks to Mikael
+ Magnusson)(EJAB-831)
+
+---
+ src/cyrsasl.erl           |   71 +++++++++++++++----
+ src/cyrsasl.hrl           |   15 ++++
+ src/cyrsasl_anonymous.erl |    6 +-
+ src/cyrsasl_digest.erl    |    6 +-
+ src/cyrsasl_gssapi.erl    |  166 +++++++++++++++++++++++++++++++++++++++++++++
+ src/cyrsasl_plain.erl     |    6 +-
+ src/cyrsasl_scram.erl     |    5 +-
+ src/ejabberd_c2s.erl      |   12 +++-
+ src/ejabberd_socket.erl   |   19 ++++++
+ 9 files changed, 281 insertions(+), 25 deletions(-)
+ create mode 100644 src/cyrsasl.hrl
+ create mode 100644 src/cyrsasl_gssapi.erl
+
+diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
+index 23f1721..5ff0916 100644
+--- a/src/cyrsasl.erl
++++ b/src/cyrsasl.erl
+@@ -30,21 +30,36 @@
+ -export([start/0,
+ 	 register_mechanism/3,
+ 	 listmech/1,
+-	 server_new/7,
++	 server_new/8,
+ 	 server_start/3,
+ 	 server_step/2]).
+ 
++-include("cyrsasl.hrl").
+ -include("ejabberd.hrl").
+ 
++%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain}
++%%     Mechanism = string()
++%%     Module = atom()
++%%     Require_Plain = bool().
++%% Registry entry of a supported SASL mechanism.
++
+ -record(sasl_mechanism, {mechanism, module, password_type}).
+--record(sasl_state, {service, myname, realm,
+-		     get_password, check_password, check_password_digest,
+-		     mech_mod, mech_state}).
++
++%% @type saslstate() = {sasl_state, Service, Myname, Mech_Mod, Mech_State, Params}
++%%     Service = string()
++%%     Myname = string()
++%%     Mech_Mod = atom()
++%%     Mech_State = term().
++%%     Params = term().
++%% State of this process.
++
++-record(sasl_state, {service, myname,
++		     mech_mod, mech_state, params}).
+ 
+ -export([behaviour_info/1]).
+ 
+ behaviour_info(callbacks) ->
+-    [{mech_new, 4}, {mech_step, 2}];
++    [{mech_new, 1}, {mech_step, 2}];
+ behaviour_info(_Other) ->
+     undefined.
+ 
+@@ -56,8 +71,30 @@ start() ->
+     cyrsasl_digest:start([]),
+     cyrsasl_scram:start([]),
+     cyrsasl_anonymous:start([]),
++    maybe_try_start_gssapi(),
+     ok.
+ 
++maybe_try_start_gssapi() ->
++    case os:getenv("KRB5_KTNAME") of
++        false ->
++	    ok;
++        _String ->
++	    try_start_gssapi()
++    end.
++
++try_start_gssapi() ->
++    case code:load_file(esasl) of
++	{module, _Module} ->
++	    cyrsasl_gssapi:start([]);
++	{error, What} ->
++	    ?ERROR_MSG("Support for GSSAPI not started because esasl.beam was not found: ~p", [What])
++    end.
++
++%% @spec (Mechanism, Module, Require_Plain) -> true
++%%     Mechanism = string()
++%%     Module = atom()
++%%     Require_Plain = bool()
++
+ register_mechanism(Mechanism, Module, PasswordType) ->
+     ets:insert(sasl_mechanism,
+ 	       #sasl_mechanism{mechanism = Mechanism,
+@@ -119,24 +156,28 @@ listmech(Host) ->
+     filter_anonymous(Host, Mechs).
+ 
+ server_new(Service, ServerFQDN, UserRealm, _SecFlags,
+-	   GetPassword, CheckPassword, CheckPasswordDigest) ->
++	   GetPassword, CheckPassword, CheckPasswordDigest, Socket) ->
++    Params = #sasl_params{
++      host = ServerFQDN,
++      realm = UserRealm,
++      get_password = GetPassword,
++      check_password = CheckPassword,
++      check_password_digest= CheckPasswordDigest,
++      socket = Socket
++     },
++
+     #sasl_state{service = Service,
+ 		myname = ServerFQDN,
+-		realm = UserRealm,
+-		get_password = GetPassword,
+-		check_password = CheckPassword,
+-		check_password_digest= CheckPasswordDigest}.
++		params = Params}.
++
+ 
+ server_start(State, Mech, ClientIn) ->
+     case lists:member(Mech, listmech(State#sasl_state.myname)) of
+ 	true ->
+ 	    case ets:lookup(sasl_mechanism, Mech) of
+ 		[#sasl_mechanism{module = Module}] ->
+-		    {ok, MechState} = Module:mech_new(
+-					State#sasl_state.myname,
+-					State#sasl_state.get_password,
+-					State#sasl_state.check_password,
+-					State#sasl_state.check_password_digest),
++		    {ok, MechState} =
++			Module:mech_new(State#sasl_state.params),
+ 		    server_step(State#sasl_state{mech_mod = Module,
+ 						 mech_state = MechState},
+ 				ClientIn);
+diff --git a/src/cyrsasl.hrl b/src/cyrsasl.hrl
+new file mode 100644
+index 0000000..b4cc3e3
+--- /dev/null
++++ b/src/cyrsasl.hrl
+@@ -0,0 +1,15 @@
++%% @type saslparams() = {sasl_params, Host, Realm, GetPassword, CheckPassword, CheckPasswordDigest}
++%%     Host = string()
++%%     Realm = string()
++%%     GetPassword = function()
++%%     CheckPassword = function()
++%%     CheckPasswordDigest = any().
++%% Parameters for SASL.
++
++-record(sasl_params, {
++	  host,
++	  realm,
++	  get_password,
++	  check_password,
++	  check_password_digest,
++	  socket}).
+diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl
+index 25fcd66..aeb09ea 100644
+--- a/src/cyrsasl_anonymous.erl
++++ b/src/cyrsasl_anonymous.erl
+@@ -27,7 +27,9 @@
+ 
+ -module(cyrsasl_anonymous).
+ 
+--export([start/1, stop/0, mech_new/4, mech_step/2]).
++-export([start/1, stop/0, mech_new/1, mech_step/2]).
++
++-include("cyrsasl.hrl").
+ 
+ -behaviour(cyrsasl).
+ 
+@@ -40,7 +42,7 @@ start(_Opts) ->
+ stop() ->
+     ok.
+ 
+-mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
++mech_new(#sasl_params{host=Host}) ->
+     {ok, #state{server = Host}}.
+ 
+ mech_step(State, _ClientIn) ->
+diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
+index 134a86d..d904801 100644
+--- a/src/cyrsasl_digest.erl
++++ b/src/cyrsasl_digest.erl
+@@ -29,10 +29,11 @@
+ 
+ -export([start/1,
+ 	 stop/0,
+-	 mech_new/4,
++	 mech_new/1,
+ 	 mech_step/2]).
+ 
+ -include("ejabberd.hrl").
++-include("cyrsasl.hrl").
+ 
+ -behaviour(cyrsasl).
+ 
+@@ -47,7 +48,8 @@ start(_Opts) ->
+ stop() ->
+     ok.
+ 
+-mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
++mech_new(#sasl_params{host=Host, get_password=GetPassword,
++		      check_password_digest=CheckPasswordDigest}) ->
+     {ok, #state{step = 1,
+ 		nonce = randoms:get_string(),
+ 		host = Host,
+diff --git a/src/cyrsasl_gssapi.erl b/src/cyrsasl_gssapi.erl
+new file mode 100644
+index 0000000..11d9955
+--- /dev/null
++++ b/src/cyrsasl_gssapi.erl
+@@ -0,0 +1,166 @@
++%%%----------------------------------------------------------------------
++%%% File    : cyrsasl_gssapi.erl
++%%% Author  : Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Purpose : GSSAPI SASL mechanism
++%%% Created : 1 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
++%%% Id      : $Id: $
++%%%----------------------------------------------------------------------
++%%%
++%%% Copyright (C) 2007-2009  Mikael Magnusson <mikma@users.sourceforge.net>
++%%%
++%%% Permission is hereby granted, free of charge, to any person
++%%% obtaining a copy of this software and associated documentation
++%%% files (the "Software"), to deal in the Software without
++%%% restriction, including without limitation the rights to use, copy,
++%%% modify, merge, publish, distribute, sublicense, and/or sell copies
++%%% of the Software, and to permit persons to whom the Software is
++%%% furnished to do so, subject to the following conditions:
++%%%
++%%% The above copyright notice and this permission notice shall be
++%%% included in all copies or substantial portions of the Software.
++%%%
++%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++%%% SOFTWARE.
++%%%
++
++%%%
++%%% configuration options:
++%%% {sasl_realm, "<Kerberos realm>"}.
++%%%
++%%% environment variables:
++%%% KRB5_KTNAME
++%%%
++
++-module(cyrsasl_gssapi).
++-author('mikma@users.sourceforge.net').
++-vsn('$Revision: $ ').
++
++-export([start/1,
++	 stop/0,
++	 mech_new/1,
++	 mech_step/2]).
++
++-include("ejabberd.hrl").
++-include("cyrsasl.hrl").
++
++-behaviour(cyrsasl).
++
++-define(SERVER, ?MODULE).
++-define(SERVICE, "xmpp").
++
++-record(state, {sasl,
++		needsmore=true,
++		step=0,
++		host,
++		realm,
++		authid,
++		authzid,
++		authrealm,
++		error}).
++
++start(_Opts) ->
++    ChildSpec =
++	{?SERVER,
++	 {esasl, start_link, [{local, ?SERVER}]},
++	 transient,
++	 1000,
++	 worker,
++	 [esasl]},
++
++    case supervisor:start_child(ejabberd_sup, ChildSpec) of
++	{ok, _Pid} ->
++	    cyrsasl:register_mechanism("GSSAPI", ?MODULE, false);
++	{error, Error} = E ->
++	    ?ERROR_MSG("esasl failed: ~p", [Error]),
++	    E
++    end.
++
++
++stop() ->
++    catch esasl:stop(?SERVER),
++    supervisor:terminate_child(ejabberd_sup, ?SERVER),
++    supervisor:delete_child(ejabberd_sup, ?SERVER).
++
++mech_new(#sasl_params{host=Host, realm=Realm, socket=Socket}) ->
++    case ejabberd_socket:gethostname(Socket) of
++	{ok, FQDN} ->
++	    ?DEBUG("mech_new ~p ~p ~p~n", [Host, Realm, FQDN]),
++	    case esasl:server_start(?SERVER, "GSSAPI", ?SERVICE, FQDN) of
++		{ok, Sasl} ->
++		    {ok, #state{sasl=Sasl,host=Host,realm=Realm}};
++		{error, {gsasl_error, Error}} ->
++		    {ok, Str} = esasl:str_error(?SERVER, Error),
++		    ?DEBUG("esasl error: ~p", [Str]),
++		    {ok, #state{needsmore=error,error="internal-server-error"}};
++		{error, Error} ->
++		    ?DEBUG("esasl error: ~p", [Error]),
++		    {ok, #state{needsmore=error,error="internal-server-error"}}
++	    end;
++	{error, Error} ->
++	    ?DEBUG("gethostname error: ~p", [Error]),
++	    {ok, #state{needsmore=error,error="internal-server-error"}}
++    end.
++
++mech_step(State, ClientIn) when is_list(ClientIn) ->
++    catch do_step(State, ClientIn).
++
++do_step(#state{needsmore=error,error=Error}=_State, _) ->
++    {error, Error};
++do_step(#state{needsmore=false}=State, _) ->
++    check_user(State);
++do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
++    ?DEBUG("mech_step~n", []),
++    case esasl:step(Sasl, list_to_binary(ClientIn)) of
++	{ok, RspAuth} ->
++	    ?DEBUG("ok~n", []),
++	    {ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name),
++	    {ok, Authzid} = esasl:property_get(Sasl, authzid),
++	    {Authid, [$@ | Auth_realm]} =
++		lists:splitwith(fun(E)->E =/= $@ end, Display_name),
++	    State1 = State#state{authid=Authid,
++				 authzid=Authzid,
++				 authrealm=Auth_realm},
++	    handle_step_ok(State1, binary_to_list(RspAuth));
++	{needsmore, RspAuth} ->
++	    ?DEBUG("needsmore~n", []),
++	    if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
++		    {error, "not-authorized"};
++		true ->
++		    {continue, binary_to_list(RspAuth),
++		     State#state{step=Step+1}}
++	    end;
++	{error, _} ->
++	    {error, "not-authorized"}
++    end.
++
++handle_step_ok(State, []) ->
++    check_user(State);
++handle_step_ok(#state{step=Step}=State, RspAuth) ->
++    ?DEBUG("continue~n", []),
++    {continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
++
++check_user(#state{authid=Authid,authzid=Authzid,
++		  authrealm=Auth_realm,host=Host,realm=Realm}) ->
++    if Realm =/= Auth_realm ->
++	    ?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
++	    throw({error, "not-authorized"});
++       true ->
++	    ok
++    end,
++
++    case ejabberd_auth:is_user_exists(Authid, Host) of
++	false ->
++	    ?DEBUG("bad user ~p~n",[Authid]),
++	    throw({error, "not-authorized"});
++	true ->
++	    ok
++    end,
++
++    ?DEBUG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
++    {ok, [{username, Authid}, {authzid, Authzid}]}.
+diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
+index a0804fd..14b3c19 100644
+--- a/src/cyrsasl_plain.erl
++++ b/src/cyrsasl_plain.erl
+@@ -27,7 +27,9 @@
+ -module(cyrsasl_plain).
+ -author('alexey@process-one.net').
+ 
+--export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
++-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]).
++
++-include("cyrsasl.hrl").
+ 
+ -behaviour(cyrsasl).
+ 
+@@ -40,7 +42,7 @@ start(_Opts) ->
+ stop() ->
+     ok.
+ 
+-mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
++mech_new(#sasl_params{check_password = CheckPassword}) ->
+     {ok, #state{check_password = CheckPassword}}.
+ 
+ mech_step(State, ClientIn) ->
+diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl
+index 74977bd..c3bf5c6 100644
+--- a/src/cyrsasl_scram.erl
++++ b/src/cyrsasl_scram.erl
+@@ -29,10 +29,11 @@
+ 
+ -export([start/1,
+ 	 stop/0,
+-	 mech_new/4,
++	 mech_new/1,
+ 	 mech_step/2]).
+ 
+ -include("ejabberd.hrl").
++-include("cyrsasl.hrl").
+ 
+ -behaviour(cyrsasl).
+ 
+@@ -48,7 +49,7 @@ start(_Opts) ->
+ stop() ->
+     ok.
+ 
+-mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) ->
++mech_new(#sasl_params{get_password=GetPassword}) ->
+     {ok, #state{step = 2, get_password = GetPassword}}.
+ 
+ mech_step(#state{step = 2} = State, ClientIn) ->
+diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
+index 552aa6d..038d3c5 100644
+--- a/src/ejabberd_c2s.erl
++++ b/src/ejabberd_c2s.erl
+@@ -305,9 +305,16 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
+ 			    send_header(StateData, Server, "1.0", DefaultLang),
+ 			    case StateData#state.authenticated of
+ 				false ->
++				    Realm =
++					case ejabberd_config:get_local_option({sasl_realm, Server}) of
++					    undefined ->
++						"";
++					    Realm0 ->
++						Realm0
++					end,
+ 				    SASLState =
+ 					cyrsasl:server_new(
+-					  "jabber", Server, "", [],
++					  "jabber", Server, Realm, [],
+ 					  fun(U) ->
+ 						  ejabberd_auth:get_password_with_authmodule(
+ 						    U, Server)
+@@ -319,7 +326,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
+ 					  fun(U, P, D, DG) ->
+ 						  ejabberd_auth:check_password_with_authmodule(
+ 						    U, Server, P, D, DG)
+-					  end),
++					  end,
++					  StateData#state.socket),
+ 				    Mechs = lists:map(
+ 					      fun(S) ->
+ 						      {xmlelement, "mechanism", [],
+diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
+index 254751b..3509018 100644
+--- a/src/ejabberd_socket.erl
++++ b/src/ejabberd_socket.erl
+@@ -44,9 +44,11 @@
+ 	 get_peer_certificate/1,
+ 	 get_verify_result/1,
+ 	 close/1,
++	 gethostname/1,
+ 	 sockname/1, peername/1]).
+ 
+ -include("ejabberd.hrl").
++-include_lib("kernel/include/inet.hrl").
+ 
+ -record(socket_state, {sockmod, socket, receiver}).
+ 
+@@ -228,6 +230,23 @@ peername(#socket_state{sockmod = SockMod, socket = Socket}) ->
+ 	    SockMod:peername(Socket)
+     end.
+ 
++gethostname(#socket_state{socket = Socket} = State) ->
++    ?DEBUG("gethostname ~p~n", [Socket]),
++
++    case sockname(State) of
++	{ok, {Addr, _Port}} ->
++	    case inet:gethostbyaddr(Addr) of
++		{ok, HostEnt} when is_record(HostEnt, hostent) ->
++		    ?DEBUG("gethostname result ~p~n",
++			   [HostEnt#hostent.h_name]),
++		    {ok, HostEnt#hostent.h_name};
++		{error, _Reason} = E ->
++		    E
++	    end;
++	{error, _Reason} = E ->
++	    E
++    end.
++
+ %%====================================================================
+ %% Internal functions
+ %%====================================================================
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch b/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
deleted file mode 100644
index c6a4284..0000000
--- a/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From ff5baa27c9bb0175e4bc4ae3862d8e639234a701 Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Sat, 12 Jun 2010 16:12:11 +0400
-Subject: [PATCH 07/10] Use versioned directory for storing docs
-
-It's also a good idea to store doc-files under the versioned directory.
-This may greatly simplify parallel installation of different versions of
-ejabbed.
-
-Note that this may be done in the other way - by changing proper field
-in the AC_INIT macro, see the explanation of the field 'tarname':
-
-http://www.gnu.org/software/autoconf/manual/html_node/Initializing-configure.html
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/Makefile.in |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index 64ed856..43b5e1f 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -98,7 +98,7 @@ SBINDIR = $(DESTDIR)@sbindir@
- EJABBERDDIR = $(DESTDIR)@libdir@/ejabberd
- 
- # /share/doc/ejabberd
--PACKAGE_TARNAME = @PACKAGE_TARNAME@
-+PACKAGE_TARNAME = @PACKAGE_TARNAME@-@PACKAGE_VERSION@
- datarootdir = @datarootdir@
- DOCDIR = $(DESTDIR)@docdir@
- 
--- 
-1.7.10.1
-

diff --git a/ejabberd-0008-Disable-INET_DIST_INTERFACE-by-default.patch b/ejabberd-0008-Disable-INET_DIST_INTERFACE-by-default.patch
new file mode 100644
index 0000000..a9b0b5b
--- /dev/null
+++ b/ejabberd-0008-Disable-INET_DIST_INTERFACE-by-default.patch
@@ -0,0 +1,26 @@
+From a6a1ee5a9d5a8400415a96456dbb79f4341c3410 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Sat, 18 Jun 2011 23:24:28 +0400
+Subject: [PATCH 8/8] Disable INET_DIST_INTERFACE by default
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/ejabberdctl.cfg.example |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/ejabberdctl.cfg.example b/src/ejabberdctl.cfg.example
+index b72058a..5ee43cf 100644
+--- a/src/ejabberdctl.cfg.example
++++ b/src/ejabberdctl.cfg.example
+@@ -59,7 +59,7 @@
+ #
+ # Default: {127,0,0,1}
+ #
+-INET_DIST_INTERFACE={127,0,0,1}
++#INET_DIST_INTERFACE={127,0,0,1}
+ 
+ #.
+ #' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections
+-- 
+1.7.10.4
+

diff --git a/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch b/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
deleted file mode 100644
index 67b0717..0000000
--- a/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
+++ /dev/null
@@ -1,505 +0,0 @@
-From b1dbc5224d000b62d405f4e82b6dd646f54997a1 Mon Sep 17 00:00:00 2001
-From: Badlop <badlop@process-one.net>
-Date: Thu, 15 Apr 2010 17:20:16 +0200
-Subject: [PATCH 08/10] Support SASL GSSAPI authentication (thanks to Mikael
- Magnusson)(EJAB-831)
-
----
- src/cyrsasl.erl           |   71 +++++++++++++++----
- src/cyrsasl.hrl           |   15 ++++
- src/cyrsasl_anonymous.erl |    6 +-
- src/cyrsasl_digest.erl    |    6 +-
- src/cyrsasl_gssapi.erl    |  166 +++++++++++++++++++++++++++++++++++++++++++++
- src/cyrsasl_plain.erl     |    6 +-
- src/cyrsasl_scram.erl     |    5 +-
- src/ejabberd_c2s.erl      |   12 +++-
- src/ejabberd_socket.erl   |   19 ++++++
- 9 files changed, 281 insertions(+), 25 deletions(-)
- create mode 100644 src/cyrsasl.hrl
- create mode 100644 src/cyrsasl_gssapi.erl
-
-diff --git a/src/cyrsasl.erl b/src/cyrsasl.erl
-index 23f1721..5ff0916 100644
---- a/src/cyrsasl.erl
-+++ b/src/cyrsasl.erl
-@@ -30,21 +30,36 @@
- -export([start/0,
- 	 register_mechanism/3,
- 	 listmech/1,
--	 server_new/7,
-+	 server_new/8,
- 	 server_start/3,
- 	 server_step/2]).
- 
-+-include("cyrsasl.hrl").
- -include("ejabberd.hrl").
- 
-+%% @type saslmechanism() = {sasl_mechanism, Mechanism, Module, Require_Plain}
-+%%     Mechanism = string()
-+%%     Module = atom()
-+%%     Require_Plain = bool().
-+%% Registry entry of a supported SASL mechanism.
-+
- -record(sasl_mechanism, {mechanism, module, password_type}).
---record(sasl_state, {service, myname, realm,
--		     get_password, check_password, check_password_digest,
--		     mech_mod, mech_state}).
-+
-+%% @type saslstate() = {sasl_state, Service, Myname, Mech_Mod, Mech_State, Params}
-+%%     Service = string()
-+%%     Myname = string()
-+%%     Mech_Mod = atom()
-+%%     Mech_State = term().
-+%%     Params = term().
-+%% State of this process.
-+
-+-record(sasl_state, {service, myname,
-+		     mech_mod, mech_state, params}).
- 
- -export([behaviour_info/1]).
- 
- behaviour_info(callbacks) ->
--    [{mech_new, 4}, {mech_step, 2}];
-+    [{mech_new, 1}, {mech_step, 2}];
- behaviour_info(_Other) ->
-     undefined.
- 
-@@ -56,8 +71,30 @@ start() ->
-     cyrsasl_digest:start([]),
-     cyrsasl_scram:start([]),
-     cyrsasl_anonymous:start([]),
-+    maybe_try_start_gssapi(),
-     ok.
- 
-+maybe_try_start_gssapi() ->
-+    case os:getenv("KRB5_KTNAME") of
-+        false ->
-+	    ok;
-+        _String ->
-+	    try_start_gssapi()
-+    end.
-+
-+try_start_gssapi() ->
-+    case code:load_file(esasl) of
-+	{module, _Module} ->
-+	    cyrsasl_gssapi:start([]);
-+	{error, What} ->
-+	    ?ERROR_MSG("Support for GSSAPI not started because esasl.beam was not found: ~p", [What])
-+    end.
-+
-+%% @spec (Mechanism, Module, Require_Plain) -> true
-+%%     Mechanism = string()
-+%%     Module = atom()
-+%%     Require_Plain = bool()
-+
- register_mechanism(Mechanism, Module, PasswordType) ->
-     ets:insert(sasl_mechanism,
- 	       #sasl_mechanism{mechanism = Mechanism,
-@@ -119,24 +156,28 @@ listmech(Host) ->
-     filter_anonymous(Host, Mechs).
- 
- server_new(Service, ServerFQDN, UserRealm, _SecFlags,
--	   GetPassword, CheckPassword, CheckPasswordDigest) ->
-+	   GetPassword, CheckPassword, CheckPasswordDigest, Socket) ->
-+    Params = #sasl_params{
-+      host = ServerFQDN,
-+      realm = UserRealm,
-+      get_password = GetPassword,
-+      check_password = CheckPassword,
-+      check_password_digest= CheckPasswordDigest,
-+      socket = Socket
-+     },
-+
-     #sasl_state{service = Service,
- 		myname = ServerFQDN,
--		realm = UserRealm,
--		get_password = GetPassword,
--		check_password = CheckPassword,
--		check_password_digest= CheckPasswordDigest}.
-+		params = Params}.
-+
- 
- server_start(State, Mech, ClientIn) ->
-     case lists:member(Mech, listmech(State#sasl_state.myname)) of
- 	true ->
- 	    case ets:lookup(sasl_mechanism, Mech) of
- 		[#sasl_mechanism{module = Module}] ->
--		    {ok, MechState} = Module:mech_new(
--					State#sasl_state.myname,
--					State#sasl_state.get_password,
--					State#sasl_state.check_password,
--					State#sasl_state.check_password_digest),
-+		    {ok, MechState} =
-+			Module:mech_new(State#sasl_state.params),
- 		    server_step(State#sasl_state{mech_mod = Module,
- 						 mech_state = MechState},
- 				ClientIn);
-diff --git a/src/cyrsasl.hrl b/src/cyrsasl.hrl
-new file mode 100644
-index 0000000..b4cc3e3
---- /dev/null
-+++ b/src/cyrsasl.hrl
-@@ -0,0 +1,15 @@
-+%% @type saslparams() = {sasl_params, Host, Realm, GetPassword, CheckPassword, CheckPasswordDigest}
-+%%     Host = string()
-+%%     Realm = string()
-+%%     GetPassword = function()
-+%%     CheckPassword = function()
-+%%     CheckPasswordDigest = any().
-+%% Parameters for SASL.
-+
-+-record(sasl_params, {
-+	  host,
-+	  realm,
-+	  get_password,
-+	  check_password,
-+	  check_password_digest,
-+	  socket}).
-diff --git a/src/cyrsasl_anonymous.erl b/src/cyrsasl_anonymous.erl
-index 25fcd66..aeb09ea 100644
---- a/src/cyrsasl_anonymous.erl
-+++ b/src/cyrsasl_anonymous.erl
-@@ -27,7 +27,9 @@
- 
- -module(cyrsasl_anonymous).
- 
---export([start/1, stop/0, mech_new/4, mech_step/2]).
-+-export([start/1, stop/0, mech_new/1, mech_step/2]).
-+
-+-include("cyrsasl.hrl").
- 
- -behaviour(cyrsasl).
- 
-@@ -40,7 +42,7 @@ start(_Opts) ->
- stop() ->
-     ok.
- 
--mech_new(Host, _GetPassword, _CheckPassword, _CheckPasswordDigest) ->
-+mech_new(#sasl_params{host=Host}) ->
-     {ok, #state{server = Host}}.
- 
- mech_step(State, _ClientIn) ->
-diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
-index 134a86d..d904801 100644
---- a/src/cyrsasl_digest.erl
-+++ b/src/cyrsasl_digest.erl
-@@ -29,10 +29,11 @@
- 
- -export([start/1,
- 	 stop/0,
--	 mech_new/4,
-+	 mech_new/1,
- 	 mech_step/2]).
- 
- -include("ejabberd.hrl").
-+-include("cyrsasl.hrl").
- 
- -behaviour(cyrsasl).
- 
-@@ -47,7 +48,8 @@ start(_Opts) ->
- stop() ->
-     ok.
- 
--mech_new(Host, GetPassword, _CheckPassword, CheckPasswordDigest) ->
-+mech_new(#sasl_params{host=Host, get_password=GetPassword,
-+		      check_password_digest=CheckPasswordDigest}) ->
-     {ok, #state{step = 1,
- 		nonce = randoms:get_string(),
- 		host = Host,
-diff --git a/src/cyrsasl_gssapi.erl b/src/cyrsasl_gssapi.erl
-new file mode 100644
-index 0000000..11d9955
---- /dev/null
-+++ b/src/cyrsasl_gssapi.erl
-@@ -0,0 +1,166 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : cyrsasl_gssapi.erl
-+%%% Author  : Mikael Magnusson <mikma@users.sourceforge.net>
-+%%% Purpose : GSSAPI SASL mechanism
-+%%% Created : 1 June 2007 by Mikael Magnusson <mikma@users.sourceforge.net>
-+%%% Id      : $Id: $
-+%%%----------------------------------------------------------------------
-+%%%
-+%%% Copyright (C) 2007-2009  Mikael Magnusson <mikma@users.sourceforge.net>
-+%%%
-+%%% Permission is hereby granted, free of charge, to any person
-+%%% obtaining a copy of this software and associated documentation
-+%%% files (the "Software"), to deal in the Software without
-+%%% restriction, including without limitation the rights to use, copy,
-+%%% modify, merge, publish, distribute, sublicense, and/or sell copies
-+%%% of the Software, and to permit persons to whom the Software is
-+%%% furnished to do so, subject to the following conditions:
-+%%%
-+%%% The above copyright notice and this permission notice shall be
-+%%% included in all copies or substantial portions of the Software.
-+%%%
-+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-+%%% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-+%%% MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-+%%% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-+%%% BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-+%%% ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-+%%% CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-+%%% SOFTWARE.
-+%%%
-+
-+%%%
-+%%% configuration options:
-+%%% {sasl_realm, "<Kerberos realm>"}.
-+%%%
-+%%% environment variables:
-+%%% KRB5_KTNAME
-+%%%
-+
-+-module(cyrsasl_gssapi).
-+-author('mikma@users.sourceforge.net').
-+-vsn('$Revision: $ ').
-+
-+-export([start/1,
-+	 stop/0,
-+	 mech_new/1,
-+	 mech_step/2]).
-+
-+-include("ejabberd.hrl").
-+-include("cyrsasl.hrl").
-+
-+-behaviour(cyrsasl).
-+
-+-define(SERVER, ?MODULE).
-+-define(SERVICE, "xmpp").
-+
-+-record(state, {sasl,
-+		needsmore=true,
-+		step=0,
-+		host,
-+		realm,
-+		authid,
-+		authzid,
-+		authrealm,
-+		error}).
-+
-+start(_Opts) ->
-+    ChildSpec =
-+	{?SERVER,
-+	 {esasl, start_link, [{local, ?SERVER}]},
-+	 transient,
-+	 1000,
-+	 worker,
-+	 [esasl]},
-+
-+    case supervisor:start_child(ejabberd_sup, ChildSpec) of
-+	{ok, _Pid} ->
-+	    cyrsasl:register_mechanism("GSSAPI", ?MODULE, false);
-+	{error, Error} = E ->
-+	    ?ERROR_MSG("esasl failed: ~p", [Error]),
-+	    E
-+    end.
-+
-+
-+stop() ->
-+    catch esasl:stop(?SERVER),
-+    supervisor:terminate_child(ejabberd_sup, ?SERVER),
-+    supervisor:delete_child(ejabberd_sup, ?SERVER).
-+
-+mech_new(#sasl_params{host=Host, realm=Realm, socket=Socket}) ->
-+    case ejabberd_socket:gethostname(Socket) of
-+	{ok, FQDN} ->
-+	    ?DEBUG("mech_new ~p ~p ~p~n", [Host, Realm, FQDN]),
-+	    case esasl:server_start(?SERVER, "GSSAPI", ?SERVICE, FQDN) of
-+		{ok, Sasl} ->
-+		    {ok, #state{sasl=Sasl,host=Host,realm=Realm}};
-+		{error, {gsasl_error, Error}} ->
-+		    {ok, Str} = esasl:str_error(?SERVER, Error),
-+		    ?DEBUG("esasl error: ~p", [Str]),
-+		    {ok, #state{needsmore=error,error="internal-server-error"}};
-+		{error, Error} ->
-+		    ?DEBUG("esasl error: ~p", [Error]),
-+		    {ok, #state{needsmore=error,error="internal-server-error"}}
-+	    end;
-+	{error, Error} ->
-+	    ?DEBUG("gethostname error: ~p", [Error]),
-+	    {ok, #state{needsmore=error,error="internal-server-error"}}
-+    end.
-+
-+mech_step(State, ClientIn) when is_list(ClientIn) ->
-+    catch do_step(State, ClientIn).
-+
-+do_step(#state{needsmore=error,error=Error}=_State, _) ->
-+    {error, Error};
-+do_step(#state{needsmore=false}=State, _) ->
-+    check_user(State);
-+do_step(#state{needsmore=true,sasl=Sasl,step=Step}=State, ClientIn) ->
-+    ?DEBUG("mech_step~n", []),
-+    case esasl:step(Sasl, list_to_binary(ClientIn)) of
-+	{ok, RspAuth} ->
-+	    ?DEBUG("ok~n", []),
-+	    {ok, Display_name} = esasl:property_get(Sasl, gssapi_display_name),
-+	    {ok, Authzid} = esasl:property_get(Sasl, authzid),
-+	    {Authid, [$@ | Auth_realm]} =
-+		lists:splitwith(fun(E)->E =/= $@ end, Display_name),
-+	    State1 = State#state{authid=Authid,
-+				 authzid=Authzid,
-+				 authrealm=Auth_realm},
-+	    handle_step_ok(State1, binary_to_list(RspAuth));
-+	{needsmore, RspAuth} ->
-+	    ?DEBUG("needsmore~n", []),
-+	    if (Step > 0) and (ClientIn =:= []) and (RspAuth =:= <<>>) ->
-+		    {error, "not-authorized"};
-+		true ->
-+		    {continue, binary_to_list(RspAuth),
-+		     State#state{step=Step+1}}
-+	    end;
-+	{error, _} ->
-+	    {error, "not-authorized"}
-+    end.
-+
-+handle_step_ok(State, []) ->
-+    check_user(State);
-+handle_step_ok(#state{step=Step}=State, RspAuth) ->
-+    ?DEBUG("continue~n", []),
-+    {continue, RspAuth, State#state{needsmore=false,step=Step+1}}.
-+
-+check_user(#state{authid=Authid,authzid=Authzid,
-+		  authrealm=Auth_realm,host=Host,realm=Realm}) ->
-+    if Realm =/= Auth_realm ->
-+	    ?DEBUG("bad realm ~p (expected ~p)~n",[Auth_realm, Realm]),
-+	    throw({error, "not-authorized"});
-+       true ->
-+	    ok
-+    end,
-+
-+    case ejabberd_auth:is_user_exists(Authid, Host) of
-+	false ->
-+	    ?DEBUG("bad user ~p~n",[Authid]),
-+	    throw({error, "not-authorized"});
-+	true ->
-+	    ok
-+    end,
-+
-+    ?DEBUG("GSSAPI authenticated ~p ~p~n", [Authid, Authzid]),
-+    {ok, [{username, Authid}, {authzid, Authzid}]}.
-diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
-index a0804fd..14b3c19 100644
---- a/src/cyrsasl_plain.erl
-+++ b/src/cyrsasl_plain.erl
-@@ -27,7 +27,9 @@
- -module(cyrsasl_plain).
- -author('alexey@process-one.net').
- 
---export([start/1, stop/0, mech_new/4, mech_step/2, parse/1]).
-+-export([start/1, stop/0, mech_new/1, mech_step/2, parse/1]).
-+
-+-include("cyrsasl.hrl").
- 
- -behaviour(cyrsasl).
- 
-@@ -40,7 +42,7 @@ start(_Opts) ->
- stop() ->
-     ok.
- 
--mech_new(_Host, _GetPassword, CheckPassword, _CheckPasswordDigest) ->
-+mech_new(#sasl_params{check_password = CheckPassword}) ->
-     {ok, #state{check_password = CheckPassword}}.
- 
- mech_step(State, ClientIn) ->
-diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl
-index 74977bd..c3bf5c6 100644
---- a/src/cyrsasl_scram.erl
-+++ b/src/cyrsasl_scram.erl
-@@ -29,10 +29,11 @@
- 
- -export([start/1,
- 	 stop/0,
--	 mech_new/4,
-+	 mech_new/1,
- 	 mech_step/2]).
- 
- -include("ejabberd.hrl").
-+-include("cyrsasl.hrl").
- 
- -behaviour(cyrsasl).
- 
-@@ -48,7 +49,7 @@ start(_Opts) ->
- stop() ->
-     ok.
- 
--mech_new(_Host, GetPassword, _CheckPassword, _CheckPasswordDigest) ->
-+mech_new(#sasl_params{get_password=GetPassword}) ->
-     {ok, #state{step = 2, get_password = GetPassword}}.
- 
- mech_step(#state{step = 2} = State, ClientIn) ->
-diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
-index 552aa6d..038d3c5 100644
---- a/src/ejabberd_c2s.erl
-+++ b/src/ejabberd_c2s.erl
-@@ -305,9 +305,16 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
- 			    send_header(StateData, Server, "1.0", DefaultLang),
- 			    case StateData#state.authenticated of
- 				false ->
-+				    Realm =
-+					case ejabberd_config:get_local_option({sasl_realm, Server}) of
-+					    undefined ->
-+						"";
-+					    Realm0 ->
-+						Realm0
-+					end,
- 				    SASLState =
- 					cyrsasl:server_new(
--					  "jabber", Server, "", [],
-+					  "jabber", Server, Realm, [],
- 					  fun(U) ->
- 						  ejabberd_auth:get_password_with_authmodule(
- 						    U, Server)
-@@ -319,7 +326,8 @@ wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
- 					  fun(U, P, D, DG) ->
- 						  ejabberd_auth:check_password_with_authmodule(
- 						    U, Server, P, D, DG)
--					  end),
-+					  end,
-+					  StateData#state.socket),
- 				    Mechs = lists:map(
- 					      fun(S) ->
- 						      {xmlelement, "mechanism", [],
-diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl
-index 254751b..3509018 100644
---- a/src/ejabberd_socket.erl
-+++ b/src/ejabberd_socket.erl
-@@ -44,9 +44,11 @@
- 	 get_peer_certificate/1,
- 	 get_verify_result/1,
- 	 close/1,
-+	 gethostname/1,
- 	 sockname/1, peername/1]).
- 
- -include("ejabberd.hrl").
-+-include_lib("kernel/include/inet.hrl").
- 
- -record(socket_state, {sockmod, socket, receiver}).
- 
-@@ -228,6 +230,23 @@ peername(#socket_state{sockmod = SockMod, socket = Socket}) ->
- 	    SockMod:peername(Socket)
-     end.
- 
-+gethostname(#socket_state{socket = Socket} = State) ->
-+    ?DEBUG("gethostname ~p~n", [Socket]),
-+
-+    case sockname(State) of
-+	{ok, {Addr, _Port}} ->
-+	    case inet:gethostbyaddr(Addr) of
-+		{ok, HostEnt} when is_record(HostEnt, hostent) ->
-+		    ?DEBUG("gethostname result ~p~n",
-+			   [HostEnt#hostent.h_name]),
-+		    {ok, HostEnt#hostent.h_name};
-+		{error, _Reason} = E ->
-+		    E
-+	    end;
-+	{error, _Reason} = E ->
-+	    E
-+    end.
-+
- %%====================================================================
- %% Internal functions
- %%====================================================================
--- 
-1.7.10.1
-

diff --git a/ejabberd-0009-Added-old-modules-for-Active-Directory.patch b/ejabberd-0009-Added-old-modules-for-Active-Directory.patch
deleted file mode 100644
index ab3516b..0000000
--- a/ejabberd-0009-Added-old-modules-for-Active-Directory.patch
+++ /dev/null
@@ -1,1266 +0,0 @@
-From d5880591ff8c318584df55cbb22d91eb42aa80dd Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Tue, 14 Dec 2010 18:28:44 +0300
-Subject: [PATCH 09/10] Added old modules for Active Directory
-
-These modules were extracted from a patch found on
-http://realloc.spb.ru/share/ejabberdad.html
-
-Please, note that these are unmaintained and will be removed very
-soon (users should use *_ldap.erl instead).
----
- src/ejabberd_auth_ad.erl     |  167 +++++++++++
- src/mod_shared_roster_ad.erl |  428 ++++++++++++++++++++++++++++
- src/mod_vcard_ad.erl         |  631 ++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 1226 insertions(+)
- create mode 100644 src/ejabberd_auth_ad.erl
- create mode 100644 src/mod_shared_roster_ad.erl
- create mode 100644 src/mod_vcard_ad.erl
-
-diff --git a/src/ejabberd_auth_ad.erl b/src/ejabberd_auth_ad.erl
-new file mode 100644
-index 0000000..5e9e452
---- /dev/null
-+++ b/src/ejabberd_auth_ad.erl
-@@ -0,0 +1,167 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : ejabberd_auth_ad.erl
-+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
-+%%% Author  : Alex Gorbachenko <agent_007@immo.ru>
-+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
-+%%% Purpose : Authentification via Active Directory
-+%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@sevcom.net>
-+%%% Id      : $Id: ejabberd_auth_ad.erl 386 2005-12-20 10:06:37Z agent_007 $
-+%%%----------------------------------------------------------------------
-+
-+-module(ejabberd_auth_ad).
-+-author('alexey@sevcom.net').
-+-author('agent_007@immo.ru').
-+-author('realloc@realloc.spb.ru').
-+-vsn('$Revision: 386 $ ').
-+
-+%% External exports
-+-export([start/1,
-+	 set_password/3,
-+	 check_password/3,
-+	 check_password/5,
-+	 try_register/3,
-+	 dirty_get_registered_users/0,
-+	 get_vh_registered_users/1,
-+	 get_password/2,
-+	 get_password_s/2,
-+	 is_user_exists/2,
-+	 remove_user/2,
-+	 remove_user/3,
-+	 plain_password_required/0
-+	]).
-+
-+-include("ejabberd.hrl").
-+-include("eldap/eldap.hrl").
-+
-+%%%----------------------------------------------------------------------
-+%%% API
-+%%%----------------------------------------------------------------------
-+start(Host) ->
-+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
-+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
-+    Password = ejabberd_config:get_local_option({ad_password, Host}),
-+    eldap:start_link(get_eldap_id(Host, ejabberd),
-+		     LDAPServers, 389, RootDN, Password),
-+    eldap:start_link(get_eldap_id(Host, ejabberd_bind),
-+		     LDAPServers, 389, RootDN, Password),
-+    ok.
-+
-+plain_password_required() ->
-+    true.
-+
-+check_password(User, Server, Password) ->
-+    case find_user_dn(User, Server) of
-+	false ->
-+	    false;
-+	DN ->
-+	    LServer = jlib:nameprep(Server),
-+	    case eldap:bind(get_eldap_id(LServer, ejabberd_bind),
-+			    DN, Password) of
-+		ok ->
-+		    true;
-+		_ ->
-+		    false
-+	    end
-+    end.
-+
-+check_password(User, Server, Password, _StreamID, _Digest) ->
-+    check_password(User, Server, Password).
-+
-+set_password(_User, _Server, _Password) ->
-+    {error, not_allowed}.
-+
-+try_register(_User, _Server, _Password) ->
-+    {error, not_allowed}.
-+
-+dirty_get_registered_users() ->
-+    get_vh_registered_users(?MYNAME).
-+
-+get_vh_registered_users(Server) ->
-+    LServer = jlib:nameprep(Server),
-+    Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
-+%    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
-+    FilterPerson = eldap:equalityMatch("objectCategory", "person"),
-+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
-+    FilterHidden = eldap:equalityMatch("description", "hidden"),
-+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
-+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
-+    FilterDef = eldap:present(Attr),
-+    Filter = eldap:'and'([
-+			  FilterDef,
-+			  FilterPerson,
-+%			  FilterGroup,
-+			  eldap:'not'(FilterComp),
-+			  eldap:'not'(FilterHidden),
-+			  eldap:'not'(FilterLive)]),
-+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
-+    case eldap:search(get_eldap_id(LServer, ejabberd),
-+		      [{base, Base},
-+		       {filter, Filter},
-+		       {attributes, [Attr]}]) of
-+	#eldap_search_result{entries = Es} ->
-+	    lists:flatmap(
-+	      fun(E) ->
-+		      case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
-+			  {value, {_, [U]}} ->
-+			      case jlib:nodeprep(U) of
-+				  error ->
-+				      [];
-+				  LU ->
-+				      [{LU, LServer}]
-+			      end;
-+			  _ ->
-+			      []
-+		      end
-+	      end, Es);
-+	_ ->
-+	    []
-+    end.
-+
-+get_password(_User, _Server) ->
-+    false.
-+
-+get_password_s(_User, _Server) ->
-+    "".
-+
-+is_user_exists(User, Server) ->
-+    case find_user_dn(User, Server) of
-+	false ->
-+	    false;
-+	_DN ->
-+	    true
-+    end.
-+
-+remove_user(_User, _Server) ->
-+    {error, not_allowed}.
-+
-+remove_user(_User, _Server, _Password) ->
-+    not_allowed.
-+
-+
-+%%%----------------------------------------------------------------------
-+%%% Internal functions
-+%%%----------------------------------------------------------------------
-+
-+find_user_dn(User, Server) ->
-+    LServer = jlib:nameprep(Server),
-+    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
-+    Attr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
-+    FilterAttr = eldap:equalityMatch(Attr, User),
-+    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
-+    Filter = eldap:'and'([
-+                          FilterAttr,
-+                          FilterGroup
-+                          ]),
-+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
-+    case eldap:search(get_eldap_id(LServer, ejabberd),
-+		      [{base, Base},
-+		       {filter, Filter},
-+		       {attributes, []}]) of
-+	#eldap_search_result{entries = [E | _]} ->
-+	    E#eldap_entry.object_name;
-+	_ ->
-+	    false
-+    end.
-+
-+get_eldap_id(Host, Name) ->
-+    atom_to_list(gen_mod:get_module_proc(Host, Name)).
-diff --git a/src/mod_shared_roster_ad.erl b/src/mod_shared_roster_ad.erl
-new file mode 100644
-index 0000000..8dc09f1
---- /dev/null
-+++ b/src/mod_shared_roster_ad.erl
-@@ -0,0 +1,428 @@
-+%%%----------------------------------------------------------------------
-+%%% File    : mod_shared_roster.erl
-+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
-+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
-+%%% Purpose : Shared roster management
-+%%% Created :  5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
-+%%% Id      : $Id: mod_shared_roster_ad.erl,v 1.1 2006/06/23 12:40:03 jcollie Exp $
-+%%%----------------------------------------------------------------------
-+
-+-module(mod_shared_roster_ad).
-+-author('alexey@sevcom.net').
-+-author('realloc@realloc.spb.ru').
-+-vsn('$Revision: 1.1 $ ').
-+
-+-behaviour(gen_mod).
-+
-+-export([start/2, stop/1,
-+	 get_user_roster/2,
-+	 get_subscription_lists/3,
-+	 get_jid_info/4,
-+	 in_subscription/5,
-+	 out_subscription/4,
-+	 list_groups/1,
-+	 create_group/2,
-+	 create_group/3,
-+	 delete_group/2,
-+	 get_group_opts/2,
-+	 set_group_opts/3,
-+	 get_group_users/2,
-+	 get_group_explicit_users/2,
-+	 add_user_to_group/3,
-+	 remove_user_from_group/3]).
-+
-+-include("ejabberd.hrl").
-+-include("jlib.hrl").
-+-include("mod_roster.hrl").
-+-include("eldap/eldap.hrl").
-+
-+-record(sr_group, {group_host, opts}).
-+-record(sr_user, {us, group_host}).
-+
-+start(Host, _Opts) ->
-+    mnesia:create_table(sr_group,
-+			[{disc_copies, [node()]},
-+			 {attributes, record_info(fields, sr_group)}]),
-+    mnesia:create_table(sr_user,
-+			[{disc_copies, [node()]},
-+			 {type, bag},
-+			 {attributes, record_info(fields, sr_user)}]),
-+    mnesia:add_table_index(sr_user, group_host),
-+    ejabberd_hooks:add(roster_get, Host,
-+		       ?MODULE, get_user_roster, 70),
-+    ejabberd_hooks:add(roster_in_subscription, Host,
-+        	       ?MODULE, in_subscription, 30),
-+    ejabberd_hooks:add(roster_out_subscription, Host,
-+        	       ?MODULE, out_subscription, 30),
-+    ejabberd_hooks:add(roster_get_subscription_lists, Host,
-+		       ?MODULE, get_subscription_lists, 70),
-+    ejabberd_hooks:add(roster_get_jid_info, Host,
-+        	       ?MODULE, get_jid_info, 70),
-+    
-+    %ejabberd_hooks:add(remove_user, Host,
-+    %    	       ?MODULE, remove_user, 50),
-+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
-+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
-+    Password = ejabberd_config:get_local_option({ad_password, Host}),
-+    eldap:start_link("mod_shared_roster_ad", LDAPServers, 389, RootDN, Password).
-+   
-+
-+  
-+stop(Host) ->
-+    ejabberd_hooks:delete(roster_get, Host,
-+			  ?MODULE, get_user_roster, 70),
-+    ejabberd_hooks:delete(roster_in_subscription, Host,
-+        		  ?MODULE, in_subscription, 30),
-+    ejabberd_hooks:delete(roster_out_subscription, Host,
-+        		  ?MODULE, out_subscription, 30),
-+    ejabberd_hooks:delete(roster_get_subscription_lists, Host,
-+        		  ?MODULE, get_subscription_lists, 70),
-+    ejabberd_hooks:delete(roster_get_jid_info, Host,
-+        		  ?MODULE, get_jid_info, 70).
-+    %ejabberd_hooks:delete(remove_user, Host,
-+    %    		  ?MODULE, remove_user, 50),
-+
-+
-+get_user_roster(Items, US) ->
-+    {U, S} = US,
-+    DisplayedGroups = get_user_displayed_groups_ad(US),
-+    SRUsers = 
-+	lists:foldl(
-+	  fun(Group, Acc1) ->
-+		  lists:foldl(
-+		    fun(User, Acc2) ->
-+			    dict:append(User, Group, Acc2)
-+		    end, Acc1, get_group_users_ad(S, Group))
-+	  end, dict:new(), DisplayedGroups),
-+    {NewItems1, SRUsersRest} =
-+	lists:mapfoldl(
-+	  fun(Item, SRUsers1) ->
-+		  {_, _, {U1, S1, _}} = Item#roster.usj,
-+		  US1 = {U1, S1},
-+		  case dict:find(US1, SRUsers1) of
-+		      {ok, _GroupNames} ->
-+			  {Item#roster{subscription = both, ask = none},
-+			   dict:erase(US1, SRUsers1)};
-+		      error ->
-+			  {Item, SRUsers1}
-+		  end
-+	  end, SRUsers, Items),
-+    SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
-+		       us = US,
-+		       jid = {U1, S1, ""},
-+		       name = get_user_fn(U1,S1),
-+		       subscription = both,
-+		       ask = none,
-+		       groups = GroupNames} ||
-+		  {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
-+    SRItems ++ NewItems1.
-+
-+get_subscription_lists({F, T}, User, Server) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
-+    US = {LUser, LServer},
-+    DisplayedGroups = get_user_displayed_groups_ad(US),
-+    SRUsers =
-+	lists:usort(
-+	  lists:flatmap(
-+	    fun(Group) ->
-+		    get_group_users_ad(LServer, Group)
-+	    end, DisplayedGroups)),
-+    SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
-+    {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
-+
-+get_jid_info({Subscription, Groups}, User, Server, JID) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
-+    US = {LUser, LServer},
-+    {U1, S1, _} = jlib:jid_tolower(JID),
-+    US1 = {U1, S1},
-+    DisplayedGroups = get_user_displayed_groups_ad(US),
-+    SRUsers = 
-+	lists:foldl(
-+	  fun(Group, Acc1) ->
-+		  lists:foldl(
-+		    fun(User1, Acc2) ->
-+			    dict:append(
-+			      User1, Group, Acc2)
-+		    end, Acc1, get_group_users_ad(LServer, Group))
-+	  end, dict:new(), DisplayedGroups),
-+    case dict:find(US1, SRUsers) of
-+	{ok, GroupNames} ->
-+	    NewGroups = if
-+			    Groups == [] -> GroupNames;
-+			    true -> Groups
-+			end,
-+	    {both, NewGroups};
-+	error ->
-+	    {Subscription, Groups}
-+    end.
-+
-+in_subscription(Acc, User, Server, JID, Type) ->
-+    process_subscription(in, User, Server, JID, Type, Acc).
-+
-+out_subscription(User, Server, JID, Type) ->
-+    process_subscription(out, User, Server, JID, Type, false).
-+
-+process_subscription(Direction, User, Server, JID, _Type, Acc) ->
-+    LUser = jlib:nodeprep(User),
-+    LServer = jlib:nameprep(Server),
-+    US = {LUser, LServer},
-+    {U1, S1, _} = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
-+    US1 = {U1, S1},
-+    DisplayedGroups = get_user_displayed_groups_ad(US),
-+    SRUsers =
-+	lists:usort(
-+	  lists:flatmap(
-+	    fun(Group) ->
-+		    get_group_users_ad(LServer, Group)
-+	    end, DisplayedGroups)),
-+    case lists:member(US1, SRUsers) of
-+	true ->
-+	    case Direction of
-+		in ->
-+		    {stop, false};
-+		out ->
-+		    stop
-+	    end;
-+	false ->
-+	    Acc
-+    end.
-+
-+list_groups(Host) ->
-+    get_user_displayed_groups_ad({"",Host}).
-+
-+
-+create_group(Host, Group) ->
-+    create_group(Host, Group, []).
-+
-+create_group(Host, Group, Opts) ->
-+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
-+    F = fun() ->
-+		mnesia:write(R)
-+	end,
-+    mnesia:transaction(F).
-+
-+delete_group(Host, Group) ->
-+    F = fun() ->
-+		mnesia:delete({sr_group, {Group, Host}})
-+	end,
-+    mnesia:transaction(F).
-+
-+get_group_opts(Host, Group) ->
-+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
-+	[#sr_group{opts = Opts}] ->
-+	    Opts;
-+	_ ->
-+	    error
-+    end.
-+
-+set_group_opts(Host, Group, Opts) ->
-+    R = #sr_group{group_host = {Group, Host}, opts = Opts},
-+    F = fun() ->
-+		mnesia:write(R)
-+	end,
-+    mnesia:transaction(F).
-+
-+
-+
-+get_user_groups(US) ->
-+    Host = element(2, US),
-+    case catch mnesia:dirty_read(sr_user, US) of
-+	Rs when is_list(Rs) ->
-+	    [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
-+	_ ->
-+	    []
-+    end ++ get_all_users_groups(Host).
-+
-+is_group_enabled(Host, Group) ->
-+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
-+	[#sr_group{opts = Opts}] ->
-+	    not lists:member(disabled, Opts);
-+	_ ->
-+	    false
-+    end.
-+
-+get_group_opt(Host, Group, Opt, Default) ->
-+    case catch mnesia:dirty_read(sr_group, {Group, Host}) of
-+	[#sr_group{opts = Opts}] ->
-+	    case lists:keysearch(Opt, 1, Opts) of
-+		{value, {_, Val}} ->
-+		    Val;
-+		false ->
-+		    Default
-+	    end;
-+	_ ->
-+	    false
-+    end.
-+
-+get_group_users(Host, Group) ->
-+    case get_group_opt(Host, Group, all_users, false) of
-+	true ->
-+	    ejabberd_auth:get_vh_registered_users(Host);
-+	false ->
-+	    []
-+    end ++ get_group_explicit_users(Host, Group).
-+
-+get_group_explicit_users(Host, Group) ->
-+    case catch mnesia:dirty_index_read(
-+		 sr_user, {Group, Host}, #sr_user.group_host) of
-+	Rs when is_list(Rs) ->
-+	    [R#sr_user.us || R <- Rs];
-+	_ ->
-+	    []
-+    end.
-+
-+get_group_name(Host, Group) ->
-+    get_group_opt(Host, Group, name, Group).
-+
-+get_all_users_groups(Host) ->
-+    lists:filter(
-+      fun(Group) -> get_group_opt(Host, Group, all_users, false) end,
-+      list_groups(Host)).
-+
-+get_user_displayed_groups(US) ->
-+    Host = element(2, US),
-+    DisplayedGroups1 =
-+	lists:usort(
-+	  lists:flatmap(
-+	    fun(Group) ->
-+		    case is_group_enabled(Host, Group) of
-+			true ->
-+			    get_group_opt(Host, Group, displayed_groups, []);
-+			false ->
-+			    []
-+		    end
-+	    end, get_user_groups(US))),
-+    [Group || Group <- DisplayedGroups1, is_group_enabled(Host, Group)].
-+
-+
-+
-+
-+add_user_to_group(Host, US, Group) ->
-+    R = #sr_user{us = US, group_host = {Group, Host}},
-+    F = fun() ->
-+		mnesia:write(R)
-+	end,
-+    mnesia:transaction(F).
-+
-+remove_user_from_group(Host, US, Group) ->
-+    R = #sr_user{us = US, group_host = {Group, Host}},
-+    F = fun() ->
-+		mnesia:delete_object(R)
-+	end,
-+    mnesia:transaction(F).
-+
-+
-+
-+find_user_attr(User, Host) ->
-+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
-+    Filter = eldap:equalityMatch(Attr, User),
-+    Base = ejabberd_config:get_local_option({ad_base, Host}),
-+    
-+    case eldap:search("mod_shared_roster_ad",
-+                      [{base, Base},
-+                       {filter, Filter},
-+                       {attributes, []}]) of
-+        #eldap_search_result{entries = [E | _]} ->
-+            E;
-+        _ ->
-+            false
-+    end.
-+
-+get_user_displayed_groups_ad(US) ->
-+    {_, Host} = US,
-+    AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
-+    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
-+    Base = ejabberd_config:get_local_option({ad_base, Host}),
-+    
-+    case eldap:search("mod_shared_roster_ad",
-+                      [{base, Base},
-+                       {filter, FilterGroup},
-+                       {attributes, []}]) of
-+        #eldap_search_result{entries = E} ->
-+	    lists:usort(lists:map(
-+	      fun(X) ->
-+		      case X of
-+			  #eldap_entry{attributes = Attributes} ->
-+			      ldap_get_value(Attributes,"department");
-+			  false ->
-+			      ""	    
-+		      end
-+		      end, E
-+	     ));
-+	    
-+        _ ->
-+            []
-+    end.
-+
-+get_eldap_id(Host, Name) ->
-+    atom_to_list(gen_mod:get_module_proc(Host, Name)).
-+
-+
-+get_group_users_ad(Host, Group) ->
-+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
-+%    AdGroup = ejabberd_config:get_local_option({ad_group, Host}),
-+    FilterPerson = eldap:equalityMatch("objectCategory", "person"),
-+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
-+    FilterHidden = eldap:equalityMatch("description", "hidden"),
-+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
-+    FilterDep = eldap:equalityMatch("department", Group),
-+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
-+    FilterDef = eldap:present(Attr),
-+    Filter = eldap:'and'([
-+			  FilterDef,
-+			  FilterPerson,
-+%			  FilterGroup,
-+			  FilterDep,
-+			  eldap:'not'(FilterComp),
-+			  eldap:'not'(FilterHidden),
-+			  eldap:'not'(FilterLive)]),
-+    Base = ejabberd_config:get_local_option({ad_base, Host}),
-+    case eldap:search(get_eldap_id(Host, ejabberd),
-+		      [{base, Base},
-+		       {filter, Filter},
-+		       {attributes, [Attr]}]) of
-+	#eldap_search_result{entries = Es} ->
-+	    lists:flatmap(
-+	      fun(E) ->
-+		      case lists:keysearch(Attr, 1, E#eldap_entry.attributes) of
-+			  {value, {_, [U]}} ->
-+			      case jlib:nodeprep(U) of
-+				  error ->
-+				      [];
-+				  LU ->
-+				      [{LU, Host}]
-+			      end;
-+			  _ ->
-+			      []
-+		      end
-+	      end, Es);
-+	_ ->
-+	    []
-+    end.
-+
-+
-+
-+
-+
-+ldap_get_value(E,Attribute) ->
-+    case lists:filter(fun({A,_}) ->
-+			      string:equal(A,Attribute)
-+		      end,E) of
-+	[{_,[Value|_]}] ->
-+	    Value;
-+	_ -> 
-+	    none
-+    end.
-+
-+get_user_fn(User, Host) ->
-+    case find_user_attr(User,Host) of
-+	#eldap_entry{attributes = Attributes} ->
-+	    ldap_get_value(Attributes,"cn");
-+	
-+	false ->
-+	    ""	    
-+    end.
-+
-+
-diff --git a/src/mod_vcard_ad.erl b/src/mod_vcard_ad.erl
-new file mode 100644
-index 0000000..52ddbec
---- /dev/null
-+++ b/src/mod_vcard_ad.erl
-@@ -0,0 +1,631 @@
-+
-+%%%----------------------------------------------------------------------
-+%%% File    : mod_vcard_ad.erl
-+%%% Author  : Stanislav Bogatyrev <realloc@realloc.spb.ru>
-+%%% Author  : Alexey Shchepin <alexey@sevcom.net>
-+%%% Author  : Alex <agent_007> Gorbachenko (agent_007@immo.ru)
-+%%% Purpose : 
-+%%% Created :  2 Jan 2003 by Alexey Shchepin <alexey@sevcom.net>
-+%%% Id      : $Id: mod_vcard_ad.erl 437 2005-11-19 01:20:05Z agent_007 $
-+%%%----------------------------------------------------------------------
-+
-+-module(mod_vcard_ad).
-+-author('realloc@realloc.spb.ru').
-+-author('alexey@sevcom.net').
-+-author('agent_007@immo.ru').
-+-vsn('$Revision: 437 $ ').
-+
-+-behaviour(gen_mod).
-+
-+-export([start/2, init/3, stop/1,
-+ 	 get_sm_features/5,
-+	 process_local_iq/3,
-+	 process_sm_iq/3,
-+	 remove_user/1]).
-+
-+-include("ejabberd.hrl").
-+-include("eldap/eldap.hrl").
-+-include("jlib.hrl").
-+
-+-define(PROCNAME, ejabberd_mod_vcard_ad).
-+
-+start(Host, Opts) ->
-+    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
-+    gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD,
-+				  ?MODULE, process_local_iq, IQDisc),
-+    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_VCARD,
-+				  ?MODULE, process_sm_iq, IQDisc),
-+    ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
-+    LDAPServers = ejabberd_config:get_local_option({ad_servers, Host}),
-+    RootDN = ejabberd_config:get_local_option({ad_rootdn, Host}),
-+    Password = ejabberd_config:get_local_option({ad_password, Host}),
-+    eldap:start_link("mod_vcard_ad", LDAPServers, 389, RootDN, Password),
-+    MyHost = gen_mod:get_opt(host, Opts, "vjud." ++ Host),
-+    Search = gen_mod:get_opt(search, Opts, true),
-+    register(gen_mod:get_module_proc(Host, ?PROCNAME),
-+	     spawn(?MODULE, init, [MyHost, Host, Search])).
-+
-+init(Host, ServerHost, Search) ->
-+    case Search of
-+	false ->
-+	    loop(Host, ServerHost);
-+	_ ->
-+	    ejabberd_router:register_route(Host),
-+	    loop(Host, ServerHost)
-+    end.
-+
-+loop(Host, ServerHost) ->
-+    receive
-+	{route, From, To, Packet} ->
-+	    case catch do_route(ServerHost, From, To, Packet) of
-+		{'EXIT', Reason} ->
-+		    ?ERROR_MSG("~p", [Reason]);
-+		_ ->
-+		    ok
-+	    end,
-+	    loop(Host, ServerHost);
-+	stop ->
-+	    ejabberd_router:unregister_route(Host),
-+	    ok;
-+	_ ->
-+	    loop(Host, ServerHost)
-+    end.
-+
-+stop(Host) ->
-+    gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
-+    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_VCARD),
-+    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
-+    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-+    Proc ! stop,
-+    {wait, Proc}.
-+
-+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
-+    Acc;
-+get_sm_features(Acc, _From, _To, Node, _Lang) ->
-+    case Node of
-+ 	[] ->
-+ 	    case Acc of
-+ 		{result, Features} ->
-+ 		    {result, [?NS_VCARD | Features]};
-+ 		empty ->
-+ 		    {result, [?NS_VCARD]}
-+ 	    end;
-+  	_ ->
-+ 	    Acc
-+    end.
-+
-+process_local_iq(_From, _To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
-+    case Type of
-+	set ->
-+	    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
-+	get ->
-+	    IQ#iq{type = result,
-+		  sub_el = [{xmlelement, "vCard",
-+			     [{"xmlns", ?NS_VCARD}],
-+			     [{xmlelement, "FN", [],
-+			       [{xmlcdata, "ejabberd"}]},
-+			      {xmlelement, "URL", [],
-+			       [{xmlcdata,
-+				 "http://ejabberd.jabberstudio.org/"}]},
-+			      {xmlelement, "DESC", [],
-+			       [{xmlcdata,
-+				 translate:translate(
-+				   Lang,
-+				   "Erlang Jabber Server\n"
-+				   "Copyright (c) 2002-2005 Alexey Shchepin")}]},
-+			      {xmlelement, "BDAY", [],
-+			       [{xmlcdata, "2002-11-16"}]}
-+			     ]}]}
-+    end.
-+
-+find_ldap_user(Host, User) ->
-+    Attr = ejabberd_config:get_local_option({ad_uidattr, Host}),
-+    Filter = eldap:equalityMatch(Attr, User),
-+    Base = ejabberd_config:get_local_option({ad_base, Host}),
-+    case eldap:search("mod_vcard_ad", [{base, Base},
-+					 {filter, Filter},
-+					 {attributes, []}]) of
-+	#eldap_search_result{entries = [E | _]} ->
-+	    E;
-+	_ ->
-+	    false
-+    end.
-+
-+is_attribute_read_allowed(Name,From,To) ->
-+    true.
-+
-+ldap_attribute_to_vcard(Prefix,{Name,Values},From,To) ->
-+    case is_attribute_read_allowed(Name,From,To) of 
-+	true ->
-+	    ldap_lca_to_vcard(Prefix,stringprep:tolower(Name),Values);
-+	_ ->
-+	    none
-+    end.
-+
-+ldap_lca_to_vcard(vCard,"displayname",[Value|_]) ->
-+    {xmlelement,"FN",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCard,"cn",[Value|_]) ->
-+    {xmlelement,"NICKNAME",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCard,"title",[Value|_]) ->
-+    {xmlelement,"TITLE",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCard,"wwwhomepage",[Value|_]) ->
-+    {xmlelement,"URL",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCard,"description",[Value|_]) ->
-+    {xmlelement,"DESC",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCard,"telephonenumber",[Value|_]) ->
-+    {xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]},
-+			  {xmlelement,"WORK",[],[]},
-+			  {xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]};
-+
-+ldap_lca_to_vcard(vCard,"mail",[Value|_]) ->
-+    {xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]},
-+			    {xmlelement,"PREF",[],[]},
-+			    {xmlelement,"USERID",[],[{xmlcdata,Value}]}]};
-+
-+ldap_lca_to_vcard(vCardN,"sn",[Value|_]) ->
-+    {xmlelement,"FAMILY",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCardN,"givenname",[Value|_]) ->
-+    {xmlelement,"GIVEN",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCardN,"initials",[Value|_]) ->
-+    {xmlelement,"MIDDLE",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCardAdr,"streetaddress",[Value|_]) ->
-+    {xmlelement,"STREET",[],[{xmlcdata,Value}]};
-+ldap_lca_to_vcard(vCardAdr,"co",[Value|_]) ->
-+    {xmlelement,"CTRY",[],[{xmlcdata,Value}]};
-+ldap_lca_to_vcard(vCardAdr,"l",[Value|_]) ->
-+    {xmlelement,"LOCALITY",[],[{xmlcdata,Value}]};
-+ldap_lca_to_vcard(vCardAdr,"st",[Value|_]) ->
-+    {xmlelement,"REGION",[],[{xmlcdata,Value}]};
-+ldap_lca_to_vcard(vCardAdr,"postalcode",[Value|_]) ->
-+    {xmlelement,"PCODE",[],[{xmlcdata,Value}]};
-+ldap_lca_to_vcard(vCardO,"company",[Value|_]) ->
-+    {xmlelement,"ORGNAME",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(vCardO,"department",[Value|_]) ->
-+    {xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]};
-+
-+ldap_lca_to_vcard(_,_,_) -> none.
-+
-+ldap_attributes_to_vcard(Attributes,From,To) ->
-+    Elts = lists:map(fun(Attr) ->
-+			     ldap_attribute_to_vcard(vCard,Attr,From,To)
-+		     end,Attributes),
-+    FElts = [ X || X <- Elts, X /= none ],
-+    NElts = lists:map(fun(Attr) ->
-+			      ldap_attribute_to_vcard(vCardN,Attr,From,To)
-+		      end,Attributes),
-+    FNElts = [ X || X <- NElts, X /= none ],
-+   
-+    ADRElts = lists:map(fun(Attr) ->
-+			      ldap_attribute_to_vcard(vCardAdr,Attr,From,To)
-+		      end,Attributes),
-+    FADRElts = [ X || X <- ADRElts, X /= none ],
-+    
-+    OElts = lists:map(fun(Attr) ->
-+			      ldap_attribute_to_vcard(vCardO,Attr,From,To)
-+		      end,Attributes),
-+    FOElts = [ X || X <- OElts, X /= none ],
-+    [{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}],
-+      lists:append(FElts,
-+		   [{xmlelement,"N",[],FNElts},
-+		    {xmlelement,"ADR",[],FADRElts},
-+		    {xmlelement,"ORG",[],FOElts}])
-+     }].
-+
-+process_sm_iq(From, To, #iq{type = Type, sub_el = SubEl} = IQ) ->
-+    case Type of
-+	set ->
-+	    IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
-+	get ->
-+	    #jid{luser = LUser, lserver = LServer} = To,
-+	    case find_ldap_user(LServer, LUser) of
-+		#eldap_entry{attributes = Attributes} ->
-+		    Vcard = ldap_attributes_to_vcard(Attributes,From,To),
-+		    IQ#iq{type = result, sub_el = Vcard};
-+		_ ->
-+		    IQ#iq{type = result, sub_el = []}
-+	    end
-+	end.
-+
-+-define(TLFIELD(Type, Label, Var),
-+	{xmlelement, "field", [{"type", Type},
-+			       {"label", translate:translate(Lang, Label)},
-+			       {"var", Var}], []}).
-+
-+
-+-define(FORM(JID),
-+	[{xmlelement, "instructions", [],
-+	  [{xmlcdata, translate:translate(Lang, "You need an x:data capable client to search")}]},
-+	 {xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}],
-+	  [{xmlelement, "title", [],
-+	    [{xmlcdata, translate:translate(Lang, "Search users in ") ++
-+	      jlib:jid_to_string(JID)}]},
-+	   {xmlelement, "instructions", [],
-+	    [{xmlcdata, translate:translate(Lang, "Fill in fields to search "
-+					    "for any matching Jabber User")}]},
-+	   ?TLFIELD("text-single", "User", "user"),
-+	   ?TLFIELD("text-single", "Full Name", "fn"),
-+	   ?TLFIELD("text-single", "Given Name", "given"),
-+	   ?TLFIELD("text-single", "Middle Name", "middle"),
-+	   ?TLFIELD("text-single", "Family Name", "family"),
-+	   ?TLFIELD("text-single", "Nickname", "nickname"),
-+	   ?TLFIELD("text-single", "Birthday", "bday"),
-+	   ?TLFIELD("text-single", "Country", "ctry"),
-+	   ?TLFIELD("text-single", "City", "locality"),
-+	   ?TLFIELD("text-single", "email", "email"),
-+	   ?TLFIELD("text-single", "Organization Name", "orgname"),
-+	   ?TLFIELD("text-single", "Organization Unit", "orgunit")
-+	  ]}]).
-+
-+
-+
-+
-+do_route(ServerHost, From, To, Packet) ->
-+    #jid{user = User, resource = Resource} = To,
-+    if
-+	(User /= "") or (Resource /= "") ->
-+	    Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE),
-+	    ejabberd_router ! {route, To, From, Err};
-+	true ->
-+	    IQ = jlib:iq_query_info(Packet),
-+	    case IQ of
-+		#iq{type = Type, xmlns = ?NS_SEARCH, lang = Lang, sub_el = SubEl} ->
-+		    case Type of
-+			set ->
-+			    XDataEl = find_xdata_el(SubEl),
-+			    case XDataEl of
-+				false ->
-+				    Err = jlib:make_error_reply(
-+					    Packet, ?ERR_BAD_REQUEST),
-+				    ejabberd_router:route(To, From, Err);
-+				_ ->
-+				    XData = jlib:parse_xdata_submit(XDataEl),
-+				    case XData of
-+					invalid ->
-+					    Err = jlib:make_error_reply(
-+						    Packet,
-+						    ?ERR_BAD_REQUEST),
-+					    ejabberd_router:route(To, From,
-+								  Err);
-+					_ ->
-+					    ResIQ =
-+						IQ#iq{
-+						  type = result,
-+						  sub_el =
-+						  [{xmlelement,
-+						    "query",
-+						    [{"xmlns", ?NS_SEARCH}],
-+						    [{xmlelement, "x",
-+						      [{"xmlns", ?NS_XDATA},
-+						       {"type", "result"}],
-+						      search_result(Lang, To, ServerHost, XData)
-+						     }]}]},
-+					    ejabberd_router:route(
-+					      To, From, jlib:iq_to_xml(ResIQ))
-+				    end
-+			    end;
-+			get ->
-+			    ResIQ = IQ#iq{type = result,
-+					  sub_el = [{xmlelement,
-+						     "query",
-+						     [{"xmlns", ?NS_SEARCH}],
-+						     ?FORM(To)
-+						    }]},
-+			    ejabberd_router:route(To,
-+						  From,
-+						  jlib:iq_to_xml(ResIQ))
-+		    end;
-+		#iq{type = Type, xmlns = ?NS_DISCO_INFO, sub_el = SubEl} ->
-+		    case Type of
-+			set ->
-+			    Err = jlib:make_error_reply(
-+				    Packet, ?ERR_NOT_ALLOWED),
-+			    ejabberd_router:route(To, From, Err);
-+			get ->
-+			    ResIQ =
-+				IQ#iq{type = result,
-+				      sub_el = [{xmlelement,
-+						 "query",
-+						 [{"xmlns", ?NS_DISCO_INFO}],
-+						 [{xmlelement, "identity",
-+						   [{"category", "directory"},
-+						    {"type", "user"},
-+						    {"name",
-+						     "vCard User Search"}],
-+						   []},
-+						  {xmlelement, "feature",
-+						   [{"var", ?NS_SEARCH}], []},
-+						  {xmlelement, "feature",
-+						   [{"var", ?NS_VCARD}], []}
-+						 ]
-+						}]},
-+			    ejabberd_router:route(To,
-+						  From,
-+						  jlib:iq_to_xml(ResIQ))
-+		    end;
-+		#iq{type = Type, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} ->
-+		    case Type of
-+			set ->
-+			    Err = jlib:make_error_reply(
-+				    Packet, ?ERR_NOT_ALLOWED),
-+			    ejabberd_router:route(To, From, Err);
-+			get ->
-+			    ResIQ = 
-+				IQ#iq{type = result,
-+				      sub_el = [{xmlelement,
-+						 "query",
-+						 [{"xmlns", ?NS_DISCO_ITEMS}],
-+						 []}]},
-+			    ejabberd_router:route(To,
-+						  From,
-+						  jlib:iq_to_xml(ResIQ))
-+		    end;
-+		#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} ->
-+		    ResIQ = 
-+			IQ#iq{type = result,
-+			      sub_el = [{xmlelement,
-+					 "vCard",
-+					 [{"xmlns", ?NS_VCARD}],
-+					 iq_get_vcard(Lang)}]},
-+		    ejabberd_router:route(To,
-+					  From,
-+					  jlib:iq_to_xml(ResIQ));
-+		_ ->
-+		    Err = jlib:make_error_reply(Packet,
-+						?ERR_SERVICE_UNAVAILABLE),
-+		    ejabberd_router:route(To, From, Err)
-+	    end
-+    end.
-+
-+iq_get_vcard(Lang) ->
-+    [{xmlelement, "FN", [],
-+      [{xmlcdata, "ejabberd/mod_vcard"}]},
-+     {xmlelement, "URL", [],
-+      [{xmlcdata,
-+        "http://ejabberd.jabberstudio.org/"}]},
-+     {xmlelement, "DESC", [],
-+      [{xmlcdata, translate:translate(
-+		    Lang,
-+		    "ejabberd vCard module\n"
-+		    "Copyright (c) 2003-2005 Alexey Shchepin")}]}].
-+
-+find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) ->
-+    find_xdata_el1(SubEls).
-+
-+find_xdata_el1([]) ->
-+    false;
-+find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
-+    case xml:get_attr_s("xmlns", Attrs) of
-+	?NS_XDATA ->
-+	    {xmlelement, Name, Attrs, SubEls};
-+	_ ->
-+	    find_xdata_el1(Els)
-+    end;
-+find_xdata_el1([_ | Els]) ->
-+    find_xdata_el1(Els).
-+
-+-define(LFIELD(Label, Var),
-+	{xmlelement, "field", [{"label", translate:translate(Lang, Label)},
-+			       {"var", Var}], []}).
-+
-+search_result(Lang, JID, ServerHost, Data) ->
-+    [{xmlelement, "title", [],
-+      [{xmlcdata, translate:translate(Lang, "Results of search in ") ++
-+	jlib:jid_to_string(JID)}]},
-+     {xmlelement, "reported", [],
-+      [?LFIELD("JID", "jid"),
-+       ?LFIELD("Full Name", "fn"),
-+       ?LFIELD("Given Name", "given"),
-+       ?LFIELD("Middle Name", "middle"),
-+       ?LFIELD("Family Name", "family"),
-+       ?LFIELD("Nickname", "nickname"),
-+       ?LFIELD("Birthday", "bday"),
-+       ?LFIELD("Country", "ctry"),
-+       ?LFIELD("City", "locality"),
-+       ?LFIELD("email", "email"),
-+       ?LFIELD("Organization Name", "orgname"),
-+       ?LFIELD("Organization Unit", "orgunit")
-+      ]}] ++ lists:map(fun(E) -> 
-+			       record_to_item(E#eldap_entry.attributes)
-+		       end, search(ServerHost, Data)).
-+
-+-define(FIELD(Var, Val),
-+	{xmlelement, "field", [{"var", Var}],
-+	 [{xmlelement, "value", [],
-+	   [{xmlcdata, Val}]}]}).
-+
-+case_exact_compare(none,_) ->
-+    false;
-+case_exact_compare(_,none) ->
-+    false;
-+case_exact_compare(X,Y) ->
-+    X > Y.
-+
-+ldap_sort_entries(L) ->
-+    lists:sort(fun(E1,E2) ->
-+		       case_exact_compare(ldap_get_value(E1,"cn"),ldap_get_value(E2,"cn"))
-+	       end,L).
-+
-+ldap_get_value(E,Attribute) ->
-+    #eldap_entry{attributes = Attributes} = E,
-+    case lists:filter(fun({A,_}) ->
-+			      string:equal(A,Attribute)
-+		      end,Attributes) of
-+	[{Attr,[Value|_]}] ->
-+	    Value;
-+	_ -> 
-+	    none
-+    end.
-+
-+ldap_attribute_to_item("samaccountname",Value) ->
-+    [
-+     ?FIELD("jid",Value ++ "@" ++ ?MYNAME),
-+     ?FIELD("uid",Value)
-+    ];
-+
-+ldap_attribute_to_item("cn",Value) ->
-+    [
-+     ?FIELD("nickname",Value)
-+    ];
-+
-+ldap_attribute_to_item("displayname",Value) ->
-+    [
-+     ?FIELD("fn",Value)
-+    ];
-+
-+ldap_attribute_to_item("sn",Value) ->
-+    [
-+     ?FIELD("family",Value)
-+    ];
-+ldap_attribute_to_item("co",Value) ->
-+    [
-+     ?FIELD("ctry",Value)
-+    ];
-+ldap_attribute_to_item("l",Value) ->
-+    [
-+     ?FIELD("locality",Value)
-+    ];
-+
-+ldap_attribute_to_item("givenname",Value) ->
-+    [
-+     ?FIELD("given",Value)
-+    ];
-+
-+ldap_attribute_to_item("initials",Value) ->
-+    [
-+     ?FIELD("middle",Value)
-+    ];
-+
-+ldap_attribute_to_item("mail",Value) ->
-+    [
-+     ?FIELD("email",Value)
-+    ];
-+
-+ldap_attribute_to_item("company",Value) ->
-+    [
-+     ?FIELD("orgname",Value)
-+    ];
-+
-+ldap_attribute_to_item("department",Value) ->
-+    [
-+     ?FIELD("orgunit",Value)
-+    ];
-+
-+ldap_attribute_to_item(_,_) ->
-+    [none].
-+
-+record_to_item(Attributes) ->
-+    List = lists:append(lists:map(fun({Attr,[Value|_]}) -> 
-+					  ldap_attribute_to_item(stringprep:tolower(Attr),Value)
-+				  end,Attributes)),
-+    FList = [X || X <- List, X /= none],
-+    {xmlelement, "item", [],FList}.
-+
-+search(LServer, Data) ->
-+%    AdGroup = ejabberd_config:get_local_option({ad_group, LServer}),
-+    FilterDef = make_filter(Data),
-+    FilterPerson =  eldap:equalityMatch("objectCategory", "person"),
-+    FilterComp = eldap:equalityMatch("objectClass", "computer"),
-+    FilterHidden = eldap:equalityMatch("description", "hidden"),
-+%    FilterGroup = eldap:equalityMatch("memberOf", AdGroup),
-+    FilterLive = eldap:equalityMatch("userAccountControl", "66050"),
-+    Filter = eldap:'and'([
-+			  FilterDef,
-+			  FilterPerson,
-+%			  FilterGroup,
-+			  eldap:'not'(FilterComp),
-+			  eldap:'not'(FilterHidden),
-+			  eldap:'not'(FilterLive)]),
-+    Base = ejabberd_config:get_local_option({ad_base, LServer}),
-+    UIDAttr = ejabberd_config:get_local_option({ad_uidattr, LServer}),
-+    case eldap:search("mod_vcard_ad",[{base, Base},
-+					{filter, Filter},
-+					{attributes, []}]) of
-+	#eldap_search_result{entries = E} ->
-+	    [X || X <- E,
-+		  ejabberd_auth:is_user_exists(
-+		    ldap_get_value(X, UIDAttr), LServer)];
-+	Err ->
-+	    ?ERROR_MSG("Bad search: ~p", [[LServer, {base, Base},
-+					{filter, Filter},
-+					{attributes, []}]])
-+    end.
-+
-+
-+make_filter(Data) ->
-+    Filter = [X || X <- lists:map(fun(R) -> 
-+					  make_assertion(R)
-+				  end, Data),
-+		   X /= none ],
-+    case Filter of
-+	[F] -> 
-+	    F;
-+	_ ->
-+	    eldap:'and'(Filter)
-+    end.
-+
-+
-+make_assertion("givenName",Value) ->
-+    eldap:substrings("givenName",[{any,Value}]);
-+
-+make_assertion("cn",Value) ->
-+    eldap:substrings("cn",[{any,Value}]);
-+
-+make_assertion("sn",Value) ->
-+    eldap:substrings("sn",[{any,Value}]);
-+
-+make_assertion(Attr, Value) ->
-+    eldap:equalityMatch(Attr,Value).
-+
-+make_assertion({SVar, [Val]}) ->
-+    LAttr = ldap_attribute(SVar),
-+    case LAttr of
-+	none ->
-+	    none;
-+	_ ->
-+	    if 
-+		is_list(Val) and (Val /= "") ->
-+		    make_assertion(LAttr,Val);
-+		true ->
-+		    none
-+	    end
-+    end.
-+
-+ldap_attribute("user") ->
-+    "samaccountname";
-+
-+ldap_attribute("fn") ->
-+    "cn";
-+
-+ldap_attribute("family") ->
-+    "sn";
-+
-+ldap_attribute("given") ->
-+    "givenName";
-+
-+ldap_attribute("middle") ->
-+    "initials";
-+
-+ldap_attribute("email") ->
-+    "mail";
-+
-+ldap_attribute("orgname") ->
-+    "company";
-+
-+ldap_attribute("orgunit") ->
-+    "department";
-+
-+ldap_attribute(_) ->
-+    none.
-+
-+remove_user(User) ->
-+    true.
-+
--- 
-1.7.10.1
-

diff --git a/ejabberd-0010-Disable-INET_DIST_INTERFACE-by-default.patch b/ejabberd-0010-Disable-INET_DIST_INTERFACE-by-default.patch
deleted file mode 100644
index 10be6cc..0000000
--- a/ejabberd-0010-Disable-INET_DIST_INTERFACE-by-default.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From a5e1042ca338c81a5c75ef2c8d2fb4cab8b935e3 Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Sat, 18 Jun 2011 23:24:28 +0400
-Subject: [PATCH 10/10] Disable INET_DIST_INTERFACE by default
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/ejabberdctl.cfg.example |    2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/ejabberdctl.cfg.example b/src/ejabberdctl.cfg.example
-index b72058a..5ee43cf 100644
---- a/src/ejabberdctl.cfg.example
-+++ b/src/ejabberdctl.cfg.example
-@@ -59,7 +59,7 @@
- #
- # Default: {127,0,0,1}
- #
--INET_DIST_INTERFACE={127,0,0,1}
-+#INET_DIST_INTERFACE={127,0,0,1}
- 
- #.
- #' ERL_EPMD_ADDRESS: IP addresses where epmd listens for connections
--- 
-1.7.10.1
-

diff --git a/ejabberd.spec b/ejabberd.spec
index df00937..05c4cea 100644
--- a/ejabberd.spec
+++ b/ejabberd.spec
@@ -3,15 +3,15 @@
 # Currently, hevea available only in Fedora
 %if 0%{?fedora}
 # No hevea for ppc64
-# see https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=250253
-%ifnarch ppc64 s390 s390x %{arm}
+# see https://bugzilla.redhat.com/bugzilla/250253
+%ifnarch ppc64 s390 s390x sparc64
 %define with_hevea 1
 %endif
 %endif
 
 Name:           ejabberd
 Version:        2.1.11
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        A distributed, fault-tolerant Jabber/XMPP server
 
 Group:          Applications/Internet
@@ -33,24 +33,20 @@ Source11:       ejabberd.pam
 
 # Use ejabberd as an example for PAM service name (fedora/epel-specific)
 Patch1: ejabberd-0001-Fix-PAM-service-example-name-to-match-actual-one.patch
-# Introducing mod_ctlextra
-Patch2: ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch
 # fixed delays in s2s connections
-Patch3: ejabberd-0003-Fixed-delays-in-s2s-connections.patch
+Patch2: ejabberd-0002-Fixed-delays-in-s2s-connections.patch
 # Introducing mod_admin_extra
-Patch4: ejabberd-0004-Introducing-mod_admin_extra.patch
+Patch3: ejabberd-0003-Introducing-mod_admin_extra.patch
 # BZ# 439583, 452326, 451554, 465196, 502361 (fedora/epel-specific)
-Patch5: ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
+Patch4: ejabberd-0004-Fedora-specific-changes-to-ejabberdctl.patch
 # Fix so-lib permissions while installing (fedora/epel-specific)
-Patch6:	ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
+Patch5:	ejabberd-0005-Install-.so-objects-with-0755-permissions.patch
 # Will be proposed for inclusion into upstream
-Patch7: ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
+Patch6: ejabberd-0006-Use-versioned-directory-for-storing-docs.patch
 # Backported from upstream
-Patch8: ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
-# Introduce old AD stuff
-Patch9: ejabberd-0009-Added-old-modules-for-Active-Directory.patch
+Patch7: ejabberd-0007-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
 # Disable IP restriction for ejabberdctl (seems that it doesn't work well)
-Patch10: ejabberd-0010-Disable-INET_DIST_INTERFACE-by-default.patch
+Patch8: ejabberd-0008-Disable-INET_DIST_INTERFACE-by-default.patch
 
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
@@ -116,15 +112,13 @@ Documentation for ejabberd.
 %setup -q
 
 %patch1 -p1 -b .pam_name
-%patch2 -p1 -b .mod_ctlextra
-%patch3 -p1 -b .s2s_delays
-%patch4 -p1 -b .mod_admin_extra
-%patch5 -p1 -b .fedora_specific
-%patch6 -p1 -b .fix_perms
-%patch7 -p1 -b .versioned_docdir
-%patch8 -p1 -b .gssapi
-%patch9 -p1 -b .ad_stuff
-%patch10 -p1 -b .disable_ip_restriction_for_ejabberdctl
+%patch2 -p1 -b .s2s_delays
+%patch3 -p1 -b .mod_admin_extra
+%patch4 -p1 -b .fedora_specific
+%patch5 -p1 -b .fix_perms
+%patch6 -p1 -b .versioned_docdir
+%patch7 -p1 -b .gssapi
+%patch8 -p1 -b .disable_ip_restriction_for_ejabberdctl
 
 # FIXME last-minute fix for 2.1.11 only
 sed -i -e "s,2.1.x,2.1.11,g" src/configure
@@ -134,7 +128,7 @@ touch -r src/configure.ac src/configure
 %build
 pushd src
 %configure --enable-odbc --enable-pam
-# doesn't builds on SMP currently
+# doesn't build on SMP currently
 make
 popd
 %if 0%{?with_hevea}
@@ -307,8 +301,6 @@ rm -rf %{buildroot}
 
 
 %files
-%defattr(-,root,root,-)
-
 %dir %{_docdir}/%{name}-%{version}
 %doc %{_docdir}/%{name}-%{version}/COPYING
 
@@ -387,7 +379,6 @@ rm -rf %{buildroot}
 %attr(750,ejabberd,ejabberd) %dir /var/log/ejabberd
 
 %files doc
-%defattr(-,root,root,-)
 %doc %{_docdir}/%{name}-%{version}/*.html
 %doc %{_docdir}/%{name}-%{version}/*.png
 %if 0%{?with_hevea}
@@ -396,6 +387,10 @@ rm -rf %{buildroot}
 %doc %{_docdir}/%{name}-%{version}/*.txt
 
 %changelog
+* Sun Jul 15 2012 Peter Lemenkov <lemenkov@gmail.com> - 2.1.11-2
+- Updated mod_Admin_extra to work with R15B (see rhbz #840039)
+- Updated list of arches where we don't have hevea
+
 * Sun May 06 2012 Peter Lemenkov <lemenkov@gmail.com> - 2.1.11-1
 - Ver. 2.1.11
 - Fixed systemd installation

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-23 19:07 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-23 19:07 [rpms/ejabberd] f45: Updated mod_Admin_extra to work with R15B (see rhbz #840039) Peter Lemenkov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox