mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-19 15:32:08 +02:00
214 lines
6.8 KiB
Erlang
214 lines
6.8 KiB
Erlang
|
%%%-------------------------------------------------------------------
|
||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||
|
%%% @doc
|
||
|
%%%
|
||
|
%%% @end
|
||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||
|
%%%-------------------------------------------------------------------
|
||
|
-module(mod_vcard_mnesia).
|
||
|
|
||
|
-behaviour(mod_vcard).
|
||
|
|
||
|
%% API
|
||
|
-export([init/2, import/2, get_vcard/2, set_vcard/4, search/4, remove_user/2]).
|
||
|
|
||
|
-include("ejabberd.hrl").
|
||
|
-include("jlib.hrl").
|
||
|
-include("mod_vcard.hrl").
|
||
|
-include("logger.hrl").
|
||
|
|
||
|
%%%===================================================================
|
||
|
%%% API
|
||
|
%%%===================================================================
|
||
|
init(_Host, _Opts) ->
|
||
|
mnesia:create_table(vcard,
|
||
|
[{disc_only_copies, [node()]},
|
||
|
{attributes, record_info(fields, vcard)}]),
|
||
|
mnesia:create_table(vcard_search,
|
||
|
[{disc_copies, [node()]},
|
||
|
{attributes,
|
||
|
record_info(fields, vcard_search)}]),
|
||
|
update_tables(),
|
||
|
mnesia:add_table_index(vcard_search, luser),
|
||
|
mnesia:add_table_index(vcard_search, lfn),
|
||
|
mnesia:add_table_index(vcard_search, lfamily),
|
||
|
mnesia:add_table_index(vcard_search, lgiven),
|
||
|
mnesia:add_table_index(vcard_search, lmiddle),
|
||
|
mnesia:add_table_index(vcard_search, lnickname),
|
||
|
mnesia:add_table_index(vcard_search, lbday),
|
||
|
mnesia:add_table_index(vcard_search, lctry),
|
||
|
mnesia:add_table_index(vcard_search, llocality),
|
||
|
mnesia:add_table_index(vcard_search, lemail),
|
||
|
mnesia:add_table_index(vcard_search, lorgname),
|
||
|
mnesia:add_table_index(vcard_search, lorgunit).
|
||
|
|
||
|
get_vcard(LUser, LServer) ->
|
||
|
US = {LUser, LServer},
|
||
|
F = fun () -> mnesia:read({vcard, US}) end,
|
||
|
case mnesia:transaction(F) of
|
||
|
{atomic, Rs} ->
|
||
|
lists:map(fun (R) -> R#vcard.vcard end, Rs);
|
||
|
{aborted, _Reason} -> error
|
||
|
end.
|
||
|
|
||
|
set_vcard(LUser, LServer, VCARD, VCardSearch) ->
|
||
|
US = {LUser, LServer},
|
||
|
F = fun () ->
|
||
|
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||
|
mnesia:write(VCardSearch)
|
||
|
end,
|
||
|
mnesia:transaction(F).
|
||
|
|
||
|
search(LServer, Data, AllowReturnAll, MaxMatch) ->
|
||
|
MatchSpec = make_matchspec(LServer, Data),
|
||
|
if (MatchSpec == #vcard_search{_ = '_'}) and
|
||
|
not AllowReturnAll ->
|
||
|
[];
|
||
|
true ->
|
||
|
case catch mnesia:dirty_select(vcard_search,
|
||
|
[{MatchSpec, [], ['$_']}]) of
|
||
|
{'EXIT', Reason} ->
|
||
|
?ERROR_MSG("~p", [Reason]), [];
|
||
|
Rs ->
|
||
|
case MaxMatch of
|
||
|
infinity ->
|
||
|
Rs;
|
||
|
Val ->
|
||
|
lists:sublist(Rs, Val)
|
||
|
end
|
||
|
end
|
||
|
end.
|
||
|
|
||
|
remove_user(LUser, LServer) ->
|
||
|
US = {LUser, LServer},
|
||
|
F = fun () ->
|
||
|
mnesia:delete({vcard, US}),
|
||
|
mnesia:delete({vcard_search, US})
|
||
|
end,
|
||
|
mnesia:transaction(F).
|
||
|
|
||
|
import(_LServer, #vcard{} = VCard) ->
|
||
|
mnesia:dirty_write(VCard);
|
||
|
import(_LServer, #vcard_search{} = S) ->
|
||
|
mnesia:dirty_write(S).
|
||
|
|
||
|
%%%===================================================================
|
||
|
%%% Internal functions
|
||
|
%%%===================================================================
|
||
|
update_tables() ->
|
||
|
update_vcard_table(),
|
||
|
update_vcard_search_table().
|
||
|
|
||
|
update_vcard_table() ->
|
||
|
Fields = record_info(fields, vcard),
|
||
|
case mnesia:table_info(vcard, attributes) of
|
||
|
Fields ->
|
||
|
ejabberd_config:convert_table_to_binary(
|
||
|
vcard, Fields, set,
|
||
|
fun(#vcard{us = {U, _}}) -> U end,
|
||
|
fun(#vcard{us = {U, S}, vcard = El} = R) ->
|
||
|
R#vcard{us = {iolist_to_binary(U),
|
||
|
iolist_to_binary(S)},
|
||
|
vcard = fxml:to_xmlel(El)}
|
||
|
end);
|
||
|
_ ->
|
||
|
?INFO_MSG("Recreating vcard table", []),
|
||
|
mnesia:transform_table(vcard, ignore, Fields)
|
||
|
end.
|
||
|
|
||
|
update_vcard_search_table() ->
|
||
|
Fields = record_info(fields, vcard_search),
|
||
|
case mnesia:table_info(vcard_search, attributes) of
|
||
|
Fields ->
|
||
|
ejabberd_config:convert_table_to_binary(
|
||
|
vcard_search, Fields, set,
|
||
|
fun(#vcard_search{us = {U, _}}) -> U end,
|
||
|
fun(#vcard_search{} = VS) ->
|
||
|
[vcard_search | L] = tuple_to_list(VS),
|
||
|
NewL = lists:map(
|
||
|
fun({U, S}) ->
|
||
|
{iolist_to_binary(U),
|
||
|
iolist_to_binary(S)};
|
||
|
(Str) ->
|
||
|
iolist_to_binary(Str)
|
||
|
end, L),
|
||
|
list_to_tuple([vcard_search | NewL])
|
||
|
end);
|
||
|
_ ->
|
||
|
?INFO_MSG("Recreating vcard_search table", []),
|
||
|
mnesia:transform_table(vcard_search, ignore, Fields)
|
||
|
end.
|
||
|
|
||
|
make_matchspec(LServer, Data) ->
|
||
|
GlobMatch = #vcard_search{_ = '_'},
|
||
|
Match = filter_fields(Data, GlobMatch, LServer),
|
||
|
Match.
|
||
|
|
||
|
filter_fields([], Match, _LServer) ->
|
||
|
Match;
|
||
|
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
||
|
when is_binary(Val) and (Val /= <<"">>) ->
|
||
|
LVal = mod_vcard:string2lower(Val),
|
||
|
NewMatch = case SVar of
|
||
|
<<"user">> ->
|
||
|
case gen_mod:get_module_opt(LServer, ?MODULE,
|
||
|
search_all_hosts,
|
||
|
fun(B) when is_boolean(B) ->
|
||
|
B
|
||
|
end, true) of
|
||
|
true -> Match#vcard_search{luser = make_val(LVal)};
|
||
|
false ->
|
||
|
Host = find_my_host(LServer),
|
||
|
Match#vcard_search{us = {make_val(LVal), Host}}
|
||
|
end;
|
||
|
<<"fn">> -> Match#vcard_search{lfn = make_val(LVal)};
|
||
|
<<"last">> ->
|
||
|
Match#vcard_search{lfamily = make_val(LVal)};
|
||
|
<<"first">> ->
|
||
|
Match#vcard_search{lgiven = make_val(LVal)};
|
||
|
<<"middle">> ->
|
||
|
Match#vcard_search{lmiddle = make_val(LVal)};
|
||
|
<<"nick">> ->
|
||
|
Match#vcard_search{lnickname = make_val(LVal)};
|
||
|
<<"bday">> ->
|
||
|
Match#vcard_search{lbday = make_val(LVal)};
|
||
|
<<"ctry">> ->
|
||
|
Match#vcard_search{lctry = make_val(LVal)};
|
||
|
<<"locality">> ->
|
||
|
Match#vcard_search{llocality = make_val(LVal)};
|
||
|
<<"email">> ->
|
||
|
Match#vcard_search{lemail = make_val(LVal)};
|
||
|
<<"orgname">> ->
|
||
|
Match#vcard_search{lorgname = make_val(LVal)};
|
||
|
<<"orgunit">> ->
|
||
|
Match#vcard_search{lorgunit = make_val(LVal)};
|
||
|
_ -> Match
|
||
|
end,
|
||
|
filter_fields(Ds, NewMatch, LServer);
|
||
|
filter_fields([_ | Ds], Match, LServer) ->
|
||
|
filter_fields(Ds, Match, LServer).
|
||
|
|
||
|
make_val(Val) ->
|
||
|
case str:suffix(<<"*">>, Val) of
|
||
|
true -> [str:substr(Val, 1, byte_size(Val) - 1)] ++ '_';
|
||
|
_ -> Val
|
||
|
end.
|
||
|
|
||
|
find_my_host(LServer) ->
|
||
|
Parts = str:tokens(LServer, <<".">>),
|
||
|
find_my_host(Parts, ?MYHOSTS).
|
||
|
|
||
|
find_my_host([], _Hosts) -> ?MYNAME;
|
||
|
find_my_host([_ | Tail] = Parts, Hosts) ->
|
||
|
Domain = parts_to_string(Parts),
|
||
|
case lists:member(Domain, Hosts) of
|
||
|
true -> Domain;
|
||
|
false -> find_my_host(Tail, Hosts)
|
||
|
end.
|
||
|
|
||
|
parts_to_string(Parts) ->
|
||
|
str:strip(list_to_binary(
|
||
|
lists:map(fun (S) -> <<S/binary, $.>> end, Parts)),
|
||
|
right, $.).
|