./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 hooks/1,
35 supported_features/0,
36 config_spec/0,
37 process_iq/5,
38 remove_user/3,
39 remove_domain/3]).
40
41 -export([get_personal_data/3]).
42
43 -export([config_metrics/1]).
44
45 -include("mongoose.hrl").
46 -include("jlib.hrl").
47 -include("mongoose_config_spec.hrl").
48 -xep([{xep, 49}, {version, "1.2"}]).
49
50 %%--------------------------------------------------------------------
51 %% gdpr callback
52 %%--------------------------------------------------------------------
53
54 -spec get_personal_data(Acc, Params, Extra) -> {ok, Acc} when
55 Acc :: gdpr:personal_data(),
56 Params :: #{jid := jid:jid()},
57 Extra :: gen_hook:extra().
58 get_personal_data(Acc, #{jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
59 15 Schema = ["ns", "xml"],
60 15 NSs = mod_private_backend:get_all_nss(HostType, LUser, LServer),
61 15 Entries = lists:map(
62 fun(NS) ->
63 12 Data = mod_private_backend:multi_get_data(
64 HostType, LUser, LServer, [{NS, default}]),
65 12 {NS, exml:to_binary(Data)}
66 end, NSs),
67 15 NewAcc = [{private, Schema, Entries} | Acc],
68 15 {ok, NewAcc}.
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 8 mod_private_backend:init(HostType, Opts),
76 8 gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_PRIVATE, ejabberd_sm,
77 fun ?MODULE:process_iq/5, #{}, IQDisc).
78
79 -spec stop(HostType :: mongooseim:host_type()) -> ok | {error, not_registered}.
80 stop(HostType) ->
81 8 gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_PRIVATE, ejabberd_sm).
82
83 10 supported_features() -> [dynamic_domains].
84
85 -spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
86 hooks(HostType) ->
87 16 [{remove_user, HostType, fun ?MODULE:remove_user/3, #{}, 50},
88 {remove_domain, HostType, fun ?MODULE:remove_domain/3, #{}, 50},
89 {anonymous_purge_hook, HostType, fun ?MODULE:remove_user/3, #{}, 50},
90 {get_personal_data, HostType, fun ?MODULE:get_personal_data/3, #{}, 50}].
91
92 config_spec() ->
93 186 #section{
94 items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc(),
95 <<"backend">> => #option{type = atom,
96 validate = {module, mod_private}}},
97 defaults = #{<<"iqdisc">> => one_queue,
98 <<"backend">> => rdbms}
99 }.
100
101 %% ------------------------------------------------------------------
102 %% Handlers
103
104 -spec remove_user(Acc, Params, Extra) -> {ok, Acc} when
105 Acc :: mongoose_acc:t(),
106 Params :: #{jid := jid:jid()},
107 Extra :: gen_hook:extra().
108 remove_user(Acc, #{jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
109 29 R = mod_private_backend:remove_user(HostType, LUser, LServer),
110 29 mongoose_lib:log_if_backend_error(R, ?MODULE, ?LINE, {Acc, LUser, LServer}),
111 29 {ok, Acc}.
112
113 -spec remove_domain(Acc, Params, Extra) -> {ok , Acc} when
114 Acc :: mongoose_domain_api:remove_domain_acc(),
115 Params :: #{domain := jid:lserver()},
116 Extra :: gen_hook:extra().
117 remove_domain(Acc, #{domain := Domain}, #{host_type := HostType}) ->
118 1 mod_private_backend:remove_domain(HostType, Domain),
119 1 {ok, Acc}.
120
121 process_iq(Acc,
122 From = #jid{lserver = LServer, luser = LUser},
123 To = #jid{lserver = LServer, luser = LUser},
124 IQ = #iq{type = Type, sub_el = SubElem = #xmlel{children = Elems}},
125 _Extra) ->
126 36 HostType = mongoose_acc:host_type(Acc),
127 36 IsEqual = jid:are_bare_equal(From, To),
128 36 Strategy = choose_strategy(IsEqual, Type),
129 36 Res = case Strategy of
130 get ->
131 12 NS2XML = to_map(Elems),
132 12 XMLs = mod_private_backend:multi_get_data(HostType, LUser, LServer, NS2XML),
133 12 IQ#iq{type = result, sub_el = [SubElem#xmlel{children = XMLs}]};
134 set ->
135 24 NS2XML = to_map(Elems),
136 24 Result = mod_private_backend:multi_set_data(HostType, LUser, LServer, NS2XML),
137 24 case Result of
138 ok ->
139 24 IQ#iq{type = result, sub_el = [SubElem]};
140 {error, Reason} ->
141
:-(
?LOG_ERROR(#{what => multi_set_data_failed, reason => Reason,
142
:-(
user => LUser, server => LServer}),
143
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error());
144 {aborted, Reason} ->
145
:-(
?LOG_ERROR(#{what => multi_set_data_aborted, reason => Reason,
146
:-(
user => LUser, server => LServer}),
147
:-(
error_iq(IQ, mongoose_xmpp_errors:internal_server_error())
148 end;
149 forbidden ->
150
:-(
error_iq(IQ, mongoose_xmpp_errors:forbidden())
151 end,
152 36 {Acc, Res};
153 process_iq(Acc, _From, _To, IQ, _Extra) ->
154 2 Txt = <<"Only requests from/to your JID are allowed">>,
155 2 Err = mongoose_xmpp_errors:forbidden(<<"en">>, Txt),
156 2 Res = error_iq(IQ, Err),
157 2 {Acc, Res}.
158
159 %% ------------------------------------------------------------------
160 %% Helpers
161
162 12 choose_strategy(true, get) -> get;
163 24 choose_strategy(true, set) -> set;
164
:-(
choose_strategy(_, _ ) -> forbidden.
165
166 element_to_namespace(#xmlel{attrs = Attrs}) ->
167 36 xml:get_attr_s(<<"xmlns">>, Attrs);
168 element_to_namespace(_) ->
169
:-(
<<>>.
170
171 %% Skip invalid elements.
172 to_map(Elems) ->
173 36 [{NS, Elem} || Elem <- Elems, is_valid_namespace(NS = element_to_namespace(Elem))].
174
175 36 is_valid_namespace(Namespace) -> Namespace =/= <<>>.
176
177 error_iq(IQ=#iq{sub_el=SubElem}, ErrorStanza) ->
178 2 IQ#iq{type = error, sub_el = [SubElem, ErrorStanza]}.
179
180 -spec config_metrics(mongooseim:host_type()) -> [{gen_mod:opt_key(), gen_mod:opt_value()}].
181 config_metrics(HostType) ->
182 24 mongoose_module_metrics:opts_for_module(HostType, ?MODULE, [backend]).
Line Hits Source