1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% @author Uvarov Michael <arcusfelis@gmail.com> |
3 |
|
%%% @copyright (C) 2013, Uvarov Michael |
4 |
|
%%% @doc Assigns archive integer identifiers (multihost version). |
5 |
|
%%% |
6 |
|
%%% This module supports several hosts. |
7 |
|
%%% |
8 |
|
%%% Archive id is assigned based on user_name and host. |
9 |
|
%%% @end |
10 |
|
%%%------------------------------------------------------------------- |
11 |
|
-module(mod_mam_rdbms_user). |
12 |
|
-behaviour(gen_mod). |
13 |
|
|
14 |
|
%% gen_mod handlers |
15 |
|
-export([start/2, stop/1, hooks/1, supported_features/0]). |
16 |
|
|
17 |
|
%% ejabberd handlers |
18 |
|
-export([archive_id/3, |
19 |
|
remove_archive/3]). |
20 |
|
|
21 |
|
%% For debugging ONLY |
22 |
|
-export([create_user_archive/3]). |
23 |
|
-ignore_xref([create_user_archive/3]). |
24 |
|
|
25 |
|
-include("mongoose.hrl"). |
26 |
|
-include("jlib.hrl"). |
27 |
|
|
28 |
|
%% ---------------------------------------------------------------------- |
29 |
|
%% gen_mod callbacks |
30 |
|
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok. |
31 |
|
start(_HostType, _Opts) -> |
32 |
100 |
prepare_queries(), |
33 |
100 |
ok. |
34 |
|
|
35 |
|
-spec stop(mongooseim:host_type()) -> ok. |
36 |
|
stop(_HostType) -> |
37 |
100 |
ok. |
38 |
|
|
39 |
|
-spec supported_features() -> [atom()]. |
40 |
|
supported_features() -> |
41 |
1 |
[dynamic_domains]. |
42 |
|
|
43 |
|
hooks(HostType) -> |
44 |
200 |
[{Hook, HostType, Fun, #{}, N} |
45 |
200 |
|| {true, Hook, Fun, N} <- hooks2(HostType)]. |
46 |
|
|
47 |
|
hooks2(HostType) -> |
48 |
|
%% FIXME the auto_remove option is missing from the config spec |
49 |
200 |
AR = gen_mod:get_module_opt(HostType, ?MODULE, auto_remove, false), |
50 |
200 |
PM = gen_mod:get_module_opt(HostType, ?MODULE, pm, false), |
51 |
200 |
MUC = gen_mod:get_module_opt(HostType, ?MODULE, muc, false), |
52 |
200 |
[{PM, mam_archive_id, fun ?MODULE:archive_id/3, 50}, |
53 |
|
{PM and AR, mam_remove_archive, fun ?MODULE:remove_archive/3, 90}, |
54 |
|
{MUC, mam_muc_archive_id, fun ?MODULE:archive_id/3, 50}, |
55 |
|
{MUC and AR, mam_muc_remove_archive, fun ?MODULE:remove_archive/3, 90}]. |
56 |
|
|
57 |
|
prepare_queries() -> |
58 |
100 |
mongoose_rdbms:prepare(mam_user_insert, mam_server_user, [server, user_name], |
59 |
|
<<"INSERT INTO mam_server_user (server, user_name) VALUES (?, ?)">>), |
60 |
100 |
mongoose_rdbms:prepare(mam_user_select, mam_server_user, [server, user_name], |
61 |
|
<<"SELECT id FROM mam_server_user WHERE server=? AND user_name=?">>), |
62 |
100 |
mongoose_rdbms:prepare(mam_user_remove, mam_server_user, [server, user_name], |
63 |
|
<<"DELETE FROM mam_server_user WHERE server=? AND user_name=?">>), |
64 |
100 |
ok. |
65 |
|
|
66 |
|
%%==================================================================== |
67 |
|
%% API |
68 |
|
%%==================================================================== |
69 |
|
-spec archive_id(Acc, Params, Extra) -> {ok, Acc} when |
70 |
|
Acc :: mod_mam:archive_id() | undefined, |
71 |
|
Params :: map(), |
72 |
|
Extra :: gen_hook:extra(). |
73 |
|
archive_id(undefined, |
74 |
|
#{owner := #jid{lserver = LServer, luser = LUser}}, |
75 |
|
#{host_type := HostType}) -> |
76 |
7906 |
{ok, query_archive_id(HostType, LServer, LUser)}; |
77 |
|
archive_id(ArcID, _Params, _Extra) -> |
78 |
4045 |
{ok, ArcID}. |
79 |
|
|
80 |
|
-spec remove_archive(Acc, Params, Extra) -> {ok, Acc} when |
81 |
|
Acc :: term(), |
82 |
|
Params :: map(), |
83 |
|
Extra :: gen_hook:extra(). |
84 |
|
remove_archive(Acc, |
85 |
|
#{owner := #jid{lserver = LServer, luser = LUser}}, |
86 |
|
#{host_type := HostType}) -> |
87 |
:-( |
execute_user_remove(HostType, LServer, LUser), |
88 |
:-( |
{ok, Acc}. |
89 |
|
|
90 |
|
%%==================================================================== |
91 |
|
%% Internal functions |
92 |
|
%%==================================================================== |
93 |
|
|
94 |
|
execute_user_remove(HostType, LServer, LUser) -> |
95 |
:-( |
{updated, _} = |
96 |
|
mongoose_rdbms:execute(HostType, mam_user_remove, [LUser, LServer]). |
97 |
|
|
98 |
|
-spec query_archive_id(mongooseim:host_type(), jid:lserver(), jid:luser()) -> integer(). |
99 |
|
query_archive_id(HostType, LServer, LUser) -> |
100 |
7906 |
Tries = 5, |
101 |
7906 |
query_archive_id(HostType, LServer, LUser, Tries). |
102 |
|
|
103 |
|
query_archive_id(HostType, LServer, LUser, 0) -> |
104 |
:-( |
?LOG_ERROR(#{what => query_archive_id_failed, |
105 |
:-( |
host => HostType, server => LServer, user => LUser}), |
106 |
:-( |
error(query_archive_id_failed); |
107 |
|
query_archive_id(HostType, LServer, LUser, Tries) when Tries > 0 -> |
108 |
10022 |
Result = mongoose_rdbms:execute(HostType, mam_user_select, [LServer, LUser]), |
109 |
10022 |
case Result of |
110 |
|
{selected, [{IdBin}]} -> |
111 |
7906 |
mongoose_rdbms:result_to_integer(IdBin); |
112 |
|
{selected, []} -> |
113 |
|
%% The user is not found |
114 |
2116 |
create_user_archive(HostType, LServer, LUser), |
115 |
2116 |
query_archive_id(HostType, LServer, LUser, Tries - 1) |
116 |
|
end. |
117 |
|
|
118 |
|
-spec create_user_archive(mongooseim:host_type(), jid:lserver(), jid:luser()) -> ok. |
119 |
|
create_user_archive(HostType, LServer, LUser) -> |
120 |
2116 |
Res = mongoose_rdbms:execute(HostType, mam_user_insert, [LServer, LUser]), |
121 |
2116 |
case Res of |
122 |
|
{updated, 1} -> |
123 |
1999 |
ok; |
124 |
|
_ -> |
125 |
|
%% There is a common race condition case |
126 |
|
%% Duplicate entry ... for key 'uc_mam_server_user_name'. |
127 |
|
%% In this case Res can de: |
128 |
|
%% - {error, duplicate_key} |
129 |
|
%% - {error, "[FreeTDS][SQL Server]Violation of UNIQUE KEY constraint" ++ _} |
130 |
|
%% Let's ignore the errors and just retry in query_archive_id |
131 |
117 |
?LOG_WARNING(#{what => create_user_archive_failed, reason => Res, |
132 |
:-( |
user => LUser, host => HostType, server => LServer}), |
133 |
117 |
ok |
134 |
|
end. |