./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 15 Schema = ["ns", "xml"],
61 15 NSs = mod_private_backend:get_all_nss(HostType, LUser, LServer),
62 15 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 15 [{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 8 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 152 #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 format_items = map,
102 process = fun ?MODULE:remove_unused_backend_opts/1
103 }.
104
105
:-(
remove_unused_backend_opts(Opts = #{backend := riak}) -> Opts;
106
:-(
remove_unused_backend_opts(Opts) -> maps:remove(riak, Opts).
107
108 riak_config_spec() ->
109 152 #section{
110 items = #{<<"bucket_type">> => #option{type = binary,
111 validate = non_empty}
112 },
113 defaults = #{<<"bucket_type">> => <<"private">>},
114 include = always,
115 format_items = map
116 }.
117
118 %% ------------------------------------------------------------------
119 %% Handlers
120
121 remove_user(Acc, User, Server) ->
122 18 HostType = mongoose_acc:host_type(Acc),
123 18 LUser = jid:nodeprep(User),
124 18 LServer = jid:nameprep(Server),
125 18 R = mod_private_backend:remove_user(HostType, LUser, LServer),
126 18 mongoose_lib:log_if_backend_error(R, ?MODULE, ?LINE, {Acc, User, Server}),
127 18 Acc.
128
129 -spec remove_domain(mongoose_hooks:simple_acc(),
130 mongooseim:host_type(), jid:lserver()) ->
131 mongoose_hooks:simple_acc().
132 remove_domain(Acc, HostType, Domain) ->
133 1 mod_private_backend:remove_domain(HostType, Domain),
134 1 Acc.
135
136 process_iq(Acc,
137 From = #jid{lserver = LServer, luser = LUser},
138 To = #jid{lserver = LServer, luser = LUser},
139 IQ = #iq{type = Type, sub_el = SubElem = #xmlel{children = Elems}},
140 _Extra) ->
141 23 HostType = mongoose_acc:host_type(Acc),
142 23 IsEqual = compare_bare_jids(From, To),
143 23 Strategy = choose_strategy(IsEqual, Type),
144 23 Res = case Strategy of
145 get ->
146 5 NS2XML = to_map(Elems),
147 5 XMLs = mod_private_backend:multi_get_data(HostType, LUser, LServer, NS2XML),
148 5 IQ#iq{type = result, sub_el = [SubElem#xmlel{children = XMLs}]};
149 set ->
150 18 NS2XML = to_map(Elems),
151 18 Result = mod_private_backend:multi_set_data(HostType, LUser, LServer, NS2XML),
152 18 case Result of
153 ok ->
154 18 IQ#iq{type = result, sub_el = [SubElem]};
155 {error, Reason} ->
156
:-(
?LOG_ERROR(#{what => multi_set_data_failed, reason => Reason,
157
:-(
user => LUser, server => LServer}),
158
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error());
159 {aborted, Reason} ->
160
:-(
?LOG_ERROR(#{what => multi_set_data_aborted, reason => Reason,
161
:-(
user => LUser, server => LServer}),
162
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error())
163 end;
164 forbidden ->
165
:-(
error_iq(IQ, mongoose_xmpp_errors:forbidden())
166 end,
167 23 {Acc, Res};
168 process_iq(Acc, _From, _To, IQ, _Extra) ->
169 2 Txt = <<"Only requests from/to your JID are allowed">>,
170 2 Err = mongoose_xmpp_errors:forbidden(<<"en">>, Txt),
171 2 Res = error_iq(IQ, Err),
172 2 {Acc, Res}.
173
174 %% ------------------------------------------------------------------
175 %% Helpers
176
177 5 choose_strategy(true, get) -> get;
178 18 choose_strategy(true, set) -> set;
179
:-(
choose_strategy(_, _ ) -> forbidden.
180
181 compare_bare_jids(#jid{luser = LUser, lserver = LServer},
182 23 #jid{luser = LUser, lserver = LServer}) -> true;
183
:-(
compare_bare_jids(_, _) -> false.
184
185 element_to_namespace(#xmlel{attrs = Attrs}) ->
186 23 xml:get_attr_s(<<"xmlns">>, Attrs);
187 element_to_namespace(_) ->
188
:-(
<<>>.
189
190 %% Skip invalid elements.
191 to_map(Elems) ->
192 23 [{NS, Elem} || Elem <- Elems, is_valid_namespace(NS = element_to_namespace(Elem))].
193
194 23 is_valid_namespace(Namespace) -> Namespace =/= <<>>.
195
196 error_iq(IQ=#iq{sub_el=SubElem}, ErrorStanza) ->
197 2 IQ#iq{type = error, sub_el = [SubElem, ErrorStanza]}.
198
199 -spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}].
200 config_metrics(HostType) ->
201 24 mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]).
Line Hits Source