forked from calculate/calculate-overlay
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
16 KiB
558 lines
16 KiB
diff -uNr ejabberd-2.0.2-beta1.ORIG/src/mod_shared_roster_ldap.erl ejabberd-2.0.2-beta1/src/mod_shared_roster_ldap.erl
|
|
--- mod_shared_roster_ldap.erl
|
|
+++ mod_shared_roster_ldap.erl
|
|
@@ -0,0 +1,553 @@
|
|
+%%%----------------------------------------------------------------------
|
|
+%%% File : mod_shared_roster_ldap.erl
|
|
+%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
|
+%%% Purpose : LDAP shared roster management
|
|
+%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@sevcom.net>
|
|
+%%% Id : $Id: mod_shared_roster.erl 24 2005-04-14 01:15:31Z alexey $
|
|
+%%%----------------------------------------------------------------------
|
|
+
|
|
+%%%----------------------------------------------------------------------
|
|
+%%% Some changes to make it AD friendly and more usable :-)
|
|
+%%% realloc@realloc.spb.ru
|
|
+%%%----------------------------------------------------------------------
|
|
+
|
|
+
|
|
+-module(mod_shared_roster_ldap).
|
|
+-author('alexey@sevcom.net').
|
|
+
|
|
+-behaviour(gen_server).
|
|
+-behaviour(gen_mod).
|
|
+
|
|
+%% gen_server callbacks
|
|
+-export([
|
|
+ init/1,
|
|
+ handle_info/2,
|
|
+ handle_call/3,
|
|
+ handle_cast/2,
|
|
+ terminate/2,
|
|
+ code_change/3
|
|
+]).
|
|
+
|
|
+-export([
|
|
+ start/2,
|
|
+ start_link/2,
|
|
+ stop/1,
|
|
+ get_user_roster/2,
|
|
+ get_subscription_lists/3,
|
|
+ get_jid_info/4,
|
|
+ process_item/2,
|
|
+ in_subscription/6,
|
|
+ out_subscription/4
|
|
+]).
|
|
+
|
|
+-include("ejabberd.hrl").
|
|
+-include("eldap/eldap.hrl").
|
|
+-include("jlib.hrl").
|
|
+-include("mod_roster.hrl").
|
|
+
|
|
+-record(state, {
|
|
+ host,
|
|
+ eldap_id,
|
|
+ servers,
|
|
+ port,
|
|
+ dn,
|
|
+ base,
|
|
+ password,
|
|
+ uid,
|
|
+ group_attr,
|
|
+ group_desc,
|
|
+ user_desc,
|
|
+ uid_format,
|
|
+ filter,
|
|
+ ufilter,
|
|
+ rfilter,
|
|
+ gfilter
|
|
+}).
|
|
+
|
|
+-define(LDAP_REQUEST_TIMEOUT, 10000).
|
|
+
|
|
+%% Unused callbacks.
|
|
+handle_cast(_Request, State) ->
|
|
+ {noreply, State}.
|
|
+code_change(_OldVsn, State, _Extra) ->
|
|
+ {ok, State}.
|
|
+handle_info(_Info, State) ->
|
|
+ {noreply, State}.
|
|
+%% -----
|
|
+
|
|
+start(Host, Opts) ->
|
|
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
|
+ ChildSpec = {
|
|
+ Proc, {?MODULE, start_link, [Host, Opts]},
|
|
+ permanent, 1000, worker, [?MODULE]
|
|
+ },
|
|
+ supervisor:start_child(ejabberd_sup, ChildSpec).
|
|
+
|
|
+stop(Host) ->
|
|
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
|
+ gen_server:call(Proc, stop),
|
|
+ supervisor:terminate_child(ejabberd_sup, Proc),
|
|
+ supervisor:delete_child(ejabberd_sup, Proc).
|
|
+
|
|
+start_link(Host, Opts) ->
|
|
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
|
+ gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
|
|
+
|
|
+terminate(_Reason, State) ->
|
|
+ Host = State#state.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(roster_process_item, Host,
|
|
+ ?MODULE, process_item, 50).
|
|
+
|
|
+init([Host, Opts]) ->
|
|
+ State = parse_options(Host, Opts),
|
|
+ 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(roster_process_item, Host,
|
|
+ ?MODULE, process_item, 50),
|
|
+ eldap:start_link(State#state.eldap_id,
|
|
+ State#state.servers,
|
|
+ State#state.port,
|
|
+ State#state.dn,
|
|
+ State#state.password),
|
|
+ {ok, State}.
|
|
+
|
|
+get_user_roster(Items, US) ->
|
|
+ {U, S} = US,
|
|
+ DisplayedGroups = get_user_displayed_groups(US),
|
|
+ %% Get shared roster users in all groups and remove self:
|
|
+ SRUsers =
|
|
+ lists:foldl(
|
|
+ fun(Group, Acc1) ->
|
|
+ lists:foldl(
|
|
+ fun(User, Acc2) ->
|
|
+ if User == US -> Acc2;
|
|
+ true -> dict:append(User,
|
|
+ get_group_name(S, Group),
|
|
+ Acc2)
|
|
+ end
|
|
+ end, Acc1, get_group_users(S, Group))
|
|
+ end, dict:new(), DisplayedGroups),
|
|
+
|
|
+ %% If partially subscribed users are also in shared roster, show them as
|
|
+ %% totally subscribed:
|
|
+ {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),
|
|
+
|
|
+ %% Export items in roster format:
|
|
+ SRItems = [#roster{usj = {U, S, {U1, S1, ""}},
|
|
+ us = US,
|
|
+ jid = {U1, S1, ""},
|
|
+ name = get_user_name(U1,S1),
|
|
+ subscription = both,
|
|
+ ask = none,
|
|
+ groups = GroupNames} ||
|
|
+ {{U1, S1}, GroupNames} <- dict:to_list(SRUsersRest)],
|
|
+ SRItems ++ NewItems1.
|
|
+
|
|
+%% This function in use to rewrite the roster entries when moving or renaming
|
|
+%% them in the user contact list.
|
|
+process_item(RosterItem, Host) ->
|
|
+ USFrom = RosterItem#roster.us,
|
|
+ {User,Server,_Resource} = RosterItem#roster.jid,
|
|
+ USTo = {User,Server},
|
|
+ DisplayedGroups = get_user_displayed_groups(USFrom),
|
|
+ CommonGroups = lists:filter(fun(Group) ->
|
|
+ is_user_in_group(USTo, Group, Server)
|
|
+ end, DisplayedGroups),
|
|
+ case CommonGroups of
|
|
+ [] -> RosterItem;
|
|
+ %% Roster item cannot be removed: We simply reset the original groups:
|
|
+ _ when RosterItem#roster.subscription == remove ->
|
|
+ GroupNames = lists:map(fun(Group) ->
|
|
+ get_group_name(Host, Group)
|
|
+ end, CommonGroups),
|
|
+ RosterItem#roster{subscription = both, ask = none,
|
|
+ groups=[GroupNames]};
|
|
+ _ -> RosterItem#roster{subscription = both, ask = none}
|
|
+ end.
|
|
+
|
|
+get_subscription_lists({F, T}, User, Server) ->
|
|
+ LUser = jlib:nodeprep(User),
|
|
+ LServer = jlib:nameprep(Server),
|
|
+ US = {LUser, LServer},
|
|
+ DisplayedGroups = get_user_displayed_groups(US),
|
|
+ SRUsers =
|
|
+ lists:usort(
|
|
+ lists:flatmap(
|
|
+ fun(Group) ->
|
|
+ get_group_users(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(US),
|
|
+ SRUsers =
|
|
+ lists:foldl(
|
|
+ fun(Group, Acc1) ->
|
|
+ lists:foldl(
|
|
+ fun(User1, Acc2) ->
|
|
+ dict:append(
|
|
+ User1, get_group_name(LServer, Group), Acc2)
|
|
+ end, Acc1, get_group_users(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, _Reason) ->
|
|
+ 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(US),
|
|
+ SRUsers =
|
|
+ lists:usort(
|
|
+ lists:flatmap(
|
|
+ fun(Group) ->
|
|
+ get_group_users(LServer, Group)
|
|
+ end, DisplayedGroups)),
|
|
+ case lists:member(US1, SRUsers) of
|
|
+ true ->
|
|
+ case Direction of
|
|
+ in ->
|
|
+ {stop, false};
|
|
+ out ->
|
|
+ stop
|
|
+ end;
|
|
+ false ->
|
|
+ Acc
|
|
+ end.
|
|
+
|
|
+get_group_users(Host, Group) ->
|
|
+ make_request(Host, {get_group_users, Group}, []).
|
|
+
|
|
+get_group_name(Host, Group) ->
|
|
+ make_request(Host, {get_group_name, Group}, Group).
|
|
+
|
|
+get_user_displayed_groups({User, Host}) ->
|
|
+ make_request(Host, {get_user_displayed_groups, User}, []).
|
|
+
|
|
+is_user_in_group({User, _Server}, Group, Host) ->
|
|
+ make_request(Host, {is_user_in_group, User, Group}, false).
|
|
+
|
|
+get_user_name(User, Host) ->
|
|
+ make_request(Host, {get_user_name, User},[]).
|
|
+
|
|
+
|
|
+%%%-----------------------
|
|
+%%% Internal functions.
|
|
+%%%-----------------------
|
|
+handle_call({get_user_displayed_groups, User}, _From, State) ->
|
|
+ GroupAttr = State#state.group_attr,
|
|
+ Reply = case eldap_filter:parse(State#state.rfilter) of
|
|
+ {ok, EldapFilter} ->
|
|
+ case eldap:search(State#state.eldap_id, [
|
|
+ {base, State#state.base},
|
|
+ {filter, EldapFilter},
|
|
+ {attributes, [GroupAttr]}]) of
|
|
+ #eldap_search_result{entries = Es} ->
|
|
+ lists:usort(lists:flatmap(
|
|
+ fun(#eldap_entry{attributes = Attrs}) ->
|
|
+ case Attrs of
|
|
+ [{GroupAttr, ValuesList}] ->
|
|
+ ValuesList;
|
|
+ _ ->
|
|
+ []
|
|
+ end
|
|
+ end, Es));
|
|
+ _ ->
|
|
+ []
|
|
+ end;
|
|
+ _ ->
|
|
+ []
|
|
+ end,
|
|
+ {reply, Reply, State};
|
|
+
|
|
+handle_call({get_group_name, Group}, _From, State) ->
|
|
+ GroupDescAttr = State#state.group_desc,
|
|
+ Reply = case eldap_filter:parse(State#state.gfilter, [{"%g", Group}]) of
|
|
+ {ok, EldapFilter} ->
|
|
+ case eldap:search(State#state.eldap_id, [
|
|
+ {base, State#state.base},
|
|
+ {filter, EldapFilter},
|
|
+ {attributes, [GroupDescAttr]}]) of
|
|
+ #eldap_search_result{entries = [
|
|
+ #eldap_entry{attributes =
|
|
+ [{GroupDescAttr, GroupName} | _]}
|
|
+ ]} ->
|
|
+ GroupName;
|
|
+ _ ->
|
|
+ Group
|
|
+ end;
|
|
+ _ ->
|
|
+ Group
|
|
+ end,
|
|
+ {reply, Reply, State};
|
|
+
|
|
+handle_call({get_user_name, User}, _From, State) ->
|
|
+UserDescAttr = State#state.user_desc,
|
|
+Reply = case eldap_filter:parse(State#state.ufilter, [{"%u", User}]) of
|
|
+ {ok, EldapFilter} ->
|
|
+ case eldap:search(State#state.eldap_id, [
|
|
+ {base, State#state.base},
|
|
+ {filter, EldapFilter},
|
|
+ {attributes, [UserDescAttr]}]) of
|
|
+ #eldap_search_result{entries = [
|
|
+ #eldap_entry{attributes =
|
|
+ [{UserDescAttr, UserName} | _]}
|
|
+ ]} ->
|
|
+ UserName;
|
|
+ _ ->
|
|
+ User
|
|
+ end;
|
|
+ _ ->
|
|
+ User
|
|
+ end,
|
|
+ {reply, Reply, State};
|
|
+
|
|
+
|
|
+handle_call({get_group_users, Group}, _From, State) ->
|
|
+ UIDAttr = State#state.uid,
|
|
+ UAF = State#state.uid_format,
|
|
+ Host = State#state.host,
|
|
+ Reply = case eldap_filter:parse(State#state.gfilter, [{"%g", Group}]) of
|
|
+ {ok, EldapFilter} ->
|
|
+ case eldap:search(State#state.eldap_id, [
|
|
+ {base, State#state.base},
|
|
+ {filter, EldapFilter},
|
|
+ {attributes, [UIDAttr]}]) of
|
|
+ #eldap_search_result{entries = Es} ->
|
|
+ lists:usort(lists:flatmap(
|
|
+ fun(#eldap_entry{attributes = Attrs}) ->
|
|
+ case Attrs of
|
|
+ [{UIDAttr, UsersList}] ->
|
|
+ lists:foldl(fun(User, Acc) ->
|
|
+ case catch get_user_part(User, UAF) of
|
|
+ {ok, U} ->
|
|
+ case ejabberd_auth:is_user_exists(U, Host) of
|
|
+ true -> [{U, Host} | Acc];
|
|
+ _ -> Acc
|
|
+ end;
|
|
+ _ -> Acc
|
|
+ end
|
|
+ end, [], UsersList);
|
|
+ _ ->
|
|
+ []
|
|
+ end
|
|
+ end, Es));
|
|
+ _ ->
|
|
+ []
|
|
+ end;
|
|
+ _ ->
|
|
+ []
|
|
+ end,
|
|
+ {reply, Reply, State};
|
|
+
|
|
+handle_call({is_user_in_group, User, Group}, _From, State) ->
|
|
+ Reply = case eldap_filter:parse(State#state.filter,
|
|
+ [{"%u", User}, {"%g", Group}]) of
|
|
+ {ok, EldapFilter} ->
|
|
+ case eldap:search(State#state.eldap_id, [
|
|
+ {base, State#state.base},
|
|
+ {filter, EldapFilter},
|
|
+ {attributes, ["dn"]}]) of
|
|
+ #eldap_search_result{entries = [_|_]} ->
|
|
+ true;
|
|
+ _ ->
|
|
+ false
|
|
+ end;
|
|
+ _ ->
|
|
+ false
|
|
+ end,
|
|
+ {reply, Reply, State};
|
|
+
|
|
+handle_call(stop, _From, State) ->
|
|
+ {stop, normal, ok, State};
|
|
+
|
|
+handle_call(_Request, _From, State) ->
|
|
+ {reply, bad_request, State}.
|
|
+
|
|
+%%%-----------------------
|
|
+%%% Auxiliary functions.
|
|
+%%%-----------------------
|
|
+parse_options(Host, Opts) ->
|
|
+ Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?MODULE)),
|
|
+ LDAPServers = case gen_mod:get_opt(ldap_servers, Opts, undefined) of
|
|
+ undefined ->
|
|
+ ejabberd_config:get_local_option({ldap_servers, Host});
|
|
+ S -> S
|
|
+ end,
|
|
+ LDAPPort = case gen_mod:get_opt(ldap_port, Opts, undefined) of
|
|
+ undefined ->
|
|
+ case ejabberd_config:get_local_option({ldap_port, Host}) of
|
|
+ undefined -> 389;
|
|
+ P -> P
|
|
+ end;
|
|
+ P -> P
|
|
+ end,
|
|
+ LDAPBase = case gen_mod:get_opt(ldap_base, Opts, undefined) of
|
|
+ undefined ->
|
|
+ ejabberd_config:get_local_option({ldap_base, Host});
|
|
+ B -> B
|
|
+ end,
|
|
+ GroupAttr = case gen_mod:get_opt(ldap_groupattr, Opts, undefined) of
|
|
+ undefined -> "cn";
|
|
+ GA -> GA
|
|
+ end,
|
|
+ GroupDesc = case gen_mod:get_opt(ldap_groupdesc, Opts, undefined) of
|
|
+ undefined -> "cn";
|
|
+ GD -> GD
|
|
+ end,
|
|
+ UserDesc = case gen_mod:get_opt(ldap_userdesc, Opts, undefined) of
|
|
+ undefined -> "cn";
|
|
+ UD -> UD
|
|
+ end,
|
|
+ UIDAttr = case gen_mod:get_opt(ldap_memberattr, Opts, undefined) of
|
|
+ undefined -> "memberUid";
|
|
+ UA -> UA
|
|
+ end,
|
|
+ UIDAttrFormat = case gen_mod:get_opt(ldap_memberattr_format, Opts, undefined) of
|
|
+ undefined -> "%u";
|
|
+ UAF -> UAF
|
|
+ end,
|
|
+ RootDN = case gen_mod:get_opt(ldap_rootdn, Opts, undefined) of
|
|
+ undefined ->
|
|
+ case ejabberd_config:get_local_option({ldap_rootdn, Host}) of
|
|
+ undefined -> "";
|
|
+ RDN -> RDN
|
|
+ end;
|
|
+ RDN -> RDN
|
|
+ end,
|
|
+ Password = case gen_mod:get_opt(ldap_password, Opts, undefined) of
|
|
+ undefined ->
|
|
+ case ejabberd_config:get_local_option({ldap_password, Host}) of
|
|
+ undefined -> "";
|
|
+ Pass -> Pass
|
|
+ end;
|
|
+ Pass -> Pass
|
|
+ end,
|
|
+ ConfigFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of
|
|
+ undefined ->
|
|
+ ejabberd_config:get_local_option({ldap_filter, Host});
|
|
+ F ->
|
|
+ F
|
|
+ end,
|
|
+
|
|
+ RosterFilter = case gen_mod:get_opt(ldap_rfilter, Opts, undefined) of
|
|
+ undefined ->
|
|
+ ejabberd_config:get_local_option({ldap_rfilter, Host});
|
|
+ RF ->
|
|
+ RF
|
|
+ end,
|
|
+
|
|
+ SubFilter = "(&("++UIDAttr++"="++UIDAttrFormat++")("++GroupAttr++"=%g))",
|
|
+ UserSubFilter = eldap_filter:do_sub(SubFilter, [{"%g", "*"}]),
|
|
+ GroupSubFilter = eldap_filter:do_sub(SubFilter, [{"%u", "*"}]),
|
|
+ Filter = case ConfigFilter of
|
|
+ undefined -> SubFilter;
|
|
+ "" -> SubFilter;
|
|
+ _ -> "(&" ++ SubFilter ++ ConfigFilter ++ ")"
|
|
+ end,
|
|
+ UserFilter = case ConfigFilter of
|
|
+ undefined -> UserSubFilter;
|
|
+ "" -> UserSubFilter;
|
|
+ _ -> "(&" ++ UserSubFilter ++ ConfigFilter ++ ")"
|
|
+ end,
|
|
+ GroupFilter = case ConfigFilter of
|
|
+ undefined -> GroupSubFilter;
|
|
+ "" -> GroupSubFilter;
|
|
+ _ -> "(&" ++ GroupSubFilter ++ ConfigFilter ++ ")"
|
|
+ end,
|
|
+ #state{
|
|
+ host = Host,
|
|
+ eldap_id = Eldap_ID,
|
|
+ servers = LDAPServers,
|
|
+ port = LDAPPort,
|
|
+ dn = RootDN,
|
|
+ base = LDAPBase,
|
|
+ password = Password,
|
|
+ uid = UIDAttr,
|
|
+ group_attr = GroupAttr,
|
|
+ group_desc = GroupDesc,
|
|
+ user_desc = UserDesc,
|
|
+ uid_format = UIDAttrFormat,
|
|
+ filter = Filter,
|
|
+ ufilter = UserFilter,
|
|
+ rfilter = RosterFilter,
|
|
+ gfilter = GroupFilter
|
|
+ }.
|
|
+
|
|
+get_user_part(String, Pattern) ->
|
|
+ F = fun(S, P) ->
|
|
+ First = string:str(P, "%u"),
|
|
+ TailLength = length(P) - (First+1),
|
|
+ string:sub_string(S, First, length(S) - TailLength)
|
|
+ end,
|
|
+ case catch F(String, Pattern) of
|
|
+ {'EXIT', _} ->
|
|
+ {error, badmatch};
|
|
+ Result ->
|
|
+ case regexp:sub(Pattern, "%u", Result) of
|
|
+ {ok, String, _} -> {ok, Result};
|
|
+ _ -> {error, badmatch}
|
|
+ end
|
|
+ end.
|
|
+
|
|
+make_request(Host, Request, Fallback) ->
|
|
+ Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
|
+ case catch gen_server:call(Proc, Request, ?LDAP_REQUEST_TIMEOUT) of
|
|
+ {'EXIT', _} ->
|
|
+ Fallback;
|
|
+ Result ->
|
|
+ Result
|
|
+ end.
|
|
+
|