diff options
Diffstat (limited to 'src/mod_vcard_ldap.erl')
-rw-r--r-- | src/mod_vcard_ldap.erl | 1317 |
1 files changed, 662 insertions, 655 deletions
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index d3e60774a..be9bc7c53 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -25,125 +25,108 @@ %%%---------------------------------------------------------------------- -module(mod_vcard_ldap). + -author('alexey@process-one.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_sm_features/5, - process_local_iq/3, - process_sm_iq/3, - remove_user/1, - route/4 - ]). +-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_sm_features/5, process_local_iq/3, process_sm_iq/3, + remove_user/1, route/4]). -include("ejabberd.hrl"). + -include("eldap/eldap.hrl"). + -include("jlib.hrl"). -define(PROCNAME, ejabberd_mod_vcard_ldap). --record(state, {serverhost, - myhost, - eldap_id, - search, - servers, - backups, - port, - tls_options, - dn, - base, - password, - uids, - vcard_map, - vcard_map_attrs, - user_filter, - search_filter, - search_fields, - search_reported, - search_reported_attrs, - deref_aliases, - matches - }). +-record(state, + {serverhost = <<"">> :: binary(), + myhost = <<"">> :: binary(), + eldap_id = <<"">> :: binary(), + search = true :: boolean(), + servers = [] :: [binary()], + backups = [] :: [binary()], + port = ?LDAP_PORT :: inet:port_number(), + tls_options = [] :: list(), + dn = <<"">> :: binary(), + base = <<"">> :: binary(), + password = <<"">> :: binary(), + uids = [] :: [{binary()} | {binary(), binary()}], + vcard_map = [] :: [{binary(), binary(), [binary()]}], + vcard_map_attrs = [] :: [binary()], + user_filter = <<"">> :: binary(), + search_filter :: eldap:filter(), + search_fields = [] :: [{binary(), binary()}], + search_reported = [] :: [{binary(), binary()}], + search_reported_attrs = [] :: [binary()], + deref_aliases = never :: never | searching | finding | always, + matches = 0 :: non_neg_integer()}). -define(VCARD_MAP, - [{"NICKNAME", "%u", []}, - {"FN", "%s", ["displayName"]}, - {"FAMILY", "%s", ["sn"]}, - {"GIVEN", "%s", ["givenName"]}, - {"MIDDLE", "%s", ["initials"]}, - {"ORGNAME", "%s", ["o"]}, - {"ORGUNIT", "%s", ["ou"]}, - {"CTRY", "%s", ["c"]}, - {"LOCALITY", "%s", ["l"]}, - {"STREET", "%s", ["street"]}, - {"REGION", "%s", ["st"]}, - {"PCODE", "%s", ["postalCode"]}, - {"TITLE", "%s", ["title"]}, - {"URL", "%s", ["labeleduri"]}, - {"DESC", "%s", ["description"]}, - {"TEL", "%s", ["telephoneNumber"]}, - {"EMAIL", "%s", ["mail"]}, - {"BDAY", "%s", ["birthDay"]}, - {"ROLE", "%s", ["employeeType"]}, - {"PHOTO", "%s", ["jpegPhoto"]} - ]). + [{<<"NICKNAME">>, <<"%u">>, []}, + {<<"FN">>, <<"%s">>, [<<"displayName">>]}, + {<<"FAMILY">>, <<"%s">>, [<<"sn">>]}, + {<<"GIVEN">>, <<"%s">>, [<<"givenName">>]}, + {<<"MIDDLE">>, <<"%s">>, [<<"initials">>]}, + {<<"ORGNAME">>, <<"%s">>, [<<"o">>]}, + {<<"ORGUNIT">>, <<"%s">>, [<<"ou">>]}, + {<<"CTRY">>, <<"%s">>, [<<"c">>]}, + {<<"LOCALITY">>, <<"%s">>, [<<"l">>]}, + {<<"STREET">>, <<"%s">>, [<<"street">>]}, + {<<"REGION">>, <<"%s">>, [<<"st">>]}, + {<<"PCODE">>, <<"%s">>, [<<"postalCode">>]}, + {<<"TITLE">>, <<"%s">>, [<<"title">>]}, + {<<"URL">>, <<"%s">>, [<<"labeleduri">>]}, + {<<"DESC">>, <<"%s">>, [<<"description">>]}, + {<<"TEL">>, <<"%s">>, [<<"telephoneNumber">>]}, + {<<"EMAIL">>, <<"%s">>, [<<"mail">>]}, + {<<"BDAY">>, <<"%s">>, [<<"birthDay">>]}, + {<<"ROLE">>, <<"%s">>, [<<"employeeType">>]}, + {<<"PHOTO">>, <<"%s">>, [<<"jpegPhoto">>]}]). -define(SEARCH_FIELDS, - [{"User", "%u"}, - {"Full Name", "displayName"}, - {"Given Name", "givenName"}, - {"Middle Name", "initials"}, - {"Family Name", "sn"}, - {"Nickname", "%u"}, - {"Birthday", "birthDay"}, - {"Country", "c"}, - {"City", "l"}, - {"Email", "mail"}, - {"Organization Name", "o"}, - {"Organization Unit", "ou"} - ]). + [{<<"User">>, <<"%u">>}, + {<<"Full Name">>, <<"displayName">>}, + {<<"Given Name">>, <<"givenName">>}, + {<<"Middle Name">>, <<"initials">>}, + {<<"Family Name">>, <<"sn">>}, + {<<"Nickname">>, <<"%u">>}, + {<<"Birthday">>, <<"birthDay">>}, + {<<"Country">>, <<"c">>}, {<<"City">>, <<"l">>}, + {<<"Email">>, <<"mail">>}, + {<<"Organization Name">>, <<"o">>}, + {<<"Organization Unit">>, <<"ou">>}]). -define(SEARCH_REPORTED, - [{"Full Name", "FN"}, - {"Given Name", "FIRST"}, - {"Middle Name", "MIDDLE"}, - {"Family Name", "LAST"}, - {"Nickname", "NICK"}, - {"Birthday", "BDAY"}, - {"Country", "CTRY"}, - {"City", "LOCALITY"}, - {"Email", "EMAIL"}, - {"Organization Name", "ORGNAME"}, - {"Organization Unit", "ORGUNIT"} - ]). - -%% Unused callbacks. -handle_cast(_Request, State) -> - {noreply, State}. -code_change(_OldVsn, State, _Extra) -> - {ok, State}. -%% ----- - + [{<<"Full Name">>, <<"FN">>}, + {<<"Given Name">>, <<"FIRST">>}, + {<<"Middle Name">>, <<"MIDDLE">>}, + {<<"Family Name">>, <<"LAST">>}, + {<<"Nickname">>, <<"NICK">>}, + {<<"Birthday">>, <<"BDAY">>}, + {<<"Country">>, <<"CTRY">>}, + {<<"City">>, <<"LOCALITY">>}, + {<<"Email">>, <<"EMAIL">>}, + {<<"Organization Name">>, <<"ORGNAME">>}, + {<<"Organization Unit">>, <<"ORGUNIT">>}]). + +handle_cast(_Request, State) -> {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. start(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = { - Proc, {?MODULE, start_link, [Host, Opts]}, - transient, 1000, worker, [?MODULE] - }, + ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, + transient, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_sup, ChildSpec). stop(Host) -> @@ -154,125 +137,126 @@ stop(Host) -> terminate(_Reason, State) -> Host = State#state.serverhost, - 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), + 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), case State#state.search of - true -> - ejabberd_router:unregister_route(State#state.myhost); - _ -> - ok + true -> + ejabberd_router:unregister_route(State#state.myhost); + _ -> ok end. start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). + gen_server:start_link({local, Proc}, ?MODULE, + [Host, Opts], []). init([Host, Opts]) -> State = parse_options(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), + IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, + 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), eldap_pool:start_link(State#state.eldap_id, - State#state.servers, - State#state.backups, - State#state.port, - State#state.dn, - State#state.password, - State#state.tls_options), + State#state.servers, State#state.backups, + State#state.port, State#state.dn, + State#state.password, State#state.tls_options), case State#state.search of - true -> - ejabberd_router:register_route(State#state.myhost); - _ -> - ok + true -> + ejabberd_router:register_route(State#state.myhost); + _ -> ok end, {ok, State}. handle_info({route, From, To, Packet}, State) -> case catch do_route(State, From, To, Packet) of - Pid when is_pid(Pid) -> - ok; - _ -> - Err = jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR), - ejabberd_router:route(To, From, Err) + Pid when is_pid(Pid) -> ok; + _ -> + Err = jlib:make_error_reply(Packet, + ?ERR_INTERNAL_SERVER_ERROR), + ejabberd_router:route(To, From, Err) end, {noreply, State}; +handle_info(_Info, State) -> {noreply, State}. -handle_info(_Info, State) -> - {noreply, State}. - -get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> +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 + <<"">> -> + 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) -> +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, ?EJABBERD_URI}]}, - {xmlelement, "DESC", [], - [{xmlcdata, - translate:translate( - Lang, - "Erlang Jabber Server") ++ - "\nCopyright (c) 2002-2013 ProcessOne"}]}, - {xmlelement, "BDAY", [], - [{xmlcdata, "2002-11-16"}]} - ]}]} + set -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; + get -> + IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"vCard">>, + attrs = [{<<"xmlns">>, ?NS_VCARD}], + children = + [#xmlel{name = <<"FN">>, attrs = [], + children = + [{xmlcdata, <<"ejabberd">>}]}, + #xmlel{name = <<"URL">>, attrs = [], + children = [{xmlcdata, ?EJABBERD_URI}]}, + #xmlel{name = <<"DESC">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"Erlang Jabber Server">>))/binary, + "\nCopyright (c) 2002-2013 ProcessOne">>}]}, + #xmlel{name = <<"BDAY">>, attrs = [], + children = + [{xmlcdata, <<"2002-11-16">>}]}]}]} end. -process_sm_iq(_From, #jid{lserver=LServer} = To, #iq{sub_el = SubEl} = IQ) -> +process_sm_iq(_From, #jid{lserver = LServer} = To, + #iq{sub_el = SubEl} = IQ) -> case catch process_vcard_ldap(To, IQ, LServer) of - {'EXIT', _} -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}; - Other -> - Other + {'EXIT', _} -> + IQ#iq{type = error, + sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}; + Other -> Other end. process_vcard_ldap(To, IQ, Server) -> {ok, State} = eldap_utils:get_state(Server, ?PROCNAME), #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} = To, - LServer = State#state.serverhost, - case ejabberd_auth:is_user_exists(LUser, LServer) of - true -> - VCardMap = State#state.vcard_map, - case find_ldap_user(LUser, State) of - #eldap_entry{attributes = Attributes} -> - Vcard = ldap_attributes_to_vcard(Attributes, VCardMap, {LUser, LServer}), - IQ#iq{type = result, sub_el = Vcard}; - _ -> - IQ#iq{type = result, sub_el = []} - end; - _ -> - IQ#iq{type = result, sub_el = []} - end - end. + set -> + IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}; + get -> + #jid{luser = LUser} = To, + LServer = State#state.serverhost, + case ejabberd_auth:is_user_exists(LUser, LServer) of + true -> + VCardMap = State#state.vcard_map, + case find_ldap_user(LUser, State) of + #eldap_entry{attributes = Attributes} -> + Vcard = ldap_attributes_to_vcard(Attributes, VCardMap, + {LUser, LServer}), + IQ#iq{type = result, sub_el = Vcard}; + _ -> IQ#iq{type = result, sub_el = []} + end; + _ -> IQ#iq{type = result, sub_el = []} + end + end. handle_call(get_state, _From, State) -> {reply, {ok, State}, State}; @@ -286,124 +270,167 @@ find_ldap_user(User, State) -> RFC2254_Filter = State#state.user_filter, Eldap_ID = State#state.eldap_id, VCardAttrs = State#state.vcard_map_attrs, - case eldap_filter:parse(RFC2254_Filter, [{"%u", User}]) of - {ok, EldapFilter} -> - case eldap_pool:search(Eldap_ID, - [{base, Base}, - {filter, EldapFilter}, - {deref_aliases, State#state.deref_aliases}, - {attributes, VCardAttrs}]) of - #eldap_search_result{entries = [E | _]} -> - E; - _ -> - false - end; - _ -> - false + case eldap_filter:parse(RFC2254_Filter, + [{<<"%u">>, User}]) + of + {ok, EldapFilter} -> + case eldap_pool:search(Eldap_ID, + [{base, Base}, {filter, EldapFilter}, + {deref_aliases, State#state.deref_aliases}, + {attributes, VCardAttrs}]) + of + #eldap_search_result{entries = [E | _]} -> E; + _ -> false + end; + _ -> false end. ldap_attributes_to_vcard(Attributes, VCardMap, UD) -> - Attrs = lists:map( - fun({VCardName, _, _}) -> - {stringprep:tolower(VCardName), - map_vcard_attr(VCardName, Attributes, VCardMap, UD)} - end, VCardMap), - Elts = [ldap_attribute_to_vcard(vCard, Attr) || Attr <- Attrs], - NElts = [ldap_attribute_to_vcard(vCardN, Attr) || Attr <- Attrs], - OElts = [ldap_attribute_to_vcard(vCardO, Attr) || Attr <- Attrs], - AElts = [ldap_attribute_to_vcard(vCardA, Attr) || Attr <- Attrs], - [{xmlelement, "vCard", [{"xmlns", ?NS_VCARD}], - lists:append([X || X <- Elts, X /= none], - [{xmlelement,"N",[], [X || X <- NElts, X /= none]}, - {xmlelement,"ORG",[], [X || X <- OElts, X /= none]}, - {xmlelement,"ADR",[], [X || X <- AElts, X /= none]}]) - }]. - -ldap_attribute_to_vcard(vCard, {"fn", Value}) -> - {xmlelement,"FN",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"nickname", Value}) -> - {xmlelement,"NICKNAME",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"title", Value}) -> - {xmlelement,"TITLE",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"bday", Value}) -> - {xmlelement,"BDAY",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"url", Value}) -> - {xmlelement,"URL",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"desc", Value}) -> - {xmlelement,"DESC",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"role", Value}) -> - {xmlelement,"ROLE",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCard, {"tel", Value}) -> - {xmlelement,"TEL",[],[{xmlelement,"VOICE",[],[]}, - {xmlelement,"WORK",[],[]}, - {xmlelement,"NUMBER",[],[{xmlcdata,Value}]}]}; - -ldap_attribute_to_vcard(vCard, {"email", Value}) -> - {xmlelement,"EMAIL",[],[{xmlelement,"INTERNET",[],[]}, - {xmlelement,"PREF",[],[]}, - {xmlelement,"USERID",[],[{xmlcdata,Value}]}]}; - -ldap_attribute_to_vcard(vCard, {"photo", Value}) -> - {xmlelement,"PHOTO",[],[ - {xmlelement,"TYPE",[],[{xmlcdata,"image/jpeg"}]}, - {xmlelement,"BINVAL",[],[{xmlcdata, jlib:encode_base64(Value)}]}]}; - -ldap_attribute_to_vcard(vCardN, {"family", Value}) -> - {xmlelement,"FAMILY",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardN, {"given", Value}) -> - {xmlelement,"GIVEN",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardN, {"middle", Value}) -> - {xmlelement,"MIDDLE",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardO, {"orgname", Value}) -> - {xmlelement,"ORGNAME",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardO, {"orgunit", Value}) -> - {xmlelement,"ORGUNIT",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardA, {"locality", Value}) -> - {xmlelement,"LOCALITY",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardA, {"street", Value}) -> - {xmlelement,"STREET",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardA, {"ctry", Value}) -> - {xmlelement,"CTRY",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardA, {"region", Value}) -> - {xmlelement,"REGION",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(vCardA, {"pcode", Value}) -> - {xmlelement,"PCODE",[],[{xmlcdata,Value}]}; - -ldap_attribute_to_vcard(_, _) -> - none. + Attrs = lists:map(fun ({VCardName, _, _}) -> + {stringprep:tolower(VCardName), + map_vcard_attr(VCardName, Attributes, VCardMap, + UD)} + end, + VCardMap), + Elts = [ldap_attribute_to_vcard(vCard, Attr) + || Attr <- Attrs], + NElts = [ldap_attribute_to_vcard(vCardN, Attr) + || Attr <- Attrs], + OElts = [ldap_attribute_to_vcard(vCardO, Attr) + || Attr <- Attrs], + AElts = [ldap_attribute_to_vcard(vCardA, Attr) + || Attr <- Attrs], + [#xmlel{name = <<"vCard">>, + attrs = [{<<"xmlns">>, ?NS_VCARD}], + children = + lists:append([X || X <- Elts, X /= none], + [#xmlel{name = <<"N">>, attrs = [], + children = [X || X <- NElts, X /= none]}, + #xmlel{name = <<"ORG">>, attrs = [], + children = [X || X <- OElts, X /= none]}, + #xmlel{name = <<"ADR">>, attrs = [], + children = + [X || X <- AElts, X /= none]}])}]. + +ldap_attribute_to_vcard(vCard, {<<"fn">>, Value}) -> + #xmlel{name = <<"FN">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, + {<<"nickname">>, Value}) -> + #xmlel{name = <<"NICKNAME">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"title">>, Value}) -> + #xmlel{name = <<"TITLE">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"bday">>, Value}) -> + #xmlel{name = <<"BDAY">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"url">>, Value}) -> + #xmlel{name = <<"URL">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"desc">>, Value}) -> + #xmlel{name = <<"DESC">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"role">>, Value}) -> + #xmlel{name = <<"ROLE">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCard, {<<"tel">>, Value}) -> + #xmlel{name = <<"TEL">>, attrs = [], + children = + [#xmlel{name = <<"VOICE">>, attrs = [], children = []}, + #xmlel{name = <<"WORK">>, attrs = [], children = []}, + #xmlel{name = <<"NUMBER">>, attrs = [], + children = [{xmlcdata, Value}]}]}; +ldap_attribute_to_vcard(vCard, {<<"email">>, Value}) -> + #xmlel{name = <<"EMAIL">>, attrs = [], + children = + [#xmlel{name = <<"INTERNET">>, attrs = [], + children = []}, + #xmlel{name = <<"PREF">>, attrs = [], children = []}, + #xmlel{name = <<"USERID">>, attrs = [], + children = [{xmlcdata, Value}]}]}; +ldap_attribute_to_vcard(vCard, {<<"photo">>, Value}) -> + #xmlel{name = <<"PHOTO">>, attrs = [], + children = + [#xmlel{name = <<"TYPE">>, attrs = [], + children = [{xmlcdata, <<"image/jpeg">>}]}, + #xmlel{name = <<"BINVAL">>, attrs = [], + children = [{xmlcdata, jlib:encode_base64(Value)}]}]}; +ldap_attribute_to_vcard(vCardN, + {<<"family">>, Value}) -> + #xmlel{name = <<"FAMILY">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardN, {<<"given">>, Value}) -> + #xmlel{name = <<"GIVEN">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardN, + {<<"middle">>, Value}) -> + #xmlel{name = <<"MIDDLE">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardO, + {<<"orgname">>, Value}) -> + #xmlel{name = <<"ORGNAME">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardO, + {<<"orgunit">>, Value}) -> + #xmlel{name = <<"ORGUNIT">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardA, + {<<"locality">>, Value}) -> + #xmlel{name = <<"LOCALITY">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardA, + {<<"street">>, Value}) -> + #xmlel{name = <<"STREET">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardA, {<<"ctry">>, Value}) -> + #xmlel{name = <<"CTRY">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardA, + {<<"region">>, Value}) -> + #xmlel{name = <<"REGION">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(vCardA, {<<"pcode">>, Value}) -> + #xmlel{name = <<"PCODE">>, attrs = [], + children = [{xmlcdata, Value}]}; +ldap_attribute_to_vcard(_, _) -> none. -define(TLFIELD(Type, Label, Var), - {xmlelement, "field", [{"type", Type}, - {"label", translate:translate(Lang, Label)}, - {"var", Var}], []}). + #xmlel{name = <<"field">>, + attrs = + [{<<"type">>, Type}, + {<<"label">>, translate:translate(Lang, Label)}, + {<<"var">>, Var}], + children = []}). -define(FORM(JID, SearchFields), - [{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")}]} - ] ++ lists:map(fun({X,Y}) -> ?TLFIELD("text-single", X, Y) end, SearchFields)}]). + [#xmlel{name = <<"instructions">>, attrs = [], + children = + [{xmlcdata, + translate:translate(Lang, + <<"You need an x:data capable client to " + "search">>)}]}, + #xmlel{name = <<"x">>, + attrs = + [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}], + children = + [#xmlel{name = <<"title">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"Search users in ">>))/binary, + (jlib:jid_to_string(JID))/binary>>}]}, + #xmlel{name = <<"instructions">>, attrs = [], + children = + [{xmlcdata, + translate:translate(Lang, + <<"Fill in fields to search for any matching " + "Jabber User">>)}]}] + ++ + lists:map(fun ({X, Y}) -> + ?TLFIELD(<<"text-single">>, X, Y) + end, + SearchFields)}]). do_route(State, From, To, Packet) -> spawn(?MODULE, route, [State, From, To, Packet]). @@ -411,162 +438,186 @@ do_route(State, From, To, Packet) -> route(State, From, To, Packet) -> #jid{user = User, resource = Resource} = To, ServerHost = State#state.serverhost, - 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, State, XData) - }]}]}, - ejabberd_router:route( - To, From, jlib:iq_to_xml(ResIQ)) - end - end; - get -> - SearchFields = State#state.search_fields, - ResIQ = IQ#iq{type = result, - sub_el = [{xmlelement, - "query", - [{"xmlns", ?NS_SEARCH}], - ?FORM(To, SearchFields) - }]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(ResIQ)) - end; - #iq{type = Type, xmlns = ?NS_DISCO_INFO, lang = Lang} -> - case Type of - set -> - Err = jlib:make_error_reply( - Packet, ?ERR_NOT_ALLOWED), - ejabberd_router:route(To, From, Err); - get -> - Info = ejabberd_hooks:run_fold( - disco_info, ServerHost, [], - [ServerHost, ?MODULE, "", ""]), - ResIQ = - IQ#iq{type = result, - sub_el = [{xmlelement, - "query", - [{"xmlns", ?NS_DISCO_INFO}], - [{xmlelement, "identity", - [{"category", "directory"}, - {"type", "user"}, - {"name", - translate:translate(Lang, "vCard User Search")}], - []}, - {xmlelement, "feature", - [{"var", ?NS_SEARCH}], []}, - {xmlelement, "feature", - [{"var", ?NS_VCARD}], []} - ] ++ Info - }]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(ResIQ)) - end; - #iq{type = Type, xmlns = ?NS_DISCO_ITEMS} -> - 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 + 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 = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, + ?NS_SEARCH}], + children = + [#xmlel{name = + <<"x">>, + attrs = + [{<<"xmlns">>, + ?NS_XDATA}, + {<<"type">>, + <<"result">>}], + children + = + search_result(Lang, + To, + State, + XData)}]}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(ResIQ)) + end + end; + get -> + SearchFields = State#state.search_fields, + ResIQ = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, + ?NS_SEARCH}], + children = + ?FORM(To, SearchFields)}]}, + ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ)) + end; + #iq{type = Type, xmlns = ?NS_DISCO_INFO, lang = Lang} -> + case Type of + set -> + Err = jlib:make_error_reply(Packet, ?ERR_NOT_ALLOWED), + ejabberd_router:route(To, From, Err); + get -> + Info = ejabberd_hooks:run_fold(disco_info, ServerHost, + [], + [ServerHost, ?MODULE, + <<"">>, <<"">>]), + ResIQ = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, + ?NS_DISCO_INFO}], + children = + [#xmlel{name = + <<"identity">>, + attrs = + [{<<"category">>, + <<"directory">>}, + {<<"type">>, + <<"user">>}, + {<<"name">>, + translate:translate(Lang, + <<"vCard User Search">>)}], + children = []}, + #xmlel{name = + <<"feature">>, + attrs = + [{<<"var">>, + ?NS_SEARCH}], + children = []}, + #xmlel{name = + <<"feature">>, + attrs = + [{<<"var">>, + ?NS_VCARD}], + children = []}] + ++ Info}]}, + ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ)) + end; + #iq{type = Type, xmlns = ?NS_DISCO_ITEMS} -> + 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 = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, + ?NS_DISCO_ITEMS}], + children = []}]}, + 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 = + [#xmlel{name = <<"vCard">>, + attrs = [{<<"xmlns">>, ?NS_VCARD}], + children = 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, ?EJABBERD_URI}]}, - {xmlelement, "DESC", [], - [{xmlcdata, translate:translate( - Lang, - "ejabberd vCard module") ++ - "\nCopyright (c) 2003-2013 ProcessOne"}]}]. + [#xmlel{name = <<"FN">>, attrs = [], + children = [{xmlcdata, <<"ejabberd/mod_vcard">>}]}, + #xmlel{name = <<"URL">>, attrs = [], + children = [{xmlcdata, ?EJABBERD_URI}]}, + #xmlel{name = <<"DESC">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"ejabberd vCard module">>))/binary, + "\nCopyright (c) 2003-2013 ProcessOne">>}]}]. -define(LFIELD(Label, Var), - {xmlelement, "field", [{"label", translate:translate(Lang, Label)}, - {"var", Var}], []}). + #xmlel{name = <<"field">>, + attrs = + [{<<"label">>, translate:translate(Lang, Label)}, + {<<"var">>, Var}], + children = []}). search_result(Lang, JID, State, Data) -> SearchReported = State#state.search_reported, - Header = [{xmlelement, "title", [], - [{xmlcdata, translate:translate(Lang, "Search Results for ") ++ - jlib:jid_to_string(JID)}]}, - {xmlelement, "reported", [], - [?TLFIELD("text-single", "Jabber ID", "jid")] ++ - lists:map( - fun({Name, Value}) -> ?TLFIELD("text-single", Name, Value) end, - SearchReported) - }], + Header = [#xmlel{name = <<"title">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"Search Results for ">>))/binary, + (jlib:jid_to_string(JID))/binary>>}]}, + #xmlel{name = <<"reported">>, attrs = [], + children = + [?TLFIELD(<<"text-single">>, <<"Jabber ID">>, + <<"jid">>)] + ++ + lists:map(fun ({Name, Value}) -> + ?TLFIELD(<<"text-single">>, Name, + Value) + end, + SearchReported)}], case search(State, Data) of - error -> - Header; - Result -> - Header ++ Result + error -> Header; + Result -> Header ++ Result end. -define(FIELD(Var, Val), - {xmlelement, "field", [{"var", Var}], - [{xmlelement, "value", [], - [{xmlcdata, Val}]}]}). + #xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}], + children = + [#xmlel{name = <<"value">>, attrs = [], + children = [{xmlcdata, Val}]}]}). search(State, Data) -> Base = State#state.base, @@ -575,247 +626,203 @@ search(State, Data) -> UIDs = State#state.uids, Limit = State#state.matches, ReportedAttrs = State#state.search_reported_attrs, - Filter = eldap:'and'([SearchFilter, eldap_utils:make_filter(Data, UIDs)]), + Filter = eldap:'and'([SearchFilter, + eldap_utils:make_filter(Data, UIDs)]), case eldap_pool:search(Eldap_ID, - [{base, Base}, - {filter, Filter}, - {limit, Limit}, - {deref_aliases, State#state.deref_aliases}, - {attributes, ReportedAttrs}]) of - #eldap_search_result{entries = E} -> - search_items(E, State); - _ -> - error + [{base, Base}, {filter, Filter}, {limit, Limit}, + {deref_aliases, State#state.deref_aliases}, + {attributes, ReportedAttrs}]) + of + #eldap_search_result{entries = E} -> + search_items(E, State); + _ -> error end. search_items(Entries, State) -> LServer = State#state.serverhost, SearchReported = State#state.search_reported, VCardMap = State#state.vcard_map, - UIDs = State#state.uids, - Attributes = lists:map( - fun(E) -> - #eldap_entry{attributes = Attrs} = E, - Attrs - end, Entries), - lists:flatmap( - fun(Attrs) -> - case eldap_utils:find_ldap_attrs(UIDs, Attrs) of - {U, UIDAttrFormat} -> - case eldap_utils:get_user_part(U, UIDAttrFormat) of - {ok, Username} -> - case ejabberd_auth:is_user_exists(Username, LServer) of - true -> - RFields = lists:map( - fun({_, VCardName}) -> - {VCardName, - map_vcard_attr( - VCardName, - Attrs, - VCardMap, - {Username, ?MYNAME})} - end, SearchReported), - Result = [?FIELD("jid", Username ++ "@" ++ LServer)] ++ - [?FIELD(Name, Value) || {Name, Value} <- RFields], - [{xmlelement, "item", [], Result}]; - _ -> - [] - end; - _ -> - [] - end; - "" -> - [] - end - end, Attributes). - -remove_user(_User) -> - true. + UIDs = State#state.uids, + Attributes = lists:map(fun (E) -> + #eldap_entry{attributes = Attrs} = E, Attrs + end, + Entries), + lists:flatmap(fun (Attrs) -> + case eldap_utils:find_ldap_attrs(UIDs, Attrs) of + {U, UIDAttrFormat} -> + case eldap_utils:get_user_part(U, UIDAttrFormat) + of + {ok, Username} -> + case + ejabberd_auth:is_user_exists(Username, + LServer) + of + true -> + RFields = lists:map(fun ({_, + VCardName}) -> + {VCardName, + map_vcard_attr(VCardName, + Attrs, + VCardMap, + {Username, + ?MYNAME})} + end, + SearchReported), + Result = [?FIELD(<<"jid">>, + <<Username/binary, + "@", + LServer/binary>>)] + ++ + [?FIELD(Name, Value) + || {Name, Value} + <- RFields], + [#xmlel{name = <<"item">>, + attrs = [], + children = Result}]; + _ -> [] + end; + _ -> [] + end; + <<"">> -> [] + end + end, + Attributes). + +remove_user(_User) -> true. %%%----------------------- %%% Auxiliary functions. %%%----------------------- map_vcard_attr(VCardName, Attributes, Pattern, UD) -> - Res = lists:filter( - fun({Name, _, _}) -> - eldap_utils:case_insensitive_match(Name, VCardName) - end, Pattern), + Res = lists:filter(fun ({Name, _, _}) -> + eldap_utils:case_insensitive_match(Name, + VCardName) + end, + Pattern), case Res of - [{_, Str, Attrs}] -> - process_pattern(Str, UD, - [eldap_utils:get_ldap_attr(X, Attributes) || X<-Attrs]); - _ -> "" + [{_, Str, Attrs}] -> + process_pattern(Str, UD, + [eldap_utils:get_ldap_attr(X, Attributes) + || X <- Attrs]); + _ -> <<"">> end. process_pattern(Str, {User, Domain}, AttrValues) -> - eldap_filter:do_sub( - Str, - [{"%u", User},{"%d", Domain}] ++ - [{"%s", V, 1} || V <- AttrValues]). + eldap_filter:do_sub(Str, + [{<<"%u">>, User}, {<<"%d">>, Domain}] ++ + [{<<"%s">>, V, 1} || V <- AttrValues]). -find_xdata_el({xmlelement, _Name, _Attrs, SubEls}) -> +find_xdata_el(#xmlel{children = 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) +find_xdata_el1([]) -> false; +find_xdata_el1([#xmlel{name = Name, attrs = Attrs, + children = SubEls} + | Els]) -> + case xml:get_attr_s(<<"xmlns">>, Attrs) of + ?NS_XDATA -> + #xmlel{name = Name, attrs = Attrs, children = SubEls}; + _ -> find_xdata_el1(Els) end; -find_xdata_el1([_ | Els]) -> - find_xdata_el1(Els). +find_xdata_el1([_ | Els]) -> find_xdata_el1(Els). parse_options(Host, Opts) -> - MyHost = gen_mod:get_opt_host(Host, Opts, "vjud.@HOST@"), - Search = gen_mod:get_opt(search, Opts, true), - Matches = case gen_mod:get_opt(matches, Opts, 30) of - infinity -> 0; - N -> N - end, - Eldap_ID = atom_to_list(gen_mod:get_module_proc(Host, ?PROCNAME)), - LDAPServers = case gen_mod:get_opt(ldap_servers, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_servers, Host}); - S -> S - end, - LDAPBackups = case gen_mod:get_opt(ldap_backups, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_servers, Host}); - Backups -> Backups - end, - LDAPEncrypt = case gen_mod:get_opt(ldap_encrypt, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_encrypt, Host}); - E -> E - end, - LDAPTLSVerify = case gen_mod:get_opt(ldap_tls_verify, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_tls_verify, Host}); - Verify -> Verify - end, - LDAPTLSCAFile = case gen_mod:get_opt(ldap_tls_cacertfile, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_tls_cacertfile, Host}); - CAFile -> CAFile - end, - LDAPTLSDepth = case gen_mod:get_opt(ldap_tls_depth, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_tls_depth, Host}); - Depth -> - Depth - end, - LDAPPortTemp = case gen_mod:get_opt(ldap_port, Opts, undefined) of - undefined -> - ejabberd_config:get_local_option({ldap_port, Host}); - PT -> PT - end, - LDAPPort = case LDAPPortTemp of - undefined -> - case LDAPEncrypt of - tls -> ?LDAPS_PORT; - starttls -> ?LDAP_PORT; - _ -> ?LDAP_PORT - 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, - UIDs = case gen_mod:get_opt(ldap_uids, Opts, undefined) of - undefined -> - case ejabberd_config:get_local_option({ldap_uids, Host}) of - undefined -> [{"uid", "%u"}]; - UI -> eldap_utils:uids_domain_subst(Host, UI) - end; - UI -> eldap_utils:uids_domain_subst(Host, UI) - 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, - SubFilter = lists:flatten(eldap_utils:generate_subfilter(UIDs)), - UserFilter = case gen_mod:get_opt(ldap_filter, Opts, undefined) of - undefined -> - case ejabberd_config:get_local_option({ldap_filter, Host}) of - undefined -> SubFilter; - "" -> SubFilter; - F -> - eldap_utils:check_filter(F), - "(&" ++ SubFilter ++ F ++ ")" - end; - "" -> SubFilter; - F -> - eldap_utils:check_filter(F), - "(&" ++ SubFilter ++ F ++ ")" - end, - {ok, SearchFilter} = eldap_filter:parse( - eldap_filter:do_sub(UserFilter, [{"%u","*"}])), - VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts, ?VCARD_MAP), - SearchFields = gen_mod:get_opt(ldap_search_fields, Opts, ?SEARCH_FIELDS), - SearchReported = gen_mod:get_opt(ldap_search_reported, Opts, ?SEARCH_REPORTED), - %% In search requests we need to fetch only attributes defined - %% in vcard-map and search-reported. In some cases, - %% this will essentially reduce network traffic from an LDAP server. - UIDAttrs = [UAttr || {UAttr, _} <- UIDs], - VCardMapAttrs = lists:usort( - lists:append([A || {_, _, A} <- VCardMap]) ++ UIDAttrs), - SearchReportedAttrs = - lists:usort(lists:flatmap( - fun({_, N}) -> - case lists:keysearch(N, 1, VCardMap) of - {value, {_, _, L}} -> L; - _ -> [] - end - end, SearchReported) ++ UIDAttrs), - DerefAliases = case gen_mod:get_opt(deref_aliases, Opts, undefined) of - undefined -> - case ejabberd_config:get_local_option( - {deref_aliases, Host}) of - undefined -> never; - D -> D - end; - D -> D - end, - #state{serverhost = Host, - myhost = MyHost, - eldap_id = Eldap_ID, - search = Search, - servers = LDAPServers, - backups = LDAPBackups, - port = LDAPPort, - tls_options = [{encrypt, LDAPEncrypt}, - {tls_verify, LDAPTLSVerify}, - {tls_cacertfile, LDAPTLSCAFile}, - {tls_depth, LDAPTLSDepth}], - dn = RootDN, - base = LDAPBase, - password = Password, - uids = UIDs, - vcard_map = VCardMap, + MyHost = gen_mod:get_opt_host(Host, Opts, + <<"vjud.@HOST@">>), + Search = gen_mod:get_opt(search, Opts, + fun(B) when is_boolean(B) -> B end, + true), + Matches = gen_mod:get_opt(matches, Opts, + fun(infinity) -> 0; + (I) when is_integer(I), I>0 -> I + end, 30), + Eldap_ID = jlib:atom_to_binary(gen_mod:get_module_proc(Host, ?PROCNAME)), + Cfg = eldap_utils:get_config(Host, Opts), + UIDsTemp = eldap_utils:get_opt( + {ldap_uids, Host}, Opts, + fun(Us) -> + lists:map( + fun({U, P}) -> + {iolist_to_binary(U), + iolist_to_binary(P)}; + ({U}) -> + {iolist_to_binary(U)} + end, Us) + end, [{<<"uid">>, <<"%u">>}]), + UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp), + SubFilter = eldap_utils:generate_subfilter(UIDs), + UserFilter = case eldap_utils:get_opt( + {ldap_filter, Host}, Opts, + fun check_filter/1, <<"">>) of + <<"">> -> + SubFilter; + F -> + <<"(&", SubFilter/binary, F/binary, ")">> + end, + {ok, SearchFilter} = + eldap_filter:parse(eldap_filter:do_sub(UserFilter, + [{<<"%u">>, <<"*">>}])), + VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts, + fun(Ls) -> + lists:map( + fun({S, P, L}) -> + {iolist_to_binary(S), + iolist_to_binary(P), + [iolist_to_binary(E) + || E <- L]} + end, Ls) + end, ?VCARD_MAP), + SearchFields = gen_mod:get_opt(ldap_search_fields, Opts, + fun(Ls) -> + [{iolist_to_binary(S), + iolist_to_binary(P)} + || {S, P} <- Ls] + end, ?SEARCH_FIELDS), + SearchReported = gen_mod:get_opt(ldap_search_reported, Opts, + fun(Ls) -> + [{iolist_to_binary(S), + iolist_to_binary(P)} + || {S, P} <- Ls] + end, ?SEARCH_REPORTED), + UIDAttrs = [UAttr || {UAttr, _} <- UIDs], + VCardMapAttrs = lists:usort(lists:append([A + || {_, _, A} <- VCardMap]) + ++ UIDAttrs), + SearchReportedAttrs = lists:usort(lists:flatmap(fun ({_, + N}) -> + case + lists:keysearch(N, + 1, + VCardMap) + of + {value, + {_, _, L}} -> + L; + _ -> [] + end + end, + SearchReported) + ++ UIDAttrs), + #state{serverhost = Host, myhost = MyHost, + eldap_id = Eldap_ID, search = Search, + servers = Cfg#eldap_config.servers, + backups = Cfg#eldap_config.backups, + port = Cfg#eldap_config.port, + tls_options = Cfg#eldap_config.tls_options, + dn = Cfg#eldap_config.dn, + password = Cfg#eldap_config.password, + base = Cfg#eldap_config.base, + deref_aliases = Cfg#eldap_config.deref_aliases, + uids = UIDs, vcard_map = VCardMap, vcard_map_attrs = VCardMapAttrs, - user_filter = UserFilter, - search_filter = SearchFilter, + user_filter = UserFilter, search_filter = SearchFilter, search_fields = SearchFields, search_reported = SearchReported, search_reported_attrs = SearchReportedAttrs, - deref_aliases = DerefAliases, - matches = Matches - }. + matches = Matches}. + +check_filter(F) -> + NewF = iolist_to_binary(F), + {ok, _} = eldap_filter:parse(NewF), + NewF. |