./ct_report/coverage/mod_private.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_private.erl
3 %%% Author : Alexey Shchepin <alexey@process-one.net>
4 %%% Purpose : Support for private storage.
5 %%% Created : 16 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(mod_private).
27 -author('alexey@process-one.net').
28
29 -behaviour(gen_mod).
30 -behaviour(mongoose_module_metrics).
31
32 -export([start/2,
33 stop/1,
34 supported_features/0,
35 config_spec/0,
36 process_iq/5,
37 remove_user/3,
38 remove_domain/3,
39 remove_unused_backend_opts/1]).
40
41 -export([get_personal_data/3]).
42
43 -export([config_metrics/1]).
44
45 -ignore_xref([
46 behaviour_info/1, get_personal_data/3, remove_user/3, remove_domain/3
47 ]).
48
49 -include("mongoose.hrl").
50 -include("jlib.hrl").
51 -include("mongoose_config_spec.hrl").
52 -xep([{xep, 49}, {version, "1.2"}]).
53
54 %%--------------------------------------------------------------------
55 %% gdpr callback
56 %%--------------------------------------------------------------------
57
58 -spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data().
59 get_personal_data(Acc, HostType, #jid{ luser = LUser, lserver = LServer }) ->
60 16 Schema = ["ns", "xml"],
61 16 NSs = mod_private_backend:get_all_nss(HostType, LUser, LServer),
62 16 Entries = lists:map(
63 fun(NS) ->
64 12 Data = mod_private_backend:multi_get_data(
65 HostType, LUser, LServer, [{NS, default}]),
66 12 { NS, exml:to_binary(Data) }
67 end, NSs),
68 16 [{private, Schema, Entries} | Acc].
69
70 %% ------------------------------------------------------------------
71 %% gen_mod callbacks
72
73 -spec start(HostType :: mongooseim:host_type(), Opts :: gen_mod:module_opts()) -> ok | {error, atom()}.
74 start(HostType, #{iqdisc := IQDisc} = Opts) ->
75 6 mod_private_backend:init(HostType, Opts),
76 6 ejabberd_hooks:add(hooks(HostType)),
77 6 gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_PRIVATE, ejabberd_sm,
78 fun ?MODULE:process_iq/5, #{}, IQDisc).
79
80 -spec stop(HostType :: mongooseim:host_type()) -> ok | {error, not_registered}.
81 stop(HostType) ->
82 6 ejabberd_hooks:delete(hooks(HostType)),
83 6 gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_PRIVATE, ejabberd_sm).
84
85
:-(
supported_features() -> [dynamic_domains].
86
87 hooks(HostType) ->
88 12 [{remove_user, HostType, ?MODULE, remove_user, 50},
89 {remove_domain, HostType, ?MODULE, remove_domain, 50},
90 {anonymous_purge_hook, HostType, ?MODULE, remove_user, 50},
91 {get_personal_data, HostType, ?MODULE, get_personal_data, 50}].
92
93 config_spec() ->
94 166 #section{
95 items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(),
96 <<"backend">> => #option{type = atom,
97 validate = {module, mod_private}},
98 <<"riak">> => riak_config_spec()},
99 defaults = #{<<"iqdisc">> => one_queue,
100 <<"backend">> => rdbms},
101 process = fun ?MODULE:remove_unused_backend_opts/1
102 }.
103
104
:-(
remove_unused_backend_opts(Opts = #{backend := riak}) -> Opts;
105
:-(
remove_unused_backend_opts(Opts) -> maps:remove(riak, Opts).
106
107 riak_config_spec() ->
108 166 #section{
109 items = #{<<"bucket_type">> => #option{type = binary,
110 validate = non_empty}
111 },
112 defaults = #{<<"bucket_type">> => <<"private">>},
113 include = always
114 }.
115
116 %% ------------------------------------------------------------------
117 %% Handlers
118
119 remove_user(Acc, User, Server) ->
120 36 HostType = mongoose_acc:host_type(Acc),
121 36 LUser = jid:nodeprep(User),
122 36 LServer = jid:nameprep(Server),
123 36 R = mod_private_backend:remove_user(HostType, LUser, LServer),
124 36 mongoose_lib:log_if_backend_error(R, ?MODULE, ?LINE, {Acc, User, Server}),
125 36 Acc.
126
127 -spec remove_domain(mongoose_hooks:simple_acc(),
128 mongooseim:host_type(), jid:lserver()) ->
129 mongoose_hooks:simple_acc().
130 remove_domain(Acc, HostType, Domain) ->
131
:-(
mod_private_backend:remove_domain(HostType, Domain),
132
:-(
Acc.
133
134 process_iq(Acc,
135 From = #jid{lserver = LServer, luser = LUser},
136 To = #jid{lserver = LServer, luser = LUser},
137 IQ = #iq{type = Type, sub_el = SubElem = #xmlel{children = Elems}},
138 _Extra) ->
139 28 HostType = mongoose_acc:host_type(Acc),
140 28 IsEqual = compare_bare_jids(From, To),
141 28 Strategy = choose_strategy(IsEqual, Type),
142 28 Res = case Strategy of
143 get ->
144 7 NS2XML = to_map(Elems),
145 7 XMLs = mod_private_backend:multi_get_data(HostType, LUser, LServer, NS2XML),
146 7 IQ#iq{type = result, sub_el = [SubElem#xmlel{children = XMLs}]};
147 set ->
148 21 NS2XML = to_map(Elems),
149 21 Result = mod_private_backend:multi_set_data(HostType, LUser, LServer, NS2XML),
150 21 case Result of
151 ok ->
152 21 IQ#iq{type = result, sub_el = [SubElem]};
153 {error, Reason} ->
154
:-(
?LOG_ERROR(#{what => multi_set_data_failed, reason => Reason,
155
:-(
user => LUser, server => LServer}),
156
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error());
157 {aborted, Reason} ->
158
:-(
?LOG_ERROR(#{what => multi_set_data_aborted, reason => Reason,
159
:-(
user => LUser, server => LServer}),
160
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error())
161 end;
162 forbidden ->
163
:-(
error_iq(IQ, mongoose_xmpp_errors:forbidden())
164 end,
165 28 {Acc, Res};
166 process_iq(Acc, _From, _To, IQ, _Extra) ->
167 2 Txt = <<"Only requests from/to your JID are allowed">>,
168 2 Err = mongoose_xmpp_errors:forbidden(<<"en">>, Txt),
169 2 Res = error_iq(IQ, Err),
170 2 {Acc, Res}.
171
172 %% ------------------------------------------------------------------
173 %% Helpers
174
175 7 choose_strategy(true, get) -> get;
176 21 choose_strategy(true, set) -> set;
177
:-(
choose_strategy(_, _ ) -> forbidden.
178
179 compare_bare_jids(#jid{luser = LUser, lserver = LServer},
180 28 #jid{luser = LUser, lserver = LServer}) -> true;
181
:-(
compare_bare_jids(_, _) -> false.
182
183 element_to_namespace(#xmlel{attrs = Attrs}) ->
184 28 xml:get_attr_s(<<"xmlns">>, Attrs);
185 element_to_namespace(_) ->
186
:-(
<<>>.
187
188 %% Skip invalid elements.
189 to_map(Elems) ->
190 28 [{NS, Elem} || Elem <- Elems, is_valid_namespace(NS = element_to_namespace(Elem))].
191
192 28 is_valid_namespace(Namespace) -> Namespace =/= <<>>.
193
194 error_iq(IQ=#iq{sub_el=SubElem}, ErrorStanza) ->
195 2 IQ#iq{type = error, sub_el = [SubElem, ErrorStanza]}.
196
197 -spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}].
198 config_metrics(HostType) ->
199 12 mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]).
Line Hits Source