summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-29 17:39:13 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-07-29 17:39:13 +0300
commit9bf1bac7df54f5be9cdca9a5d7a36160c40e25dd (patch)
treec40f739fa35b9a5c3d38aa74b798d97b14d190b4 /src
parentRewrite several modules to use XML generator (diff)
Rewrite mod_vcard_ldap to use XML generator
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd_config.erl1
-rw-r--r--src/gen_mod.erl24
-rw-r--r--src/mod_vcard.erl80
-rw-r--r--src/mod_vcard_ldap.erl738
-rw-r--r--src/mod_vcard_mnesia.erl55
-rw-r--r--src/mod_vcard_riak.erl11
-rw-r--r--src/mod_vcard_sql.erl52
7 files changed, 336 insertions, 625 deletions
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 87a91870..5a39df04 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -1010,6 +1010,7 @@ replace_module(mod_private_odbc) -> {mod_private, sql};
replace_module(mod_roster_odbc) -> {mod_roster, sql};
replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, sql};
replace_module(mod_vcard_odbc) -> {mod_vcard, sql};
+replace_module(mod_vcard_ldap) -> {mod_vcard, ldap};
replace_module(mod_vcard_xupdate_odbc) -> {mod_vcard_xupdate, sql};
replace_module(mod_pubsub_odbc) -> {mod_pubsub, sql};
replace_module(Module) ->
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index c4306577..e5b50489 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -48,7 +48,7 @@
opts = [] :: opts() | '_' | '$2'}).
-type opts() :: [{atom(), any()}].
--type db_type() :: sql | mnesia | riak.
+-type db_type() :: sql | mnesia | riak | ldap.
-callback start(binary(), opts()) -> any().
-callback stop(binary()) -> any().
@@ -147,7 +147,7 @@ start_module(Host, Module) ->
-spec start_module(binary(), atom(), opts()) -> any().
start_module(Host, Module, Opts0) ->
- Opts = validate_opts(Module, Opts0),
+ Opts = validate_opts(Host, Module, Opts0),
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts}),
@@ -308,10 +308,10 @@ get_opt_host(Host, Opts, Default) ->
Val = get_opt(host, Opts, fun iolist_to_binary/1, Default),
ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-validate_opts(Module, Opts) ->
+validate_opts(Host, Module, Opts) ->
lists:filtermap(
fun({Opt, Val}) ->
- case catch Module:mod_opt_type(Opt) of
+ case catch validate_opt(Host, Module, Opt, Opts) of
VFun when is_function(VFun) ->
try VFun(Val) of
_ ->
@@ -346,6 +346,22 @@ validate_opts(Module, Opts) ->
false
end, Opts).
+validate_opt(Host, Module, Opt, Opts) ->
+ case Module:mod_opt_type(Opt) of
+ VFun1 when is_function(VFun1) ->
+ VFun1;
+ L1 when is_list(L1) ->
+ DBModule = db_mod(Host, Opts, Module),
+ try DBModule:mod_opt_type(Opt) of
+ VFun2 when is_function(VFun2) ->
+ VFun2;
+ L2 when is_list(L2) ->
+ lists:usort(L1 ++ L2)
+ catch _:undef ->
+ L1
+ end
+ end.
+
-spec db_type(binary() | global, module()) -> db_type();
(opts(), module()) -> db_type().
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index b75b6575..231c42dc 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -49,12 +49,15 @@
-define(PROCNAME, ejabberd_mod_vcard).
-callback init(binary(), gen_mod:opts()) -> any().
+-callback stop(binary()) -> any().
-callback import(binary(), #vcard{} | #vcard_search{}) -> ok | pass.
-callback get_vcard(binary(), binary()) -> [xmlel()] | error.
-callback set_vcard(binary(), binary(),
xmlel(), #vcard_search{}) -> {atomic, any()}.
+-callback search_fields(binary()) -> [{binary(), binary()}].
+-callback search_reported(binary()) -> [{binary(), binary()}].
-callback search(binary(), [{binary(), [binary()]}], boolean(),
- infinity | pos_integer()) -> [binary()].
+ infinity | pos_integer()) -> [{binary(), binary()}].
-callback remove_user(binary(), binary()) -> {atomic, any()}.
start(Host, Opts) ->
@@ -134,6 +137,8 @@ stop(Host) ->
?NS_VCARD),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE,
get_sm_features, 50),
+ Mod = gen_mod:db_type(Host, ?MODULE),
+ Mod:stop(Host),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
Proc ! stop,
{wait, Proc}.
@@ -214,7 +219,8 @@ process_vcard(#iq{type = get, lang = Lang} = IQ) ->
-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));
+ ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+ xmpp:make_iq_result(IQ, mk_search_form(To, ServerHost, Lang));
process_search(#iq{type = set, to = To, lang = Lang,
sub_els = [#search{xdata = #xdata{type = submit,
fields = Fs}}]} = IQ) ->
@@ -366,22 +372,13 @@ mk_tfield(Label, Var, Lang) ->
mk_field(Var, Val) ->
#xdata_field{var = Var, values = [Val]}.
--spec mk_search_form(jid(), undefined | binary()) -> search().
-mk_search_form(JID, Lang) ->
+-spec mk_search_form(jid(), binary(), undefined | binary()) -> search().
+mk_search_form(JID, ServerHost, 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)],
+ Mod = gen_mod:db_mod(ServerHost, ?MODULE),
+ SearchFields = Mod:search_fields(ServerHost),
+ Fs = [mk_tfield(Label, Var, Lang) || {Label, Var} <- SearchFields],
X = #xdata{type = form,
title = Title,
instructions =
@@ -398,55 +395,20 @@ mk_search_form(JID, Lang) ->
-spec search_result(undefined | binary(), jid(), binary(), [xdata_field()]) -> xdata().
search_result(Lang, JID, ServerHost, XFields) ->
+ Mod = gen_mod:db_mod(ServerHost, ?MODULE),
+ Reported = [mk_tfield(Label, Var, Lang) ||
+ {Label, Var} <- Mod:search_reported(ServerHost)],
#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,
+ reported = Reported,
+ items = lists:map(fun (Item) -> item_to_field(Item) 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]) ->
- [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,
- [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 item_to_field([{binary(), binary()}]) -> [xdata_field()].
+item_to_field(Items) ->
+ [mk_field(Var, Value) || {Var, Value} <- Items].
-spec search(binary(), [xdata_field()]) -> [binary()].
search(LServer, XFields) ->
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index a0ad305a..74b20d2f 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -1,53 +1,30 @@
-%%%----------------------------------------------------------------------
-%%% File : mod_vcard_ldap.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Support for VCards from LDAP storage.
-%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
%%%
-%%%
-%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
-%%%
-%%% This program is free software; you can redistribute it and/or
-%%% modify it under the terms of the GNU General Public License as
-%%% published by the Free Software Foundation; either version 2 of the
-%%% License, or (at your option) any later version.
-%%%
-%%% This program is distributed in the hope that it will be useful,
-%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
-%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-%%% General Public License for more details.
-%%%
-%%% You should have received a copy of the GNU General Public License along
-%%% with this program; if not, write to the Free Software Foundation, Inc.,
-%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-%%%
-%%%---------------------u-------------------------------------------------
-
+%%% @end
+%%% Created : 29 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
-module(mod_vcard_ldap).
--behaviour(ejabberd_config).
-
--author('alexey@process-one.net').
-
-behaviour(gen_server).
+-behaviour(mod_vcard).
--behaviour(gen_mod).
-
-%% gen_server callbacks.
--export([init/1, handle_info/2, handle_call/3,
- handle_cast/2, terminate/2, code_change/3]).
+%% API
+-export([start_link/2]).
+-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4,
+ remove_user/2, import/2, search_fields/1, search_reported/1,
+ mod_opt_type/1, opt_type/1]).
--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, transform_module_options/1,
- mod_opt_type/1, opt_type/1, depends/2]).
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+ terminate/2, code_change/3]).
-include("ejabberd.hrl").
-include("logger.hrl").
-
-include("eldap.hrl").
-
--include("jlib.hrl").
+-include("xmpp.hrl").
-define(PROCNAME, ejabberd_mod_vcard_ldap).
@@ -74,59 +51,14 @@
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">>]}]).
-
--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">>}]).
-
--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">>}]).
-
-handle_cast(_Request, State) -> {noreply, State}.
-
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
-
-start(Host, Opts) ->
+%%%===================================================================
+%%% API
+%%%===================================================================
+start_link(Host, Opts) ->
+ Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
+ gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
+
+init(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
transient, 1000, worker, [?MODULE]},
@@ -138,141 +70,127 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
-depends(_Host, _Opts) ->
- [].
-
-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),
- case State#state.search of
- true ->
- ejabberd_router:unregister_route(State#state.myhost);
- _ -> ok
+get_vcard(LUser, LServer) ->
+ {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
+ VCardMap = State#state.vcard_map,
+ case find_ldap_user(LUser, State) of
+ #eldap_entry{attributes = Attributes} ->
+ ldap_attributes_to_vcard(Attributes, VCardMap,
+ {LUser, LServer});
+ _ ->
+ []
end.
-start_link(Host, Opts) ->
- Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- gen_server:start_link({local, Proc}, ?MODULE,
- [Host, Opts], []).
+set_vcard(_LUser, _LServer, _VCard, _VCardSearch) ->
+ {atomic, not_implemented}.
+
+search_fields(LServer) ->
+ {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
+ State#state.search_fields.
+
+search_reported(LServer) ->
+ {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
+ State#state.search_reported.
+
+search(LServer, Data, _AllowReturnAll, MaxMatch) ->
+ {ok, State} = eldap_utils:get_state(LServer, ?PROCNAME),
+ Base = State#state.base,
+ SearchFilter = State#state.search_filter,
+ Eldap_ID = State#state.eldap_id,
+ UIDs = State#state.uids,
+ ReportedAttrs = State#state.search_reported_attrs,
+ Filter = eldap:'and'([SearchFilter,
+ eldap_utils:make_filter(Data, UIDs)]),
+ case eldap_pool:search(Eldap_ID,
+ [{base, Base}, {filter, Filter}, {limit, MaxMatch},
+ {deref_aliases, State#state.deref_aliases},
+ {attributes, ReportedAttrs}])
+ of
+ #eldap_search_result{entries = E} ->
+ search_items(E, State);
+ _ ->
+ []
+ 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),
+ J = <<Username/binary, $@, LServer/binary>>,
+ [{<<"jid">>, J} | RFields];
+ _ ->
+ []
+ end;
+ _ ->
+ []
+ end;
+ <<"">> ->
+ []
+ end
+ end, Attributes).
+
+remove_user(_User, _Server) ->
+ {atomic, not_implemented}.
+
+import(_, _) ->
+ pass.
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
init([Host, Opts]) ->
State = parse_options(Host, Opts),
- 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),
- case State#state.search of
- true ->
- ejabberd_router:register_route(State#state.myhost, Host);
- _ -> 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)
- end,
- {noreply, State};
-handle_info(_Info, State) -> {noreply, State}.
-
-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.
+handle_call(get_state, _From, State) ->
+ {reply, {ok, State}, State};
+handle_call(_Request, _From, State) ->
+ Reply = ok,
+ {reply, Reply, State}.
-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.
+handle_cast(_Msg, State) ->
+ {noreply, State}.
-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
- end.
+handle_info(_Info, State) ->
+ {noreply, State}.
-process_vcard_ldap(To, IQ, Server) ->
- {ok, State} = eldap_utils:get_state(Server, ?PROCNAME),
- #iq{type = Type, sub_el = SubEl, lang = Lang} = 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 ->
- #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.
+terminate(_Reason, _State) ->
+ ok.
-handle_call(get_state, _From, State) ->
- {reply, {ok, State}, State};
-handle_call(stop, _From, State) ->
- {stop, normal, ok, State};
-handle_call(_Request, _From, State) ->
- {reply, bad_request, State}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
find_ldap_user(User, State) ->
Base = State#state.base,
RFC2254_Filter = State#state.user_filter,
@@ -403,309 +321,6 @@ ldap_attribute_to_vcard(vCardA, {<<"pcode">>, Value}) ->
children = [{xmlcdata, Value}]};
ldap_attribute_to_vcard(_, _) -> none.
--define(TLFIELD(Type, Label, Var),
- #xmlel{name = <<"field">>,
- attrs =
- [{<<"type">>, Type},
- {<<"label">>, translate:translate(Lang, Label)},
- {<<"var">>, Var}],
- children = []}).
-
--define(FORM(JID, 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,
- (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]).
-
-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 ->
- 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,
- 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 ->
- 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_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">>}]}].
-
--define(LFIELD(Label, 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 = [#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">>)]
- ++
- lists:map(fun ({Name, Value}) ->
- ?TLFIELD(<<"text-single">>, Name,
- Value)
- end,
- SearchReported)}],
- case search(State, Data) of
- error -> Header;
- Result -> Header ++ Result
- end.
-
--define(FIELD(Var, Val),
- #xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}],
- children =
- [#xmlel{name = <<"value">>, attrs = [],
- children = [{xmlcdata, Val}]}]}).
-
-search(State, Data) ->
- Base = State#state.base,
- SearchFilter = State#state.search_filter,
- Eldap_ID = State#state.eldap_id,
- UIDs = State#state.uids,
- Limit = State#state.matches,
- ReportedAttrs = State#state.search_reported_attrs,
- 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
- 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/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,
@@ -725,19 +340,54 @@ process_pattern(Str, {User, Domain}, AttrValues) ->
[{<<"%u">>, User}, {<<"%d">>, Domain}] ++
[{<<"%s">>, V, 1} || V <- AttrValues]).
-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).
+default_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">>]}].
+
+default_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">>}].
+
+default_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">>}].
parse_options(Host, Opts) ->
MyHost = gen_mod:get_opt_host(Host, Opts,
@@ -784,19 +434,19 @@ parse_options(Host, Opts) ->
[iolist_to_binary(E)
|| E <- L]}
end, Ls)
- end, ?VCARD_MAP),
+ end, default_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),
+ end, default_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),
+ end, default_search_reported()),
UIDAttrs = [UAttr || {UAttr, _} <- UIDs],
VCardMapAttrs = lists:usort(lists:append([A
|| {_, _, A} <- VCardMap])
@@ -834,27 +484,11 @@ parse_options(Host, Opts) ->
search_reported_attrs = SearchReportedAttrs,
matches = Matches}.
-transform_module_options(Opts) ->
- lists:map(
- fun({ldap_vcard_map, Map}) ->
- NewMap = lists:map(
- fun({Field, Pattern, Attrs}) ->
- {Field, [{Pattern, Attrs}]};
- (Opt) ->
- Opt
- end, Map),
- {ldap_vcard_map, NewMap};
- (Opt) ->
- Opt
- end, Opts).
-
check_filter(F) ->
NewF = iolist_to_binary(F),
{ok, _} = eldap_filter:parse(NewF),
NewF.
-mod_opt_type(host) -> fun iolist_to_binary/1;
-mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(ldap_filter) -> fun check_filter/1;
mod_opt_type(ldap_search_fields) ->
fun (Ls) ->
@@ -882,12 +516,6 @@ mod_opt_type(ldap_vcard_map) ->
end,
Ls)
end;
-mod_opt_type(matches) ->
- fun (infinity) -> 0;
- (I) when is_integer(I), I > 0 -> I
- end;
-mod_opt_type(search) ->
- fun (B) when is_boolean(B) -> B end;
mod_opt_type(deref_aliases) ->
fun (never) -> never;
(searching) -> searching;
@@ -926,9 +554,9 @@ mod_opt_type(ldap_tls_verify) ->
(false) -> false
end;
mod_opt_type(_) ->
- [host, iqdisc, ldap_filter, ldap_search_fields,
+ [ldap_filter, ldap_search_fields,
ldap_search_reported, ldap_uids, ldap_vcard_map,
- matches, search, deref_aliases, ldap_backups, ldap_base,
+ deref_aliases, ldap_backups, ldap_base,
ldap_deref_aliases, ldap_encrypt, ldap_password,
ldap_port, ldap_rootdn, ldap_servers,
ldap_tls_cacertfile, ldap_tls_certfile, ldap_tls_depth,
diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl
index 67abed09..3b64c29e 100644
--- a/src/mod_vcard_mnesia.erl
+++ b/src/mod_vcard_mnesia.erl
@@ -11,7 +11,8 @@
-behaviour(mod_vcard).
%% API
--export([init/2, import/2, get_vcard/2, set_vcard/4, search/4, remove_user/2]).
+-export([init/2, stop/1, import/2, get_vcard/2, set_vcard/4, search/4,
+ search_fields/1, search_reported/1, remove_user/2]).
-include("ejabberd.hrl").
-include("xmpp.hrl").
@@ -43,6 +44,9 @@ init(_Host, _Opts) ->
mnesia:add_table_index(vcard_search, lorgname),
mnesia:add_table_index(vcard_search, lorgunit).
+stop(_Host) ->
+ ok.
+
get_vcard(LUser, LServer) ->
US = {LUser, LServer},
F = fun () -> mnesia:read({vcard, US}) end,
@@ -71,15 +75,44 @@ search(LServer, Data, AllowReturnAll, MaxMatch) ->
{'EXIT', Reason} ->
?ERROR_MSG("~p", [Reason]), [];
Rs ->
+ Fields = lists:map(fun record_to_item/1, Rs),
case MaxMatch of
infinity ->
- Rs;
+ Fields;
Val ->
- lists:sublist(Rs, Val)
+ lists:sublist(Fields, Val)
end
end
end.
+search_fields(_LServer) ->
+ [{<<"User">>, <<"user">>},
+ {<<"Full Name">>, <<"fn">>},
+ {<<"Name">>, <<"first">>},
+ {<<"Middle Name">>, <<"middle">>},
+ {<<"Family Name">>, <<"last">>},
+ {<<"Nickname">>, <<"nick">>},
+ {<<"Birthday">>, <<"bday">>},
+ {<<"Country">>, <<"ctry">>},
+ {<<"City">>, <<"locality">>},
+ {<<"Email">>, <<"email">>},
+ {<<"Organization Name">>, <<"orgname">>},
+ {<<"Organization Unit">>, <<"orgunit">>}].
+
+search_reported(_LServer) ->
+ [{<<"Jabber ID">>, <<"jid">>},
+ {<<"Full Name">>, <<"fn">>},
+ {<<"Name">>, <<"first">>},
+ {<<"Middle Name">>, <<"middle">>},
+ {<<"Family Name">>, <<"last">>},
+ {<<"Nickname">>, <<"nick">>},
+ {<<"Birthday">>, <<"bday">>},
+ {<<"Country">>, <<"ctry">>},
+ {<<"City">>, <<"locality">>},
+ {<<"Email">>, <<"email">>},
+ {<<"Organization Name">>, <<"orgname">>},
+ {<<"Organization Unit">>, <<"orgunit">>}].
+
remove_user(LUser, LServer) ->
US = {LUser, LServer},
F = fun () ->
@@ -211,3 +244,19 @@ parts_to_string(Parts) ->
str:strip(list_to_binary(
lists:map(fun (S) -> <<S/binary, $.>> end, Parts)),
right, $.).
+
+-spec record_to_item(#vcard_search{}) -> [{binary(), binary()}].
+record_to_item(R) ->
+ {User, Server} = R#vcard_search.user,
+ [{<<"jid">>, <<User/binary, "@", Server/binary>>},
+ {<<"fn">>, (R#vcard_search.fn)},
+ {<<"last">>, (R#vcard_search.family)},
+ {<<"first">>, (R#vcard_search.given)},
+ {<<"middle">>, (R#vcard_search.middle)},
+ {<<"nick">>, (R#vcard_search.nickname)},
+ {<<"bday">>, (R#vcard_search.bday)},
+ {<<"ctry">>, (R#vcard_search.ctry)},
+ {<<"locality">>, (R#vcard_search.locality)},
+ {<<"email">>, (R#vcard_search.email)},
+ {<<"orgname">>, (R#vcard_search.orgname)},
+ {<<"orgunit">>, (R#vcard_search.orgunit)}].
diff --git a/src/mod_vcard_riak.erl b/src/mod_vcard_riak.erl
index 397008a7..23f05f17 100644
--- a/src/mod_vcard_riak.erl
+++ b/src/mod_vcard_riak.erl
@@ -12,7 +12,7 @@
%% API
-export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2,
- import/2]).
+ search_fields/1, search_reported/1, import/2, stop/1]).
-include("xmpp.hrl").
-include("mod_vcard.hrl").
@@ -23,6 +23,9 @@
init(_Host, _Opts) ->
ok.
+stop(_Host) ->
+ ok.
+
get_vcard(LUser, LServer) ->
case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of
{ok, R} ->
@@ -89,6 +92,12 @@ set_vcard(LUser, LServer, VCARD,
search(_LServer, _Data, _AllowReturnAll, _MaxMatch) ->
[].
+search_fields(_LServer) ->
+ [].
+
+search_reported(_LServer) ->
+ [].
+
remove_user(LUser, LServer) ->
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index f448d077..f8ac6be9 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -13,8 +13,8 @@
-behaviour(mod_vcard).
%% API
--export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2,
- import/1, import/2, export/1]).
+-export([init/2, stop/1, get_vcard/2, set_vcard/4, search/4, remove_user/2,
+ search_fields/1, search_reported/1, import/1, import/2, export/1]).
-include("xmpp.hrl").
-include("mod_vcard.hrl").
@@ -27,6 +27,9 @@
init(_Host, _Opts) ->
ok.
+stop(_Host) ->
+ ok.
+
get_vcard(LUser, LServer) ->
case catch sql_queries:get_vcard(LServer, LUser) of
{selected, [{SVCARD}]} ->
@@ -93,12 +96,40 @@ search(LServer, Data, AllowReturnAll, MaxMatch) ->
<<"middle">>, <<"nickname">>, <<"bday">>, <<"ctry">>,
<<"locality">>, <<"email">>, <<"orgname">>,
<<"orgunit">>], Rs} when is_list(Rs) ->
- Rs;
+ [row_to_item(LServer, R) || R <- Rs];
Error ->
?ERROR_MSG("~p", [Error]), []
end
end.
+search_fields(_LServer) ->
+ [{<<"User">>, <<"user">>},
+ {<<"Full Name">>, <<"fn">>},
+ {<<"Name">>, <<"first">>},
+ {<<"Middle Name">>, <<"middle">>},
+ {<<"Family Name">>, <<"last">>},
+ {<<"Nickname">>, <<"nick">>},
+ {<<"Birthday">>, <<"bday">>},
+ {<<"Country">>, <<"ctry">>},
+ {<<"City">>, <<"locality">>},
+ {<<"Email">>, <<"email">>},
+ {<<"Organization Name">>, <<"orgname">>},
+ {<<"Organization Unit">>, <<"orgunit">>}].
+
+search_reported(_LServer) ->
+ [{<<"Jabber ID">>, <<"jid">>},
+ {<<"Full Name">>, <<"fn">>},
+ {<<"Name">>, <<"first">>},
+ {<<"Middle Name">>, <<"middle">>},
+ {<<"Family Name">>, <<"last">>},
+ {<<"Nickname">>, <<"nick">>},
+ {<<"Birthday">>, <<"bday">>},
+ {<<"Country">>, <<"ctry">>},
+ {<<"City">>, <<"locality">>},
+ {<<"Email">>, <<"email">>},
+ {<<"Organization Name">>, <<"orgname">>},
+ {<<"Organization Unit">>, <<"orgunit">>}].
+
remove_user(LUser, LServer) ->
ejabberd_sql:sql_transaction(
LServer,
@@ -240,3 +271,18 @@ make_val(Match, Field, Val) ->
<<"">> -> Condition;
_ -> [Match, <<" and ">>, Condition]
end.
+
+row_to_item(LServer, [Username, FN, Family, Given, Middle, Nickname, BDay,
+ CTRY, Locality, EMail, OrgName, OrgUnit]) ->
+ [{<<"jid">>, <<Username/binary, $@, LServer/binary>>},
+ {<<"fn">>, FN},
+ {<<"last">>, Family},
+ {<<"first">>, Given},
+ {<<"middle">>, Middle},
+ {<<"nick">>, Nickname},
+ {<<"bday">>, BDay},
+ {<<"ctry">>, CTRY},
+ {<<"locality">>, Locality},
+ {<<"email">>, EMail},
+ {<<"orgname">>, OrgName},
+ {<<"orgunit">>, OrgUnit}].