1 |
|
%%============================================================================== |
2 |
|
%% Copyright 2015 Erlang Solutions Ltd. |
3 |
|
%% |
4 |
|
%% Licensed under the Apache License, Version 2.0 (the "License"); |
5 |
|
%% you may not use this file except in compliance with the License. |
6 |
|
%% You may obtain a copy of the License at |
7 |
|
%% |
8 |
|
%% http://www.apache.org/licenses/LICENSE-2.0 |
9 |
|
%% |
10 |
|
%% Unless required by applicable law or agreed to in writing, software |
11 |
|
%% distributed under the License is distributed on an "AS IS" BASIS, |
12 |
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
|
%% See the License for the specific language governing permissions and |
14 |
|
%% limitations under the License. |
15 |
|
%%============================================================================== |
16 |
|
-module(mod_vcard_riak). |
17 |
|
-behaviour(mod_vcard_backend). |
18 |
|
|
19 |
|
%% API |
20 |
|
-export([init/2, |
21 |
|
remove_user/3, |
22 |
|
set_vcard/5, |
23 |
|
get_vcard/3, |
24 |
|
search/3, |
25 |
|
search_fields/2, |
26 |
|
search_reported_fields/3]). |
27 |
|
|
28 |
|
-include("mongoose.hrl"). |
29 |
|
-include("jlib.hrl"). |
30 |
|
-include("mod_vcard.hrl"). |
31 |
|
-include_lib("riakc/include/riakc.hrl"). |
32 |
|
|
33 |
|
init(_HostType, _Opts) -> |
34 |
:-( |
ok. |
35 |
|
|
36 |
|
remove_user(HostType, LUser, LServer) -> |
37 |
:-( |
mongoose_riak:delete(bucket_type(HostType, LServer), LUser, [{dw, 2}]). |
38 |
|
|
39 |
|
set_vcard(HostType, User, LServer, VCard, _VCardSearch) -> |
40 |
:-( |
BucketType = bucket_type(HostType, LServer), |
41 |
:-( |
VCardEncoded = exml:to_binary(VCard), |
42 |
:-( |
LUser = jid:nodeprep(User), |
43 |
:-( |
Obj = riakc_obj:new(BucketType, LUser, VCardEncoded, "application/xml"), |
44 |
:-( |
mongoose_riak:put(Obj). |
45 |
|
|
46 |
|
get_vcard(HostType, LUser, LServer) -> |
47 |
:-( |
BucketType = bucket_type(HostType, LServer), |
48 |
:-( |
case mongoose_riak:get(BucketType, LUser) of |
49 |
|
{ok, Obj} -> |
50 |
:-( |
XMLBin = riakc_obj:get_value(Obj), |
51 |
:-( |
case exml:parse(XMLBin) of |
52 |
|
{ok, XMLEl} -> |
53 |
:-( |
{ok, [XMLEl]}; |
54 |
|
{error, Reason} -> |
55 |
:-( |
?LOG_WARNING(#{what => vcard_lookup_failed, reason => Reason, |
56 |
:-( |
packet => XMLBin}), |
57 |
:-( |
{error, mongoose_xmpp_errors:service_unavailable()} |
58 |
|
end; |
59 |
|
{error, notfound} -> |
60 |
:-( |
{error, mongoose_xmpp_errors:item_not_found()}; |
61 |
|
Other -> |
62 |
:-( |
Other |
63 |
|
end. |
64 |
|
|
65 |
|
search(HostType, LServer, Data) -> |
66 |
:-( |
YZQuery = make_yz_query(Data, []), |
67 |
:-( |
do_search(YZQuery, HostType, LServer). |
68 |
|
|
69 |
|
do_search([], _HostType, _) -> |
70 |
:-( |
[]; |
71 |
|
do_search(YZQueryIn, HostType, LServer) -> |
72 |
:-( |
{_BucketType, BucketName} = bucket_type(HostType, LServer), |
73 |
:-( |
YZQuery = [<<"_yz_rb:", BucketName/binary>> | YZQueryIn], |
74 |
:-( |
Limit = mod_vcard:get_results_limit(HostType), |
75 |
:-( |
YZQueryBin = mongoose_bin:join(YZQuery, <<" AND ">>), |
76 |
:-( |
case mongoose_riak:search(yz_vcard_index(HostType), YZQueryBin, [{rows, Limit}]) of |
77 |
|
{ok, #search_results{docs=R, num_found = _N}} -> |
78 |
:-( |
lists:map(fun({_Index, Props}) -> doc2item(HostType, LServer, Props) end, R); |
79 |
|
Err -> |
80 |
:-( |
?LOG_ERROR(#{what => vcard_search_failed, index => yz_vcard_index(HostType), |
81 |
:-( |
riak_query => YZQueryBin, reason => Err}), |
82 |
:-( |
[] |
83 |
|
end. |
84 |
|
|
85 |
|
search_fields(_HostType, _LServer) -> |
86 |
:-( |
mod_vcard:default_search_fields(). |
87 |
|
|
88 |
|
search_reported_fields(_HostType, _LServer, Lang) -> |
89 |
:-( |
mod_vcard:get_default_reported_fields(Lang). |
90 |
|
|
91 |
:-( |
make_yz_query([], Acc) -> Acc; |
92 |
|
make_yz_query([{Var, [Val]} | Rest], Acc) -> |
93 |
:-( |
Part = [riak_search_mapping(Var), ":", make_val(Val)], |
94 |
:-( |
make_yz_query(Rest, [erlang:iolist_to_binary(Part) | Acc]). |
95 |
|
|
96 |
:-( |
riak_search_mapping(<<"user">>) -> <<"_yz_rk">>; |
97 |
:-( |
riak_search_mapping(<<"fn">>) -> <<"vCard.FN">>; |
98 |
:-( |
riak_search_mapping(<<"first">>) -> <<"vCard.N.GIVEN">>; |
99 |
:-( |
riak_search_mapping(<<"middle">>) -> <<"vCard.N.MIDDLE">>; |
100 |
:-( |
riak_search_mapping(<<"last">>) -> <<"vCard.N.FAMILY">>; |
101 |
:-( |
riak_search_mapping(<<"nick">>) -> <<"vCard.NICKNAME">>; |
102 |
:-( |
riak_search_mapping(<<"bday">>) -> <<"vCard.BDAY">>; |
103 |
:-( |
riak_search_mapping(<<"ctry">>) -> <<"vCard.ADR.CTRY">>; |
104 |
:-( |
riak_search_mapping(<<"locality">>) -> <<"vCard.ADR.LOCALITY">>; |
105 |
:-( |
riak_search_mapping(<<"email">>) -> <<"vCard.EMAIL.USERID">>; |
106 |
:-( |
riak_search_mapping(<<"orgname">>) -> <<"vCard.ORG.ORGNAME">>; |
107 |
:-( |
riak_search_mapping(<<"orgunit">>) -> <<"vCard.ORG.ORGUNIT">>. |
108 |
|
|
109 |
|
make_val(Val) -> |
110 |
:-( |
LVal = jid:str_tolower(Val), |
111 |
:-( |
case binary:match(LVal, <<" ">>) of |
112 |
|
nomatch -> |
113 |
:-( |
LVal; |
114 |
|
_ -> |
115 |
:-( |
[$", LVal, $"] |
116 |
|
end. |
117 |
|
|
118 |
|
doc2item(HostType, LServer, Props) -> |
119 |
:-( |
Vals = lists:map(pa:bind(fun extract_field/2, Props), search_fields(HostType, LServer)), |
120 |
:-( |
#xmlel{name = <<"item">>, |
121 |
|
children = Vals}. |
122 |
|
|
123 |
|
extract_field(Props, {_, <<"user">>}) -> |
124 |
:-( |
{_, Username} = lists:keyfind(riak_search_mapping(<<"user">>), 1, Props), |
125 |
:-( |
{_, Bucket} = lists:keyfind(<<"_yz_rb">>, 1, Props), |
126 |
:-( |
[_, Host] = binary:split(Bucket, <<"_">>), |
127 |
:-( |
?FIELD(<<"jid">>, iolist_to_binary([Username, "@", Host])); |
128 |
|
extract_field(Props, {_, Field}) -> |
129 |
:-( |
V = case lists:keyfind(riak_search_mapping(Field), 1, Props) of |
130 |
|
{_, Val} -> |
131 |
:-( |
Val; |
132 |
|
_ -> |
133 |
:-( |
"" |
134 |
|
end, |
135 |
:-( |
?FIELD(Field, V). |
136 |
|
|
137 |
|
|
138 |
|
bucket_type(HostType, LServer) -> |
139 |
:-( |
{gen_mod:get_module_opt(HostType, mod_vcard, bucket_type, <<"vcard">>), <<"vcard_", LServer/binary>>}. |
140 |
|
|
141 |
|
yz_vcard_index(HostType) -> |
142 |
:-( |
gen_mod:get_module_opt(HostType, mod_vcard, search_index, <<"vcard">>). |