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 |
|
|
13 |
|
%% gen_mod handlers |
14 |
|
-export([start/2, stop/1, supported_features/0]). |
15 |
|
|
16 |
|
%% ejabberd handlers |
17 |
|
-export([archive_id/3, |
18 |
|
remove_archive/4]). |
19 |
|
|
20 |
|
%% For debugging ONLY |
21 |
|
-export([create_user_archive/3]). |
22 |
|
|
23 |
|
-ignore_xref([archive_id/3, create_user_archive/3, remove_archive/4, start/2, |
24 |
|
stop/1, supported_features/0]). |
25 |
|
|
26 |
|
-include("mongoose.hrl"). |
27 |
|
-include("jlib.hrl"). |
28 |
|
|
29 |
|
%% ---------------------------------------------------------------------- |
30 |
|
%% gen_mod callbacks |
31 |
|
-spec start(mongooseim:host_type(), gen_mod:module_opts()) -> ok. |
32 |
|
start(HostType, _Opts) -> |
33 |
89 |
prepare_queries(), |
34 |
89 |
ejabberd_hooks:add(hooks(HostType)), |
35 |
89 |
ok. |
36 |
|
|
37 |
|
-spec stop(mongooseim:host_type()) -> ok. |
38 |
|
stop(HostType) -> |
39 |
89 |
ejabberd_hooks:delete(hooks(HostType)), |
40 |
89 |
ok. |
41 |
|
|
42 |
|
-spec supported_features() -> [atom()]. |
43 |
|
supported_features() -> |
44 |
89 |
[dynamic_domains]. |
45 |
|
|
46 |
|
hooks(HostType) -> |
47 |
178 |
[{Hook, HostType, ?MODULE, Fun, N} |
48 |
178 |
|| {true, Hook, Fun, N} <- hooks2(HostType)]. |
49 |
|
|
50 |
|
hooks2(HostType) -> |
51 |
178 |
AR = gen_mod:get_module_opt(HostType, ?MODULE, auto_remove, false), |
52 |
178 |
PM = gen_mod:get_module_opt(HostType, ?MODULE, pm, false), |
53 |
178 |
MUC = gen_mod:get_module_opt(HostType, ?MODULE, muc, false), |
54 |
178 |
[{PM, mam_archive_id, archive_id, 50}, |
55 |
|
{PM and AR, mam_remove_archive, remove_archive, 90}, |
56 |
|
{MUC, mam_muc_archive_id, archive_id, 50}, |
57 |
|
{MUC and AR, mam_muc_remove_archive, remove_archive, 90}]. |
58 |
|
|
59 |
|
prepare_queries() -> |
60 |
89 |
mongoose_rdbms:prepare(mam_user_insert, mam_server_user, [server, user_name], |
61 |
|
<<"INSERT INTO mam_server_user (server, user_name) VALUES (?, ?)">>), |
62 |
89 |
mongoose_rdbms:prepare(mam_user_select, mam_server_user, [server, user_name], |
63 |
|
<<"SELECT id FROM mam_server_user WHERE server=? AND user_name=?">>), |
64 |
89 |
mongoose_rdbms:prepare(mam_user_remove, mam_server_user, [server, user_name], |
65 |
|
<<"DELETE FROM mam_server_user WHERE server=? AND user_name=?">>), |
66 |
89 |
ok. |
67 |
|
|
68 |
|
%%==================================================================== |
69 |
|
%% API |
70 |
|
%%==================================================================== |
71 |
|
-spec archive_id(ArcID :: undefined | mod_mam:archive_id(), |
72 |
|
HostType :: mongooseim:host_type(), |
73 |
|
ArchiveJID :: jid:jid()) -> mod_mam:archive_id(). |
74 |
|
archive_id(undefined, HostType, _ArcJID=#jid{lserver = LServer, luser = LUser}) -> |
75 |
6423 |
query_archive_id(HostType, LServer, LUser); |
76 |
|
archive_id(ArcID, _Host, _ArcJID) -> |
77 |
2626 |
ArcID. |
78 |
|
|
79 |
|
-spec remove_archive(Acc :: map(), HostType :: mongooseim:host_type(), |
80 |
|
ArchiveID :: mod_mam:archive_id(), |
81 |
|
ArchiveJID :: jid:jid()) -> map(). |
82 |
|
remove_archive(Acc, HostType, _ArcID, _ArcJID = #jid{lserver = LServer, luser = LUser}) -> |
83 |
:-( |
execute_user_remove(HostType, LServer, LUser), |
84 |
:-( |
Acc. |
85 |
|
|
86 |
|
%%==================================================================== |
87 |
|
%% Internal functions |
88 |
|
%%==================================================================== |
89 |
|
|
90 |
|
execute_user_remove(HostType, LServer, LUser) -> |
91 |
:-( |
{updated, _} = |
92 |
|
mongoose_rdbms:execute(HostType, mam_user_remove, [LUser, LServer]). |
93 |
|
|
94 |
|
-spec query_archive_id(mongooseim:host_type(), jid:lserver(), jid:luser()) -> integer(). |
95 |
|
query_archive_id(HostType, LServer, LUser) -> |
96 |
6423 |
Tries = 5, |
97 |
6423 |
query_archive_id(HostType, LServer, LUser, Tries). |
98 |
|
|
99 |
|
query_archive_id(HostType, LServer, LUser, 0) -> |
100 |
:-( |
?LOG_ERROR(#{what => query_archive_id_failed, |
101 |
:-( |
host => HostType, server => LServer, user => LUser}), |
102 |
:-( |
error(query_archive_id_failed); |
103 |
|
query_archive_id(HostType, LServer, LUser, Tries) when Tries > 0 -> |
104 |
8003 |
Result = mongoose_rdbms:execute(HostType, mam_user_select, [LServer, LUser]), |
105 |
8003 |
case Result of |
106 |
|
{selected, [{IdBin}]} -> |
107 |
6423 |
mongoose_rdbms:result_to_integer(IdBin); |
108 |
|
{selected, []} -> |
109 |
|
%% The user is not found |
110 |
1580 |
create_user_archive(HostType, LServer, LUser), |
111 |
1580 |
query_archive_id(HostType, LServer, LUser, Tries - 1) |
112 |
|
end. |
113 |
|
|
114 |
|
-spec create_user_archive(mongooseim:host_type(), jid:lserver(), jid:luser()) -> ok. |
115 |
|
create_user_archive(HostType, LServer, LUser) -> |
116 |
1580 |
Res = mongoose_rdbms:execute(HostType, mam_user_insert, [LServer, LUser]), |
117 |
1580 |
case Res of |
118 |
|
{updated, 1} -> |
119 |
1484 |
ok; |
120 |
|
_ -> |
121 |
|
%% There is a common race condition case |
122 |
|
%% Duplicate entry ... for key 'uc_mam_server_user_name'. |
123 |
|
%% In this case Res can de: |
124 |
|
%% - {error, duplicate_key} |
125 |
|
%% - {error, "[FreeTDS][SQL Server]Violation of UNIQUE KEY constraint" ++ _} |
126 |
|
%% Let's ignore the errors and just retry in query_archive_id |
127 |
96 |
?LOG_WARNING(#{what => create_user_archive_failed, reason => Res, |
128 |
:-( |
user => LUser, host => HostType, server => LServer}), |
129 |
96 |
ok |
130 |
|
end. |