summaryrefslogtreecommitdiff
path: root/src/mod_vcard.erl
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-18 15:01:32 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-18 15:01:32 +0300
commit9a8e197d7e03298fce9cec030ae961ca7814419e (patch)
treefe78750783d006b27b66340b1bdff41a7c636ae9 /src/mod_vcard.erl
parentOmit [info] message with number of queued stanzas (diff)
Initial version based on XML generator
Diffstat (limited to 'src/mod_vcard.erl')
-rw-r--r--src/mod_vcard.erl592
1 files changed, 240 insertions, 352 deletions
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index aca9d746..de9fce00 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -33,13 +33,15 @@
-behaviour(gen_mod).
-export([start/2, init/3, stop/1, get_sm_features/5,
- process_local_iq/3, process_sm_iq/3, string2lower/1,
+ process_local_iq/1, process_sm_iq/1, string2lower/1,
remove_user/2, export/1, import/1, import/3, depends/2,
+ process_search/1, process_vcard/1,
+ disco_items/5, disco_features/5, disco_identity/5,
mod_opt_type/1, set_vcard/3, make_vcard_search/4]).
-include("ejabberd.hrl").
-include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
-include("mod_vcard.hrl").
-define(JUD_MATCHES, 30).
@@ -68,11 +70,30 @@ start(Host, Opts) ->
?NS_VCARD, ?MODULE, process_sm_iq, IQDisc),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
get_sm_features, 50),
- MyHost = gen_mod:get_opt_host(Host, Opts,
- <<"vjud.@HOST@">>),
+ 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,
false),
+ if Search ->
+ ejabberd_hooks:add(
+ disco_local_items, MyHost, ?MODULE, disco_items, 100),
+ ejabberd_hooks:add(
+ disco_local_features, MyHost, ?MODULE, disco_features, 100),
+ ejabberd_hooks:add(
+ disco_local_identity, MyHost, ?MODULE, disco_identity, 100),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_SEARCH, ?MODULE, process_search, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_VCARD, ?MODULE, process_vcard, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_DISCO_ITEMS, mod_disco,
+ process_local_iq_items, IQDisc),
+ gen_iq_handler:add_iq_handler(
+ ejabberd_local, MyHost, ?NS_DISCO_INFO, mod_disco,
+ process_local_iq_info, IQDisc);
+ true ->
+ ok
+ end,
register(gen_mod:get_module_proc(Host, ?PROCNAME),
spawn(?MODULE, init, [MyHost, Host, Search])).
@@ -87,12 +108,20 @@ init(Host, ServerHost, Search) ->
loop(Host, ServerHost) ->
receive
{route, From, To, Packet} ->
- case catch do_route(ServerHost, From, To, Packet) of
+ case catch do_route(From, To, Packet) of
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
_ -> ok
end,
loop(Host, ServerHost);
- stop -> ejabberd_router:unregister_route(Host), ok;
+ stop ->
+ ejabberd_router:unregister_route(Host),
+ ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 50),
+ ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50),
+ ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 50),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_SEARCH),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
+ gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO);
_ -> loop(Host, ServerHost)
end.
@@ -109,12 +138,23 @@ stop(Host) ->
Proc ! stop,
{wait, Proc}.
+do_route(From, To, #xmlel{name = <<"iq">>} = El) ->
+ ejabberd_router:process_iq(From, To, El);
+do_route(From, To, #iq{} = IQ) ->
+ ejabberd_router:process_iq(From, To, IQ);
+do_route(_, _, _) ->
+ ok.
+
+-spec get_sm_features({error, error()} | empty | {result, [binary()]},
+ jid(), jid(),
+ undefined | binary(), undefined | binary()) ->
+ {error, error()} | empty | {result, [binary()]}.
get_sm_features({error, _Error} = Acc, _From, _To,
_Node, _Lang) ->
Acc;
get_sm_features(Acc, _From, _To, Node, _Lang) ->
case Node of
- <<"">> ->
+ undefined ->
case Acc of
{result, Features} ->
{result, [?NS_DISCO_INFO, ?NS_VCARD | Features]};
@@ -123,67 +163,113 @@ get_sm_features(Acc, _From, _To, Node, _Lang) ->
_ -> Acc
end.
-process_local_iq(_From, _To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]};
- 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-2016 ProcessOne">>}]},
- #xmlel{name = <<"BDAY">>, attrs = [],
- children =
- [{xmlcdata, <<"2002-11-16">>}]}]}]}
- end.
-
-process_sm_iq(From, To,
- #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
- case Type of
- set ->
- #jid{user = User, lserver = LServer} = From,
- case lists:member(LServer, ?MYHOSTS) of
- true ->
- set_vcard(User, LServer, SubEl),
- IQ#iq{type = result, sub_el = []};
- false ->
- Txt = <<"The query is only allowed from local users">>,
- IQ#iq{type = error, sub_el = [SubEl, ?ERRT_NOT_ALLOWED(Lang, Txt)]}
- end;
- get ->
- #jid{luser = LUser, lserver = LServer} = To,
- case get_vcard(LUser, LServer) of
- error ->
- Txt = <<"Database failure">>,
- IQ#iq{type = error,
- sub_el = [SubEl, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt)]};
- [] ->
- IQ#iq{type = result,
- sub_el = [#xmlel{name = <<"vCard">>,
- attrs = [{<<"xmlns">>, ?NS_VCARD}],
- children = []}]};
- Els -> IQ#iq{type = result, sub_el = Els}
- end
+-spec process_local_iq(iq()) -> iq().
+process_local_iq(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_local_iq(#iq{type = get, lang = Lang} = IQ) ->
+ Desc = translate:translate(Lang, <<"Erlang Jabber Server">>),
+ Copyright = <<"Copyright (c) 2002-2016 ProcessOne">>,
+ xmpp:make_iq_result(
+ IQ, #vcard_temp{fn = <<"ejabberd">>,
+ url = ?EJABBERD_URI,
+ desc = <<Desc/binary, $\n, Copyright/binary>>,
+ bday = <<"2002-11-16">>}).
+
+-spec process_sm_iq(iq()) -> iq().
+process_sm_iq(#iq{type = set, lang = Lang, from = From,
+ sub_els = [SubEl]} = IQ) ->
+ #jid{user = User, lserver = LServer} = From,
+ case lists:member(LServer, ?MYHOSTS) of
+ true ->
+ set_vcard(User, LServer, SubEl),
+ xmpp:make_iq_result(IQ);
+ false ->
+ Txt = <<"The query is only allowed from local users">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang))
+ end;
+process_sm_iq(#iq{type = get, from = From, to = To, lang = Lang} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = To,
+ case get_vcard(LUser, LServer) of
+ error ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang));
+ [] ->
+ xmpp:make_iq_result(IQ, #vcard_temp{});
+ Els ->
+ IQ#iq{type = result, to = From, from = To, sub_els = Els}
end.
+-spec process_vcard(iq()) -> iq().
+process_vcard(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+ xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_vcard(#iq{type = get, lang = Lang} = IQ) ->
+ Desc = translate:translate(Lang, <<"ejabberd vCard module">>),
+ Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
+ xmpp:make_iq_result(
+ IQ, #vcard_temp{fn = <<"ejabberd/mod_vcard">>,
+ url = ?EJABBERD_URI,
+ desc = <<Desc/binary, $\n, Copyright/binary>>}).
+
+-spec process_search(iq()) -> iq().
+process_search(#iq{type = get, to = To, lang = Lang} = IQ) ->
+ xmpp:make_iq_result(IQ, mk_search_form(To, Lang));
+process_search(#iq{type = set, to = To, lang = Lang,
+ sub_els = [#search{xdata = #xdata{type = submit,
+ fields = Fs}}]} = IQ) ->
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ ResultXData = search_result(Lang, To, ServerHost, Fs),
+ xmpp:make_iq_result(IQ, #search{xdata = ResultXData});
+process_search(#iq{type = set, lang = Lang} = IQ) ->
+ Txt = <<"Incorrect data form">>,
+ xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)).
+
+-spec disco_items({error, error()} | {result, [disco_item()]} | empty,
+ jid(), jid(),
+ undefined | binary(), undefined | binary()) ->
+ {error, error()} | {result, [disco_item()]}.
+disco_items(empty, _From, _To, undefined, _Lang) ->
+ {result, []};
+disco_items(empty, _From, _To, _Node, Lang) ->
+ {error, xmpp:err_item_not_found(<<"No services available">>, Lang)};
+disco_items(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+-spec disco_features({error, error()} | {result, [binary()]} | empty,
+ jid(), jid(),
+ undefined | binary(), undefined | binary()) ->
+ {error, error()} | {result, [binary()]}.
+disco_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+ Acc;
+disco_features(Acc, _From, _To, undefined, _Lang) ->
+ Features = case Acc of
+ {result, Fs} -> Fs;
+ empty -> []
+ end,
+ {result, [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
+ ?NS_VCARD, ?NS_SEARCH | Features]};
+disco_features(empty, _From, _To, _Node, Lang) ->
+ Txt = <<"No features available">>,
+ {error, xmpp:err_item_not_found(Txt, Lang)};
+disco_features(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+-spec disco_identity([identity()], jid(), jid(), undefined | binary(),
+ undefined | binary()) -> [identity()].
+disco_identity(Acc, _From, _To, undefined, Lang) ->
+ [#identity{category = <<"directory">>,
+ type = <<"user">>,
+ name = translate:translate(Lang, <<"vCard User Search">>)}|Acc];
+disco_identity(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+-spec get_vcard(binary(), binary()) -> [xmlel()] | error.
get_vcard(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:get_vcard(LUser, LServer).
+-spec make_vcard_search(binary(), binary(), binary(), xmlel()) -> #vcard_search{}.
make_vcard_search(User, LUser, LServer, VCARD) ->
FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
Family = fxml:get_path_s(VCARD,
@@ -250,6 +336,7 @@ make_vcard_search(User, LUser, LServer, VCARD) ->
orgunit = OrgUnit,
lorgunit = LOrgUnit}.
+-spec set_vcard(binary(), binary(), xmlel()) -> any().
set_vcard(User, LServer, VCARD) ->
case jid:nodeprep(User) of
error ->
@@ -262,307 +349,108 @@ set_vcard(User, LServer, VCARD) ->
[LUser, LServer, VCARD])
end.
+-spec string2lower(binary()) -> binary().
string2lower(String) ->
case stringprep:tolower(String) of
Lower when is_binary(Lower) -> Lower;
error -> str:to_lower(String)
end.
--define(TLFIELD(Type, Label, Var),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = []}).
-
--define(FORM(JID),
- [#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,
- (jid:to_string(JID))/binary>>}]},
- #xmlel{name = <<"instructions">>, attrs = [],
- children =
- [{xmlcdata,
- translate:translate(Lang,
- <<"Fill in the form to search for any matching "
- "Jabber User (Add * to the end of field "
- "to match substring)">>)}]},
- ?TLFIELD(<<"text-single">>, <<"User">>, <<"user">>),
- ?TLFIELD(<<"text-single">>, <<"Full Name">>, <<"fn">>),
- ?TLFIELD(<<"text-single">>, <<"Name">>, <<"first">>),
- ?TLFIELD(<<"text-single">>, <<"Middle Name">>,
- <<"middle">>),
- ?TLFIELD(<<"text-single">>, <<"Family Name">>,
- <<"last">>),
- ?TLFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>),
- ?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 ->
- Txt = <<"Data form not found">>,
- Err = jlib:make_error_reply(
- Packet, ?ERRT_BAD_REQUEST(Lang, Txt)),
- ejabberd_router:route(To, From, Err);
- _ ->
- XData = jlib:parse_xdata_submit(XDataEl),
- case XData of
- invalid ->
- Txt = <<"Incorrect data form">>,
- Err = jlib:make_error_reply(
- Packet, ?ERRT_BAD_REQUEST(Lang, Txt)),
- 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,
- ServerHost,
- XData)}]}]},
- ejabberd_router:route(To, From,
- jlib:iq_to_xml(ResIQ))
- end
- end;
- get ->
- ResIQ = IQ#iq{type = result,
- sub_el =
- [#xmlel{name = <<"query">>,
- attrs =
- [{<<"xmlns">>,
- ?NS_SEARCH}],
- children = ?FORM(To)}]},
- ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ))
- end;
- #iq{type = Type, xmlns = ?NS_DISCO_INFO, lang = Lang} ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ALLOWED(Lang, Txt)),
- 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_DISCO_INFO}],
- 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, lang = Lang, xmlns = ?NS_DISCO_ITEMS} ->
- case Type of
- set ->
- Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
- Err = jlib:make_error_reply(Packet, ?ERRT_NOT_ALLOWED(Lang, Txt)),
- 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) ->
- [#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-2016 ProcessOne">>}]}].
-
-find_xdata_el(#xmlel{children = SubEls}) ->
- find_xdata_el1(SubEls).
-
-find_xdata_el1([]) -> false;
-find_xdata_el1([#xmlel{name = Name, attrs = Attrs,
- children = SubEls}
- | Els]) ->
- case fxml: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).
-
--define(LFIELD(Label, Var),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = []}).
-
-search_result(Lang, JID, ServerHost, Data) ->
- [#xmlel{name = <<"title">>, attrs = [],
- children =
- [{xmlcdata,
- <<(translate:translate(Lang,
- <<"Search Results for ">>))/binary,
- (jid:to_string(JID))/binary>>}]},
- #xmlel{name = <<"reported">>, attrs = [],
- children =
- [?TLFIELD(<<"text-single">>, <<"Jabber ID">>,
- <<"jid">>),
- ?TLFIELD(<<"text-single">>, <<"Full Name">>, <<"fn">>),
- ?TLFIELD(<<"text-single">>, <<"Name">>, <<"first">>),
- ?TLFIELD(<<"text-single">>, <<"Middle Name">>,
- <<"middle">>),
- ?TLFIELD(<<"text-single">>, <<"Family Name">>,
- <<"last">>),
- ?TLFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>),
- ?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">>)]}]
- ++
- lists:map(fun (R) -> record_to_item(ServerHost, R) end,
- search(ServerHost, Data)).
-
--define(FIELD(Var, Val),
- #xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
-
+-spec mk_tfield(binary(), binary(), undefined | binary()) -> xdata_field().
+mk_tfield(Label, Var, Lang) ->
+ #xdata_field{type = 'text-single',
+ label = translate:translate(Lang, Label),
+ var = Var}.
+
+-spec mk_field(binary(), binary()) -> xdata_field().
+mk_field(Var, Val) ->
+ #xdata_field{var = Var, values = [Val]}.
+
+-spec mk_search_form(jid(), undefined | binary()) -> search().
+mk_search_form(JID, Lang) ->
+ Title = <<(translate:translate(Lang, <<"Search users in ">>))/binary,
+ (jid:to_string(JID))/binary>>,
+ Fs = [mk_tfield(<<"User">>, <<"user">>, Lang),
+ mk_tfield(<<"Full Name">>, <<"fn">>, Lang),
+ mk_tfield(<<"Name">>, <<"first">>, Lang),
+ mk_tfield(<<"Middle Name">>, <<"middle">>, Lang),
+ mk_tfield(<<"Family Name">>, <<"last">>, Lang),
+ mk_tfield(<<"Nickname">>, <<"nick">>, Lang),
+ mk_tfield(<<"Birthday">>, <<"bday">>, Lang),
+ mk_tfield(<<"Country">>, <<"ctry">>, Lang),
+ mk_tfield(<<"City">>, <<"locality">>, Lang),
+ mk_tfield(<<"Email">>, <<"email">>, Lang),
+ mk_tfield(<<"Organization Name">>, <<"orgname">>, Lang),
+ mk_tfield(<<"Organization Unit">>, <<"orgunit">>, Lang)],
+ X = #xdata{type = form,
+ title = Title,
+ instructions =
+ [translate:translate(
+ Lang,
+ <<"Fill in the form to search for any matching "
+ "Jabber User (Add * to the end of field "
+ "to match substring)">>)],
+ fields = Fs},
+ #search{instructions =
+ translate:translate(
+ Lang, <<"You need an x:data capable client to search">>),
+ xdata = X}.
+
+-spec search_result(undefined | binary(), jid(), binary(), [xdata_field()]) -> xdata().
+search_result(Lang, JID, ServerHost, XFields) ->
+ #xdata{type = result,
+ title = <<(translate:translate(Lang,
+ <<"Search Results for ">>))/binary,
+ (jid:to_string(JID))/binary>>,
+ reported = [mk_tfield(<<"Jabber ID">>, <<"jid">>, Lang),
+ mk_tfield(<<"Full Name">>, <<"fn">>, Lang),
+ mk_tfield(<<"Name">>, <<"first">>, Lang),
+ mk_tfield(<<"Middle Name">>, <<"middle">>, Lang),
+ mk_tfield(<<"Family Name">>, <<"last">>, Lang),
+ mk_tfield(<<"Nickname">>, <<"nick">>, Lang),
+ mk_tfield(<<"Birthday">>, <<"bday">>, Lang),
+ mk_tfield(<<"Country">>, <<"ctry">>, Lang),
+ mk_tfield(<<"City">>, <<"locality">>, Lang),
+ mk_tfield(<<"Email">>, <<"email">>, Lang),
+ mk_tfield(<<"Organization Name">>, <<"orgname">>, Lang),
+ mk_tfield(<<"Organization Unit">>, <<"orgunit">>, Lang)],
+ items = lists:map(fun (R) -> record_to_item(ServerHost, R) end,
+ search(ServerHost, XFields))}.
+
+-spec record_to_item(binary(), [binary()] | #vcard_search{}) -> [xdata_field()].
record_to_item(LServer,
[Username, FN, Family, Given, Middle, Nickname, BDay,
CTRY, Locality, EMail, OrgName, OrgUnit]) ->
- #xmlel{name = <<"item">>, attrs = [],
- children =
- [?FIELD(<<"jid">>,
- <<Username/binary, "@", LServer/binary>>),
- ?FIELD(<<"fn">>, FN), ?FIELD(<<"last">>, Family),
- ?FIELD(<<"first">>, Given),
- ?FIELD(<<"middle">>, Middle),
- ?FIELD(<<"nick">>, Nickname), ?FIELD(<<"bday">>, BDay),
- ?FIELD(<<"ctry">>, CTRY),
- ?FIELD(<<"locality">>, Locality),
- ?FIELD(<<"email">>, EMail),
- ?FIELD(<<"orgname">>, OrgName),
- ?FIELD(<<"orgunit">>, OrgUnit)]};
+ [mk_field(<<"jid">>, <<Username/binary, "@", LServer/binary>>),
+ mk_field(<<"fn">>, FN),
+ mk_field(<<"last">>, Family),
+ mk_field(<<"first">>, Given),
+ mk_field(<<"middle">>, Middle),
+ mk_field(<<"nick">>, Nickname),
+ mk_field(<<"bday">>, BDay),
+ mk_field(<<"ctry">>, CTRY),
+ mk_field(<<"locality">>, Locality),
+ mk_field(<<"email">>, EMail),
+ mk_field(<<"orgname">>, OrgName),
+ mk_field(<<"orgunit">>, OrgUnit)];
record_to_item(_LServer, #vcard_search{} = R) ->
{User, Server} = R#vcard_search.user,
- #xmlel{name = <<"item">>, attrs = [],
- children =
- [?FIELD(<<"jid">>, <<User/binary, "@", Server/binary>>),
- ?FIELD(<<"fn">>, (R#vcard_search.fn)),
- ?FIELD(<<"last">>, (R#vcard_search.family)),
- ?FIELD(<<"first">>, (R#vcard_search.given)),
- ?FIELD(<<"middle">>, (R#vcard_search.middle)),
- ?FIELD(<<"nick">>, (R#vcard_search.nickname)),
- ?FIELD(<<"bday">>, (R#vcard_search.bday)),
- ?FIELD(<<"ctry">>, (R#vcard_search.ctry)),
- ?FIELD(<<"locality">>, (R#vcard_search.locality)),
- ?FIELD(<<"email">>, (R#vcard_search.email)),
- ?FIELD(<<"orgname">>, (R#vcard_search.orgname)),
- ?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))]}.
-
-search(LServer, Data) ->
+ [mk_field(<<"jid">>, <<User/binary, "@", Server/binary>>),
+ mk_field(<<"fn">>, (R#vcard_search.fn)),
+ mk_field(<<"last">>, (R#vcard_search.family)),
+ mk_field(<<"first">>, (R#vcard_search.given)),
+ mk_field(<<"middle">>, (R#vcard_search.middle)),
+ mk_field(<<"nick">>, (R#vcard_search.nickname)),
+ mk_field(<<"bday">>, (R#vcard_search.bday)),
+ mk_field(<<"ctry">>, (R#vcard_search.ctry)),
+ mk_field(<<"locality">>, (R#vcard_search.locality)),
+ mk_field(<<"email">>, (R#vcard_search.email)),
+ mk_field(<<"orgname">>, (R#vcard_search.orgname)),
+ mk_field(<<"orgunit">>, (R#vcard_search.orgunit))].
+
+-spec search(binary(), [xdata_field()]) -> [binary()].
+search(LServer, XFields) ->
+ Data = [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- XFields],
Mod = gen_mod:db_mod(LServer, ?MODULE),
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all,
fun(B) when is_boolean(B) -> B end,