./ct_report/coverage/mod_mam_cassandra_prefs.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @author Uvarov Michael <arcusfelis@gmail.com>
3 %%% @copyright (C) 2013, Uvarov Michael
4 %%% @doc A backend for storing MAM preferencies using Cassandra.
5 %%% @end
6 %%%-------------------------------------------------------------------
7 -module(mod_mam_cassandra_prefs).
8 -behaviour(mongoose_cassandra).
9 -behaviour(gen_mod).
10
11 %% ----------------------------------------------------------------------
12 %% Exports
13
14 %% gen_mod handlers
15 -export([start/2, stop/1]).
16
17 %% MAM hook handlers
18 -behaviour(ejabberd_gen_mam_prefs).
19 -export([get_behaviour/3,
20 get_prefs/3,
21 set_prefs/3,
22 remove_archive/3]).
23
24 -export([prepared_queries/0]).
25
26 -include("mongoose.hrl").
27 -include("jlib.hrl").
28 -include_lib("exml/include/exml.hrl").
29
30 -type host_type() :: mongooseim:host_type().
31
32 %% ----------------------------------------------------------------------
33 %% gen_mod callbacks
34 %% Starting and stopping functions for users' archives
35
36 -spec start(host_type(), gen_mod:module_opts()) -> ok.
37 start(HostType, Opts) ->
38
:-(
gen_hook:add_handlers(hooks(HostType, Opts)).
39
40 -spec stop(host_type()) -> ok.
41 stop(HostType) ->
42
:-(
Opts = gen_mod:get_loaded_module_opts(HostType, ?MODULE),
43
:-(
gen_hook:delete_handlers(hooks(HostType, Opts)).
44
45 %% ----------------------------------------------------------------------
46 %% Hooks
47
48 hooks(HostType, Opts) ->
49
:-(
lists:flatmap(fun(Type) -> hooks(HostType, Type, Opts) end, [pm, muc]).
50
51 hooks(HostType, pm, #{pm := true}) ->
52
:-(
[{mam_get_behaviour, HostType, fun ?MODULE:get_behaviour/3, #{}, 50},
53 {mam_get_prefs, HostType, fun ?MODULE:get_prefs/3, #{}, 50},
54 {mam_set_prefs, HostType, fun ?MODULE:set_prefs/3, #{}, 50},
55 {mam_remove_archive, HostType, fun ?MODULE:remove_archive/3, #{}, 50}];
56 hooks(HostType, muc, #{muc := true}) ->
57
:-(
[{mam_muc_get_behaviour, HostType, fun ?MODULE:get_behaviour/3, #{}, 50},
58 {mam_muc_get_prefs, HostType, fun ?MODULE:get_prefs/3, #{}, 50},
59 {mam_muc_set_prefs, HostType, fun ?MODULE:set_prefs/3, #{}, 50},
60 {mam_muc_remove_archive, HostType, fun ?MODULE:remove_archive/3, #{}, 50}];
61 hooks(_HostType, _Opt, _Opts) ->
62
:-(
[].
63
64 %% ----------------------------------------------------------------------
65
66 prepared_queries() ->
67
:-(
[
68 {set_prefs_ts_query,
69 "INSERT INTO mam_config(user_jid, remote_jid, behaviour) VALUES (?, ?, ?) USING TIMESTAMP ?"},
70 {get_prefs_query,
71 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ?"},
72 {get_behaviour_bare_query,
73 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ? AND remote_jid IN ('', ?)"},
74 {get_behaviour_full_query,
75 "SELECT remote_jid, behaviour FROM mam_config WHERE user_jid = ? AND remote_jid "
76 "IN ('', :start_remote_jid, :end_remote_jid)"},
77 {del_prefs_ts_query,
78 "DELETE FROM mam_config USING TIMESTAMP ? WHERE user_jid = ?"}
79 ].
80
81 %% ----------------------------------------------------------------------
82 %% Internal functions and callbacks
83
84 -spec get_behaviour(Acc, Params, Extra) -> {ok, Acc} when
85 Acc :: mod_mam:archive_behaviour(),
86 Params :: ejabberd_gen_mam_prefs:get_behaviour_params(),
87 Extra :: gen_hook:extra().
88 get_behaviour(DefaultBehaviour,
89 #{owner := LocJID, remote := RemJID},
90 #{host_type := HostType}) ->
91
:-(
get_behaviour2(DefaultBehaviour, LocJID, RemJID, HostType);
92 get_behaviour(DefaultBehaviour,
93 #{room := LocJID, remote := RemJID},
94 #{host_type := HostType}) ->
95
:-(
get_behaviour2(DefaultBehaviour, LocJID, RemJID, HostType).
96
97 get_behaviour2(DefaultBehaviour, LocJID, RemJID, HostType) ->
98
:-(
BUserJID = mod_mam_utils:bare_jid(LocJID),
99
:-(
BRemBareJID = mod_mam_utils:bare_jid(RemJID),
100
:-(
BRemJID = mod_mam_utils:full_jid(RemJID),
101
:-(
case query_behaviour(HostType, LocJID, BUserJID, BRemJID, BRemBareJID) of
102 {ok, []} ->
103
:-(
{ok, DefaultBehaviour};
104 {ok, [_ | _] = Rows} ->
105 %% After sort <<>>, <<"a">>, <<"a/b">>
106
:-(
SortedRows = lists:sort(
107 fun(#{remote_jid := JID1, behaviour := B1},
108 #{remote_jid := JID2, behaviour := B2}) ->
109
:-(
{JID1, B1} < {JID2, B2}
110 end, Rows),
111
:-(
#{behaviour := Behaviour} = lists:last(SortedRows),
112
:-(
{ok, decode_behaviour(Behaviour)}
113 end.
114
115
116 -spec set_prefs(Acc, Params, Extra) -> {ok, Acc} when
117 Acc :: term(),
118 Params :: ejabberd_gen_mam_prefs:set_prefs_params(),
119 Extra :: gen_hook:extra().
120 set_prefs(_Result,
121 #{owner := UserJID, default_mode := DefaultMode, always_jids := AlwaysJIDs,
122 never_jids := NeverJIDs},
123 #{host_type := HostType}) ->
124
:-(
set_prefs1(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs);
125 set_prefs(_Result,
126 #{room := UserJID, default_mode := DefaultMode, always_jids := AlwaysJIDs,
127 never_jids := NeverJIDs},
128 #{host_type := HostType}) ->
129
:-(
set_prefs1(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs).
130
131 set_prefs1(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
132
:-(
try
133
:-(
{ok, set_prefs2(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs)}
134 catch Type:Error:StackTrace ->
135
:-(
?LOG_ERROR(#{what => mam_set_prefs_failed,
136 user_jid => UserJID, default_mode => DefaultMode,
137 always_jids => AlwaysJIDs, never_jids => NeverJIDs,
138
:-(
class => Type, reason => Error, stacktrace => StackTrace}),
139
:-(
{ok, {error, Error}}
140 end.
141
142 set_prefs2(HostType, UserJID, DefaultMode, AlwaysJIDs, NeverJIDs) ->
143
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
144 %% Force order of operations using timestamps
145 %% http://stackoverflow.com/questions/30317877/cassandra-batch-statement-execution-order
146
:-(
Now = mongoose_cassandra:now_timestamp(),
147
:-(
Next = Now + 1,
148
:-(
DelParams = #{'[timestamp]' => Now, user_jid => BUserJID},
149
:-(
MultiParams = [encode_row(BUserJID, <<>>, encode_behaviour(DefaultMode), Next)]
150
:-(
++ [encode_row(BUserJID, BinJID, <<"A">>, Next) || BinJID <- AlwaysJIDs]
151
:-(
++ [encode_row(BUserJID, BinJID, <<"N">>, Next) || BinJID <- NeverJIDs],
152
:-(
DelQuery = {del_prefs_ts_query, [DelParams]},
153
:-(
SetQuery = {set_prefs_ts_query, MultiParams},
154
:-(
Queries = [DelQuery, SetQuery],
155
:-(
Res = [mongoose_cassandra:cql_write(pool_name(HostType), UserJID, ?MODULE, Query, Params)
156
:-(
|| {Query, Params} <- Queries],
157
:-(
?LOG_DEBUG(#{what => mam_set_prefs, user_jid => UserJID, default_mode => DefaultMode,
158
:-(
always_jids => AlwaysJIDs, never_jids => NeverJIDs, result => Res}),
159
:-(
ok.
160
161 encode_row(BUserJID, BRemoteJID, Behaviour, Timestamp) ->
162
:-(
#{user_jid => BUserJID, remote_jid => BRemoteJID,
163 behaviour => Behaviour, '[timestamp]' => Timestamp}.
164
165
166 -spec get_prefs(Acc, Params, Extra) -> {ok, Acc} when
167 Acc :: mod_mam:preference(),
168 Params :: ejabberd_gen_mam_prefs:get_prefs_params(),
169 Extra :: gen_hook:extra().
170 get_prefs({GlobalDefaultMode, _, _}, #{owner := UserJID}, #{host_type := HostType}) ->
171
:-(
get_prefs2(GlobalDefaultMode, UserJID, HostType);
172 get_prefs({GlobalDefaultMode, _, _}, #{room := UserJID}, #{host_type := HostType}) ->
173
:-(
get_prefs2(GlobalDefaultMode, UserJID, HostType).
174
175 get_prefs2(GlobalDefaultMode, UserJID, HostType) ->
176
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
177
:-(
Params = #{user_jid => BUserJID},
178
:-(
{ok, Rows} = mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
179 get_prefs_query, Params),
180
:-(
{ok, decode_prefs_rows(Rows, GlobalDefaultMode, [], [])}.
181
182 -spec remove_archive(Acc, Params, Extra) -> {ok, Acc} when
183 Acc :: term(),
184 Params :: #{archive_id := mod_mam:archive_id() | undefined, owner => jid:jid(), room => jid:jid()},
185 Extra :: gen_hook:extra().
186 remove_archive(Acc, #{owner := UserJID}, #{host_type := HostType}) ->
187
:-(
remove_archive(HostType, UserJID),
188
:-(
{ok, Acc};
189 remove_archive(Acc, #{room := UserJID}, #{host_type := HostType}) ->
190
:-(
remove_archive(HostType, UserJID),
191
:-(
{ok, Acc}.
192
193 remove_archive(HostType, UserJID) ->
194
:-(
BUserJID = mod_mam_utils:bare_jid(UserJID),
195
:-(
Now = mongoose_cassandra:now_timestamp(),
196
:-(
Params = #{'[timestamp]' => Now, user_jid => BUserJID},
197
:-(
mongoose_cassandra:cql_write(pool_name(HostType), UserJID,
198 ?MODULE, del_prefs_ts_query, [Params]).
199
200 -spec query_behaviour(host_type(), UserJID :: jid:jid(), BUserJID :: binary() | string(),
201 BRemJID :: binary() | string(), BRemBareJID :: binary() | string()) -> any().
202 query_behaviour(HostType, UserJID, BUserJID, BRemJID, BRemBareJID)
203 when BRemJID == BRemBareJID ->
204
:-(
Params = #{user_jid => BUserJID, remote_jid => BRemBareJID},
205
:-(
mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
206 get_behaviour_bare_query, Params);
207 query_behaviour(HostType, UserJID, BUserJID, BRemJID, BRemBareJID) ->
208
:-(
Params = #{user_jid => BUserJID, start_remote_jid => BRemJID,
209 end_remote_jid => BRemBareJID},
210
:-(
mongoose_cassandra:cql_read(pool_name(HostType), UserJID, ?MODULE,
211 get_behaviour_full_query, Params).
212
213 %% ----------------------------------------------------------------------
214 %% Helpers
215
216 -spec encode_behaviour('always' | 'never' | 'roster') -> binary().
217
:-(
encode_behaviour(roster) -> <<"R">>;
218
:-(
encode_behaviour(always) -> <<"A">>;
219
:-(
encode_behaviour(never) -> <<"N">>.
220
221
222 -spec decode_behaviour(<<_:8>>) -> 'always' | 'never' | 'roster'.
223
:-(
decode_behaviour(<<"R">>) -> roster;
224
:-(
decode_behaviour(<<"A">>) -> always;
225
:-(
decode_behaviour(<<"N">>) -> never.
226
227 -spec decode_prefs_rows([[term()]], DefaultMode, AlwaysJIDs, NeverJIDs) ->
228 {DefaultMode, AlwaysJIDs, NeverJIDs} when
229 DefaultMode :: mod_mam:archive_behaviour(),
230 AlwaysJIDs :: [jid:literal_jid()],
231 NeverJIDs :: [jid:literal_jid()].
232 decode_prefs_rows([], DefaultMode, AlwaysJIDs, NeverJIDs) ->
233
:-(
{DefaultMode, AlwaysJIDs, NeverJIDs};
234
235 decode_prefs_rows([#{remote_jid := <<>>, behaviour := Behaviour} | Rows],
236 _DefaultMode, AlwaysJIDs, NeverJIDs) ->
237
:-(
decode_prefs_rows(Rows, decode_behaviour(Behaviour), AlwaysJIDs, NeverJIDs);
238 decode_prefs_rows([#{remote_jid := JID, behaviour := <<"A">>} | Rows],
239 DefaultMode, AlwaysJIDs, NeverJIDs) ->
240
:-(
decode_prefs_rows(Rows, DefaultMode, [JID | AlwaysJIDs], NeverJIDs);
241 decode_prefs_rows([#{remote_jid := JID, behaviour := <<"N">>} | Rows],
242 DefaultMode, AlwaysJIDs, NeverJIDs) ->
243
:-(
decode_prefs_rows(Rows, DefaultMode, AlwaysJIDs, [JID | NeverJIDs]).
244
245 %% ----------------------------------------------------------------------
246 %% Params getters
247
248 -spec pool_name(HostType :: host_type()) -> term().
249 pool_name(_HostType) ->
250
:-(
default.
Line Hits Source