public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Peter Lemenkov <lemenkov@gmail.com>
To: git-commits@fedoraproject.org
Subject: [rpms/ejabberd] f45: Ver. 2.1.6
Date: Wed, 23 Sep 2026 19:07:02 GMT [thread overview]
Message-ID: <179019042231.1.5570967641490648007.rpms-ejabberd-5c2945ff47a1@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/ejabberd
Branch : f45
Commit : 5c2945ff47a11443f1c33686b8ab24db653fca64
Author : Peter Lemenkov <lemenkov@gmail.com>
Date : 2010-12-14T22:47:28+03:00
Stats : +2824/-3062 in 27 file(s)
URL : https://src.fedoraproject.org/rpms/ejabberd/c/5c2945ff47a11443f1c33686b8ab24db653fca64?branch=f45
Log:
Ver. 2.1.6
Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
---
diff --git a/.gitignore b/.gitignore
index 0308b93..7794ea3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
ejabberd-2.1.4.tar.gz
ejabberd-2.1.5.tar.gz
+/ejabberd-2.1.6.tar.gz
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 5e9444f..3b0dd34 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,4 +1,4 @@
-From 5fdb6557be8b5758856a91e8172acfddc9d04382 Mon Sep 17 00:00:00 2001
+From 9087a0b27b3d56f8fdb28e5190012796f348d43a 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
@@ -9,10 +9,10 @@ Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/src/ejabberd.cfg.example b/src/ejabberd.cfg.example
-index 4087947..b351cfc 100644
+index a95689b..005c73c 100644
--- a/src/ejabberd.cfg.example
+++ b/src/ejabberd.cfg.example
-@@ -235,7 +235,7 @@
+@@ -236,7 +236,7 @@
%% Authentication using PAM
%%
%%{auth_method, pam}.
@@ -22,5 +22,5 @@ index 4087947..b351cfc 100644
%%
%% Authentication using LDAP
--
-1.7.2.1
+1.7.3.3
diff --git a/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch b/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch
new file mode 100644
index 0000000..1ebcd1f
--- /dev/null
+++ b/ejabberd-0002-Add-mod_ctlextra-as-an-ejabberd-module.patch
@@ -0,0 +1,936 @@
+From 85b432d0293d867dd5c11ae8e7b5c724b7fad056 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(+), 0 deletions(-)
+ create mode 100644 src/mod_ctlextra.erl
+
+diff --git a/src/ejabberd.app b/src/ejabberd.app
+index c2d282d..6db563c 100644
+--- a/src/ejabberd.app
++++ b/src/ejabberd.app
+@@ -70,6 +70,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.3.3
+
diff --git a/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch b/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch
deleted file mode 100644
index 5d14e87..0000000
--- a/ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From ca74de7045b7393b09b53dd02e548e76cab9c849 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] Mention mod_ctlextra as an ejabberd module
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/ejabberd.app | 1 +
- 1 files changed, 1 insertions(+), 0 deletions(-)
-
-diff --git a/src/ejabberd.app b/src/ejabberd.app
-index 0ebb1aa..3a8d604 100644
---- a/src/ejabberd.app
-+++ b/src/ejabberd.app
-@@ -70,6 +70,7 @@
- mod_caps,
- mod_configure2,
- mod_configure,
-+ mod_ctlextra,
- mod_disco,
- mod_echo,
- mod_http_bind,
---
-1.7.2.1
-
diff --git a/ejabberd-0003-Fixed-delays-in-s2s-connections.patch b/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
index b313ddd..4c4fd48 100644
--- a/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
+++ b/ejabberd-0003-Fixed-delays-in-s2s-connections.patch
@@ -1,4 +1,4 @@
-From 9c8d1d47cf8e8ab98e8063161b04c8beabfc9ce5 Mon Sep 17 00:00:00 2001
+From cf4847224a1180836d1caee3621ff0d562e4e8a7 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.
@@ -9,27 +9,27 @@ 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 | 6 +++---
- 2 files changed, 4 insertions(+), 4 deletions(-)
+ 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 56086c9..9ab58a7 100644
+index eb8c05a..0ab9d94 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
-@@ -367,7 +367,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
- case {ejabberd_s2s:allow_host(To, From),
+@@ -404,7 +404,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(To, From),
-+ %%ejabberd_s2s_out:terminate_if_waiting_delay(To, From),
- ejabberd_s2s_out:start(To, From,
+- 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 a1c6412..b8393cb 100644
+index f59e8ec..1422d3a 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
-@@ -91,7 +91,7 @@
+@@ -93,7 +93,7 @@
[From, Host, Type])).
-endif.
@@ -38,17 +38,16 @@ index a1c6412..b8393cb 100644
%% We do not block on send anymore.
-define(TCP_SEND_TIMEOUT, 15000).
-@@ -228,8 +228,8 @@ open_socket(init, StateData) ->
- {error, _Reason} ->
- ?INFO_MSG("s2s connection: ~s -> ~s (remote server not found)",
- [StateData#state.myname, StateData#state.server]),
-- wait_before_reconnect(StateData)
-- %%{stop, normal, StateData}
-+ %%wait_before_reconnect(StateData)
-+ {stop, normal, StateData}
+@@ -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(stop, StateData) ->
- ?INFO_MSG("s2s connection: ~s -> ~s (stopped in open socket)",
--
-1.7.2.1
+1.7.3.3
diff --git a/ejabberd-0004-Introducing-mod_admin_extra.patch b/ejabberd-0004-Introducing-mod_admin_extra.patch
index 872e4b7..0e09141 100644
--- a/ejabberd-0004-Introducing-mod_admin_extra.patch
+++ b/ejabberd-0004-Introducing-mod_admin_extra.patch
@@ -1,4 +1,4 @@
-From a0769ced87381fa07c6845d5fda18b5e584d4ed5 Mon Sep 17 00:00:00 2001
+From 9c419c9531e16a1bafcecd3257bfc4646ebdc9d5 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
@@ -17,7 +17,7 @@ https://forge.process-one.net/browse/ejabberd-modules/mod_admin_extra
create mode 100644 src/mod_admin_extra.erl
diff --git a/src/ejabberd.app b/src/ejabberd.app
-index 3a8d604..de47a0d 100644
+index 6db563c..57f8a61 100644
--- a/src/ejabberd.app
+++ b/src/ejabberd.app
@@ -105,6 +105,7 @@
@@ -1314,5 +1314,5 @@ index 0000000..9f3ca14
+is_glob_match(String, Glob) ->
+ is_regexp_match(String, regexp:sh_to_awk(Glob)).
--
-1.7.2.1
+1.7.3.3
diff --git a/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch b/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
index a76471e..c4c280c 100644
--- a/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
+++ b/ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
@@ -1,4 +1,4 @@
-From 52209eb6efd83b91d5ad49623d99c53a27ffcbe9 Mon Sep 17 00:00:00 2001
+From e5f84e1f8e62e073be61843cc124f3150e4d2206 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
@@ -9,7 +9,7 @@ Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
-index fecdecc..bceec28 100644
+index 67eafbf..1665a02 100644
--- a/src/ejabberdctl.template
+++ b/src/ejabberdctl.template
@@ -9,10 +9,10 @@ ERL_MAX_ETS_TABLES=1400
@@ -44,5 +44,5 @@ index fecdecc..bceec28 100644
done
if [ "$ID" -eq "$EJID" ] ; then
--
-1.7.2.1
+1.7.3.3
diff --git a/ejabberd-0006-Change-directory-to-readable-by-everyone.patch b/ejabberd-0006-Change-directory-to-readable-by-everyone.patch
deleted file mode 100644
index 0567ab7..0000000
--- a/ejabberd-0006-Change-directory-to-readable-by-everyone.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From ae241feec49dd9b9fc1355e4be37c3f8b268de87 Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Thu, 18 Mar 2010 14:57:21 +0300
-Subject: [PATCH 06/10] Change directory to readable by everyone
-
-This requires to suppress error messages like
-"File operation error: eacces". See rhbz #564686:
-
-https://bugzilla.redhat.com/564686
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/ejabberdctl.template | 4 ++++
- 1 files changed, 4 insertions(+), 0 deletions(-)
-
-diff --git a/src/ejabberdctl.template b/src/ejabberdctl.template
-index bceec28..ee74650 100644
---- a/src/ejabberdctl.template
-+++ b/src/ejabberdctl.template
-@@ -355,6 +355,10 @@ wait_for_status()
- return $status
- }
-
-+# In order to prevent issues with "File operation error: eacces."
-+# See rhbz #564686.
-+cd /
-+
- case $ARGS in
- ' start') start;;
- ' debug') debug;;
---
-1.7.2.1
-
diff --git a/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
new file mode 100644
index 0000000..4366d9b
--- /dev/null
+++ b/ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
@@ -0,0 +1,26 @@
+From de49bf5543375fbccaf112bdd189a71d43acbbf0 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 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index 4e56181..ef8a55e 100644
+--- a/src/Makefile.in
++++ b/src/Makefile.in
+@@ -236,7 +236,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.3.3
+
diff --git a/ejabberd-0007-Install-.so-objects-with-0755-permissions.patch b/ejabberd-0007-Install-.so-objects-with-0755-permissions.patch
deleted file mode 100644
index be801ae..0000000
--- a/ejabberd-0007-Install-.so-objects-with-0755-permissions.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From c91ddeb5735d2a4cebda364df6419637679a0dbe Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Sat, 12 Jun 2010 14:14:52 +0400
-Subject: [PATCH 07/10] Install *.so objects with 0755 permissions
-
-Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
----
- src/Makefile.in | 2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index cf600c3..c9492f3 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -230,7 +230,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.2.1
-
diff --git a/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch b/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
new file mode 100644
index 0000000..ffb2ec1
--- /dev/null
+++ b/ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
@@ -0,0 +1,35 @@
+From 3e1eeccdfdd315c4e660e48098cb3a8c8802f75c 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 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/src/Makefile.in b/src/Makefile.in
+index ef8a55e..d15739c 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.3.3
+
diff --git a/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch b/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
new file mode 100644
index 0000000..df9055f
--- /dev/null
+++ b/ejabberd-0008-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
@@ -0,0 +1,480 @@
+From 60b08ab02f17070d66558a37fae69a735bda696a 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 | 76 +++++++++++++++++----
+ 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/ejabberd_c2s.erl | 12 +++-
+ src/ejabberd_socket.erl | 19 +++++
+ 8 files changed, 283 insertions(+), 23 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 121ef6d..ea358f0 100644
+--- a/src/cyrsasl.erl
++++ b/src/cyrsasl.erl
+@@ -30,19 +30,39 @@
+ -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, require_plain_password}).
+--record(sasl_state, {service, myname, realm,
+- get_password, check_password, check_password_digest,
+- mech_mod, mech_state}).
++
++%% @type saslstate() = {sasl_state, Service, Myname, Realm, GetPassword, CheckPassword, CheckPasswordDigest, Mech_Mod, Mech_State}
++%% Service = string()
++%% Myname = string()
++%% Realm = string()
++%% GetPassword = function()
++%% CheckPassword = function()
++%% CheckPasswordDigest = any()
++%% Mech_Mod = atom()
++%% Mech_State = 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.
+
+@@ -53,8 +73,30 @@ start() ->
+ cyrsasl_plain:start([]),
+ cyrsasl_digest: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, RequirePlainPassword) ->
+ ets:insert(sasl_mechanism,
+ #sasl_mechanism{mechanism = Mechanism,
+@@ -113,24 +155,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 2b3ba36..2647d28 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 2a7ce2a..9628137 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).
+
+@@ -45,7 +46,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 129fb8b..4799a41 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/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
+index 6dbcd77..8818cfe 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 4d21e40..66b9c4c 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.3.3
+
diff --git a/ejabberd-0008-Use-versioned-directory-for-storing-docs.patch b/ejabberd-0008-Use-versioned-directory-for-storing-docs.patch
deleted file mode 100644
index 4ea23bc..0000000
--- a/ejabberd-0008-Use-versioned-directory-for-storing-docs.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 55622d1af9b27028219257ed7e5583c607a91d2e Mon Sep 17 00:00:00 2001
-From: Peter Lemenkov <lemenkov@gmail.com>
-Date: Sat, 12 Jun 2010 16:12:11 +0400
-Subject: [PATCH 08/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 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index c9492f3..7b585d0 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -92,7 +92,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.2.1
-
diff --git a/ejabberd-0009-Added-old-modules-for-Active-Directory.patch b/ejabberd-0009-Added-old-modules-for-Active-Directory.patch
new file mode 100644
index 0000000..1261b98
--- /dev/null
+++ b/ejabberd-0009-Added-old-modules-for-Active-Directory.patch
@@ -0,0 +1,1266 @@
+From c28b04f3abf3a4e233115c3d783db356c9567d9b 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(+), 0 deletions(-)
+ 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.3.3
+
diff --git a/ejabberd-0009-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch b/ejabberd-0009-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
deleted file mode 100644
index 435d503..0000000
--- a/ejabberd-0009-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
+++ /dev/null
@@ -1,480 +0,0 @@
-From 6b258db9aba30218c2d651c2c656bdfce39c48a9 Mon Sep 17 00:00:00 2001
-From: Badlop <badlop@process-one.net>
-Date: Thu, 15 Apr 2010 17:20:16 +0200
-Subject: [PATCH 09/10] Support SASL GSSAPI authentication (thanks to Mikael Magnusson)(EJAB-831)
-
----
- src/cyrsasl.erl | 76 +++++++++++++++++----
- 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/ejabberd_c2s.erl | 12 +++-
- src/ejabberd_socket.erl | 19 +++++
- 8 files changed, 283 insertions(+), 23 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 121ef6d..ea358f0 100644
---- a/src/cyrsasl.erl
-+++ b/src/cyrsasl.erl
-@@ -30,19 +30,39 @@
- -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, require_plain_password}).
---record(sasl_state, {service, myname, realm,
-- get_password, check_password, check_password_digest,
-- mech_mod, mech_state}).
-+
-+%% @type saslstate() = {sasl_state, Service, Myname, Realm, GetPassword, CheckPassword, CheckPasswordDigest, Mech_Mod, Mech_State}
-+%% Service = string()
-+%% Myname = string()
-+%% Realm = string()
-+%% GetPassword = function()
-+%% CheckPassword = function()
-+%% CheckPasswordDigest = any()
-+%% Mech_Mod = atom()
-+%% Mech_State = 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.
-
-@@ -53,8 +73,30 @@ start() ->
- cyrsasl_plain:start([]),
- cyrsasl_digest: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, RequirePlainPassword) ->
- ets:insert(sasl_mechanism,
- #sasl_mechanism{mechanism = Mechanism,
-@@ -113,24 +155,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 2b3ba36..2647d28 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 2a7ce2a..9628137 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).
-
-@@ -45,7 +46,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 129fb8b..4799a41 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/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
-index d4b6809..3e0a49c 100644
---- a/src/ejabberd_c2s.erl
-+++ b/src/ejabberd_c2s.erl
-@@ -254,9 +254,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)
-@@ -268,7 +275,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 25ff64c..e8c5fca 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}).
-
-@@ -222,6 +224,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.2.1
-
diff --git a/ejabberd-0010-Loading-ASN.1-driver-explicitly-to-avoid-races-in-LD.patch b/ejabberd-0010-Loading-ASN.1-driver-explicitly-to-avoid-races-in-LD.patch
deleted file mode 100644
index e989690..0000000
--- a/ejabberd-0010-Loading-ASN.1-driver-explicitly-to-avoid-races-in-LD.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From c9ff37027805d1d72446b5bcbc1a60da0f2aaae4 Mon Sep 17 00:00:00 2001
-From: Evgeniy Khramtsov <ekhramtsov@process-one.net>
-Date: Wed, 4 Aug 2010 19:23:52 +1000
-Subject: [PATCH 01/11] Loading ASN.1 driver explicitly to avoid races in LDAP (EJAB-1284)
-
----
- src/ejabberd_app.erl | 2 ++
- 1 files changed, 2 insertions(+), 0 deletions(-)
-
-diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
-index 97ae7fe..2227904 100644
---- a/src/ejabberd_app.erl
-+++ b/src/ejabberd_app.erl
-@@ -57,6 +57,8 @@ start(normal, _Args) ->
- ejabberd_config:start(),
- ejabberd_check:config(),
- connect_nodes(),
-+ %% Loading ASN.1 driver explicitly to avoid races in LDAP
-+ catch asn1rt:load_driver(),
- Sup = ejabberd_sup:start_link(),
- ejabberd_rdbms:start(),
- ejabberd_auth:start(),
---
-1.7.2.1
-
diff --git a/ejabberd-0010-last-minute-fix-correct-version-in-configure.patch b/ejabberd-0010-last-minute-fix-correct-version-in-configure.patch
new file mode 100644
index 0000000..31a9d2b
--- /dev/null
+++ b/ejabberd-0010-last-minute-fix-correct-version-in-configure.patch
@@ -0,0 +1,28 @@
+From 244c5ae4bfab64220a1d7ff492bdb67dc6612f51 Mon Sep 17 00:00:00 2001
+From: Peter Lemenkov <lemenkov@gmail.com>
+Date: Tue, 14 Dec 2010 18:52:04 +0300
+Subject: [PATCH 10/10] last minute fix - correct version in configure
+
+Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
+---
+ src/configure | 4 ++--
+ 1 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/src/configure b/src/configure
+index d515abc..ab0beca 100755
+--- a/src/configure
++++ b/src/configure
+@@ -552,8 +552,8 @@ MAKEFLAGS=
+ # Identity of this package.
+ PACKAGE_NAME='ejabberd'
+ PACKAGE_TARNAME='ejabberd'
+-PACKAGE_VERSION='2.1.x'
+-PACKAGE_STRING='ejabberd 2.1.x'
++PACKAGE_VERSION='2.1.6'
++PACKAGE_STRING='ejabberd 2.1.6'
+ PACKAGE_BUGREPORT='ejabberd@process-one.net'
+ PACKAGE_URL=''
+
+--
+1.7.3.3
+
diff --git a/ejabberd-0011-Make-xml.c-correctly-compiles-on-R14A-EJAB-1288.patch b/ejabberd-0011-Make-xml.c-correctly-compiles-on-R14A-EJAB-1288.patch
deleted file mode 100644
index c181016..0000000
--- a/ejabberd-0011-Make-xml.c-correctly-compiles-on-R14A-EJAB-1288.patch
+++ /dev/null
@@ -1,157 +0,0 @@
-From 1ea09b09a227d1e630d9dde3db68d46b0cb44f55 Mon Sep 17 00:00:00 2001
-From: Evgeniy Khramtsov <ekhramtsov@process-one.net>
-Date: Sat, 7 Aug 2010 21:06:50 +1000
-Subject: [PATCH 04/11] Make xml.c correctly compiles on R14A (EJAB-1288)
-
----
- src/Makefile.in | 1 +
- src/xml.c | 60 ++++++++++++++++++++++++++++++++++++++++--------------
- 2 files changed, 45 insertions(+), 16 deletions(-)
-
-diff --git a/src/Makefile.in b/src/Makefile.in
-index cf600c3..7d225f3 100644
---- a/src/Makefile.in
-+++ b/src/Makefile.in
-@@ -31,6 +31,7 @@ else
- endif
-
- EFLAGS += @ERLANG_SSLVER@ -pa .
-+ERLANG_CFLAGS += @ERLANG_SSLVER@
-
- # make debug=true to compile Erlang module with debug informations.
- ifdef debug
-diff --git a/src/xml.c b/src/xml.c
-index d1b2b7f..e363fed 100644
---- a/src/xml.c
-+++ b/src/xml.c
-@@ -2,6 +2,23 @@
- #include <string.h>
- #include <stdio.h>
-
-+#ifdef SSL40
-+#define ENIF_ALLOC(SIZE) enif_alloc(SIZE)
-+#define ENIF_FREE(PTR) enif_free(PTR)
-+#define ENIF_REALLOC(PTR, SIZE) enif_realloc(PTR, SIZE)
-+#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(SIZE, BIN)
-+#define ENIF_COMPARE(TERM1, TERM2) enif_compare(TERM1, TERM2)
-+#else
-+#define ENIF_ALLOC(SIZE) enif_alloc(env, SIZE)
-+#define ENIF_FREE(PTR) enif_free(env, PTR)
-+#define ENIF_REALLOC(PTR, SIZE) enif_realloc(env, PTR, SIZE)
-+#define ENIF_ALLOC_BINARY(SIZE, BIN) enif_alloc_binary(env, SIZE, BIN)
-+#define ENIF_COMPARE(TERM1, TERM2) enif_compare(env, TERM1, TERM2)
-+#endif
-+
-+static ERL_NIF_TERM atom_xmlelement;
-+static ERL_NIF_TERM atom_xmlcdata;
-+
- struct buf {
- int limit;
- int len;
-@@ -10,12 +27,19 @@ struct buf {
-
- static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el);
-
-+static int load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
-+{
-+ atom_xmlelement = enif_make_atom(env, "xmlelement");
-+ atom_xmlcdata = enif_make_atom(env, "xmlcdata");
-+ return 0;
-+}
-+
- static struct buf *init_buf(ErlNifEnv* env)
- {
-- struct buf *rbuf = enif_alloc(env, sizeof(struct buf));
-+ struct buf *rbuf = ENIF_ALLOC(sizeof(struct buf));
- rbuf->limit = 1024;
- rbuf->len = 0;
-- rbuf->b = enif_alloc(env, rbuf->limit);
-+ rbuf->b = ENIF_ALLOC(rbuf->limit);
- return rbuf;
- }
-
-@@ -23,9 +47,9 @@ static void destroy_buf(ErlNifEnv* env, struct buf *rbuf)
- {
- if (rbuf) {
- if (rbuf->b) {
-- enif_free(env, rbuf->b);
-+ ENIF_FREE(rbuf->b);
- };
-- enif_free(env, rbuf);
-+ ENIF_FREE(rbuf);
- };
- }
-
-@@ -35,7 +59,7 @@ inline void resize_buf(ErlNifEnv* env, struct buf *rbuf, int len_to_add)
-
- if (new_len >= rbuf->limit) {
- rbuf->limit = ((new_len / 1024) + 1) * 1024;
-- rbuf->b = enif_realloc(env, rbuf->b, rbuf->limit);
-+ rbuf->b = ENIF_REALLOC(rbuf->b, rbuf->limit);
- };
- }
-
-@@ -141,7 +165,7 @@ static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el)
-
- if (enif_get_tuple(env, el, &arity, &tuple)) {
- if (arity == 2) {
-- if (!enif_compare(env, tuple[0], enif_make_atom(env, "xmlcdata"))) {
-+ if (!ENIF_COMPARE(tuple[0], atom_xmlcdata)) {
- if (enif_inspect_iolist_as_binary(env, tuple[1], &cdata)) {
- crypt(env, rbuf, cdata.data, cdata.size);
- ret = 1;
-@@ -149,7 +173,7 @@ static int make_element(ErlNifEnv* env, struct buf *rbuf, ERL_NIF_TERM el)
- };
- };
- if (arity == 4) {
-- if (!enif_compare(env, tuple[0], enif_make_atom(env, "xmlelement"))) {
-+ if (!ENIF_COMPARE(tuple[0], atom_xmlelement)) {
- if (enif_inspect_iolist_as_binary(env, tuple[1], &name)) {
- buf_add_char(env, rbuf, '<');
- buf_add_str(env, rbuf, (char *)name.data, name.size);
-@@ -192,7 +216,7 @@ static ERL_NIF_TERM element_to(ErlNifEnv* env, int argc,
- destroy_buf(env, rbuf);
- return result;
- } else {
-- if (enif_alloc_binary(env, rbuf->len, &output)) {
-+ if (ENIF_ALLOC_BINARY(rbuf->len, &output)) {
- memcpy(output.data, rbuf->b, rbuf->len);
- result = enif_make_binary(env, &output);
- destroy_buf(env, rbuf);
-@@ -206,11 +230,13 @@ static ERL_NIF_TERM element_to(ErlNifEnv* env, int argc,
- return enif_make_badarg(env);
- }
-
--/* static ERL_NIF_TERM element_to_string(ErlNifEnv* env, int argc, */
--/* const ERL_NIF_TERM argv[]) */
--/* { */
--/* return element_to(env, argc, argv, 1); */
--/* } */
-+#ifdef SSL40
-+static ERL_NIF_TERM element_to_string(ErlNifEnv* env, int argc,
-+ const ERL_NIF_TERM argv[])
-+{
-+ return element_to(env, argc, argv, 1);
-+}
-+#endif
-
- static ERL_NIF_TERM element_to_binary(ErlNifEnv* env, int argc,
- const ERL_NIF_TERM argv[])
-@@ -222,9 +248,11 @@ static ErlNifFunc nif_funcs[] =
- {
- /* Stupid Erlang bug with enif_make_string() is fixed
- in R14A only (OTP-8685), so we can't use
-- element_to_string yet.*/
-- /* {"element_to_string", 1, element_to_string}, */
-+ element_to_string in Erlang < R14A.*/
-+#ifdef SSL40
-+ {"element_to_string", 1, element_to_string},
-+#endif
- {"element_to_binary", 1, element_to_binary}
- };
-
--ERL_NIF_INIT(xml, nif_funcs, NULL, NULL, NULL, NULL)
-+ERL_NIF_INIT(xml, nif_funcs, load, NULL, NULL, NULL)
---
-1.7.2.1
-
diff --git a/ejabberd-0012-fixes-typo-for-table-copy.patch b/ejabberd-0012-fixes-typo-for-table-copy.patch
deleted file mode 100644
index ac62cc5..0000000
--- a/ejabberd-0012-fixes-typo-for-table-copy.patch
+++ /dev/null
@@ -1,25 +0,0 @@
-From f8dd973373a139b9857720dcb4405509e5e4fc76 Mon Sep 17 00:00:00 2001
-From: Evgeniy Khramtsov <ekhramtsov@process-one.net>
-Date: Tue, 10 Aug 2010 19:42:22 +1000
-Subject: [PATCH 08/11] fixes typo for table copy
-
----
- src/odbc/ejabberd_odbc_sup.erl | 2 +-
- 1 files changed, 1 insertions(+), 1 deletions(-)
-
-diff --git a/src/odbc/ejabberd_odbc_sup.erl b/src/odbc/ejabberd_odbc_sup.erl
-index 45ede18..8fb6e25 100644
---- a/src/odbc/ejabberd_odbc_sup.erl
-+++ b/src/odbc/ejabberd_odbc_sup.erl
-@@ -54,7 +54,7 @@ start_link(Host) ->
- {type, bag},
- {local_content, true},
- {attributes, record_info(fields, sql_pool)}]),
-- mnesia:add_table_copy(local_config, node(), ram_copies),
-+ mnesia:add_table_copy(sql_pool, node(), ram_copies),
- F = fun() ->
- mnesia:delete({sql_pool, Host})
- end,
---
-1.7.2.1
-
diff --git a/ejabberd-0013-fixes-ampersand-escaping-EJAB-1258.patch b/ejabberd-0013-fixes-ampersand-escaping-EJAB-1258.patch
deleted file mode 100644
index 2db4aba..0000000
--- a/ejabberd-0013-fixes-ampersand-escaping-EJAB-1258.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From 3024bb0cbf359f3e14b5386ed14ecfb168e8a267 Mon Sep 17 00:00:00 2001
-From: Evgeniy Khramtsov <ekhramtsov@process-one.net>
-Date: Thu, 19 Aug 2010 16:28:31 +1000
-Subject: [PATCH 09/11] fixes ampersand escaping (EJAB-1258)
-
----
- src/eldap/eldap_filter.erl | 4 ++--
- 1 files changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/eldap/eldap_filter.erl b/src/eldap/eldap_filter.erl
-index 51dac5e..341fba1 100644
---- a/src/eldap/eldap_filter.erl
-+++ b/src/eldap/eldap_filter.erl
-@@ -171,7 +171,7 @@ do_sub(S, {RegExp, New, Times}, Iter) ->
- end.
-
- replace_amps(String) ->
-- lists:map(
-+ lists:flatmap(
- fun($&) -> "\\&";
-- (Chr) -> Chr
-+ (Chr) -> [Chr]
- end, String).
---
-1.7.2.1
-
diff --git a/ejabberd-0014-Bugfix-http-poll-for-correctly-parsing-binary-thanks.patch b/ejabberd-0014-Bugfix-http-poll-for-correctly-parsing-binary-thanks.patch
deleted file mode 100644
index 6fa5aa8..0000000
--- a/ejabberd-0014-Bugfix-http-poll-for-correctly-parsing-binary-thanks.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From d87fff1a4c500d9abe3945cbfea792b4eddfa3f2 Mon Sep 17 00:00:00 2001
-From: Badlop <badlop@process-one.net>
-Date: Thu, 19 Aug 2010 17:37:21 +0200
-Subject: [PATCH 10/11] Bugfix http-poll for correctly parsing binary (thanks to Peter Lemenkov)
-
----
- src/web/ejabberd_http_poll.erl | 8 +++++++-
- 1 files changed, 7 insertions(+), 1 deletions(-)
-
-diff --git a/src/web/ejabberd_http_poll.erl b/src/web/ejabberd_http_poll.erl
-index 0bdbc62..faa0f3c 100644
---- a/src/web/ejabberd_http_poll.erl
-+++ b/src/web/ejabberd_http_poll.erl
-@@ -272,7 +272,13 @@ handle_event(_Event, StateName, StateData) ->
- %% {stop, Reason, Reply, NewStateData}
- %%----------------------------------------------------------------------
- handle_sync_event({send, Packet}, _From, StateName, StateData) ->
-- Output = StateData#state.output ++ [lists:flatten(Packet)],
-+ Packet2 = if
-+ is_binary(Packet) ->
-+ binary_to_list(Packet);
-+ true ->
-+ Packet
-+ end,
-+ Output = StateData#state.output ++ [lists:flatten(Packet2)],
- Reply = ok,
- {reply, Reply, StateName, StateData#state{output = Output}};
-
---
-1.7.2.1
-
diff --git a/ejabberd.spec b/ejabberd.spec
index 9efb71a..b26a5b2 100644
--- a/ejabberd.spec
+++ b/ejabberd.spec
@@ -10,8 +10,8 @@
%endif
Name: ejabberd
-Version: 2.1.5
-Release: 6%{?dist}
+Version: 2.1.6
+Release: 1%{?dist}
Summary: A distributed, fault-tolerant Jabber/XMPP server
Group: Applications/Internet
@@ -22,17 +22,6 @@ Source1: ejabberd.init
Source2: ejabberd.logrotate
Source3: ejabberd.sysconfig
-# http://www.ejabberd.im/mod_ctlextra
-# 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
-Source4: mod_ctlextra.erl
-
-# The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html
-# These are unmaintained and will be removed soon (users should use *_ldap.erl instead)
-Source5: ejabberd_auth_ad.erl
-Source6: mod_shared_roster_ad.erl
-Source7: mod_vcard_ad.erl
-
# PAM support
Source9: ejabberdctl.pam
Source10: ejabberdctl.apps
@@ -40,33 +29,24 @@ 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
-# Mention mod_ctlextra as an ejabberd module
-Patch2: ejabberd-0002-Mention-mod_ctlextra-as-an-ejabberd-module.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
# Introducing mod_admin_extra
Patch4: ejabberd-0004-Introducing-mod_admin_extra.patch
# BZ# 439583, 452326, 451554, 465196, 502361 (fedora/epel-specific)
Patch5: ejabberd-0005-Fedora-specific-changes-to-ejabberdctl.patch
-# Fix issue with "File operation error: eacces"
-Patch6: ejabberd-0006-Change-directory-to-readable-by-everyone.patch
# Fix so-lib permissions while installing (fedora/epel-specific)
-Patch7: ejabberd-0007-Install-.so-objects-with-0755-permissions.patch
-# Will be proposed for incusion into upstream
-Patch8: ejabberd-0008-Use-versioned-directory-for-storing-docs.patch
-# Backported from upstream
-Patch9: ejabberd-0009-Support-SASL-GSSAPI-authentication-thanks-to-Mikael-.patch
-# Backported from upstream
-Patch10: ejabberd-0010-Loading-ASN.1-driver-explicitly-to-avoid-races-in-LD.patch
-# Backported from upstream
-Patch11: ejabberd-0011-Make-xml.c-correctly-compiles-on-R14A-EJAB-1288.patch
+Patch6: ejabberd-0006-Install-.so-objects-with-0755-permissions.patch
+# Will be proposed for inclusion into upstream
+Patch7: ejabberd-0007-Use-versioned-directory-for-storing-docs.patch
# Backported from upstream
-Patch12: ejabberd-0012-fixes-typo-for-table-copy.patch
-# Backported from upstream
-Patch13: ejabberd-0013-fixes-ampersand-escaping-EJAB-1258.patch
-# Backported from upstream
-Patch14: ejabberd-0014-Bugfix-http-poll-for-correctly-parsing-binary-thanks.patch
-
+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
+# Correct version in configure (DON'T FORGET TO REMOVE IN THE NEXT VERSION)
+Patch10: ejabberd-0010-last-minute-fix-correct-version-in-configure.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -127,20 +107,12 @@ Documentation for ejabberd.
%patch3 -p1 -b .s2s_delays
%patch4 -p1 -b .mod_admin_extra
%patch5 -p1 -b .fedora_specific
-%patch6 -p1 -b .fix_access
-%patch7 -p1 -b .fix_perms
-%patch8 -p1 -b .versioned_docdir
-%patch9 -p1 -b .gssapi
-%patch10 -p1 -b .load_asn1
-%patch11 -p1 -b .R14A
-%patch12 -p1 -b .typo
-%patch13 -p1 -b .ldap_ampersand_escaping
-%patch14 -p1 -b .http-poll
-
-cp %{S:4} src
-cp %{S:5} src
-cp %{S:6} src
-cp %{S:7} src
+%patch6 -p1 -b .fix_perms
+%patch7 -p1 -b .versioned_docdir
+%patch8 -p1 -b .gssapi
+%patch9 -p1 -b .ad_stuff
+%patch10 -p1 -b .fix_version
+touch -r src/configure.fix_version src/configure
%build
@@ -366,6 +338,9 @@ rm -rf %{buildroot}
%doc %{_docdir}/%{name}-%{version}/*.txt
%changelog
+* Tue Dec 14 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.6-1
+- Ver. 2.1.6 (Bugfix release)
+
* Thu Aug 26 2010 Peter Lemenkov <lemenkov@gmail.com> 2.1.5-6
- More patches from trunk
- Rebased patches
diff --git a/ejabberd_auth_ad.erl b/ejabberd_auth_ad.erl
deleted file mode 100644
index 5e9e452..0000000
--- a/ejabberd_auth_ad.erl
+++ /dev/null
@@ -1,167 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% 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/mod_ctlextra.erl b/mod_ctlextra.erl
deleted file mode 100644
index bc61024..0000000
--- a/mod_ctlextra.erl
+++ /dev/null
@@ -1,895 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% 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).
diff --git a/mod_shared_roster_ad.erl b/mod_shared_roster_ad.erl
deleted file mode 100644
index 8dc09f1..0000000
--- a/mod_shared_roster_ad.erl
+++ /dev/null
@@ -1,428 +0,0 @@
-%%%----------------------------------------------------------------------
-%%% 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/mod_vcard_ad.erl b/mod_vcard_ad.erl
deleted file mode 100644
index 52ddbec..0000000
--- a/mod_vcard_ad.erl
+++ /dev/null
@@ -1,631 +0,0 @@
-
-%%%----------------------------------------------------------------------
-%%% 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.
-
diff --git a/sources b/sources
index ef0833a..408014a 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-2029ceca45584d704ca821a771d6d928 ejabberd-2.1.5.tar.gz
+f5a41edee2204c414150075331ced506 ejabberd-2.1.6.tar.gz
reply other threads:[~2026-09-23 19:07 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179019042231.1.5570967641490648007.rpms-ejabberd-5c2945ff47a1@fedoraproject.org \
--to=lemenkov@gmail.com \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox