1 |
|
%%%------------------------------------------------------------------- |
2 |
|
%%% File : service_admin_extra_roster.erl |
3 |
|
%%% Author : Badlop <badlop@process-one.net>, Piotr Nosek <piotr.nosek@erlang-solutions.com> |
4 |
|
%%% Purpose : Contributed administrative functions and commands |
5 |
|
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_roster). |
27 |
|
-author('badlop@process-one.net'). |
28 |
|
-export([ |
29 |
|
commands/0, |
30 |
|
add_rosteritem/7, |
31 |
|
delete_rosteritem/4, |
32 |
|
process_rosteritems/5, |
33 |
|
get_roster/2, |
34 |
|
push_roster/3, |
35 |
|
push_roster_all/1, |
36 |
|
push_alltoall/2 |
37 |
|
]). |
38 |
|
|
39 |
|
-ignore_xref([ |
40 |
|
commands/0, add_rosteritem/7, delete_rosteritem/4, process_rosteritems/5, |
41 |
|
get_roster/2, push_roster/3, push_roster_all/1, push_alltoall/2 |
42 |
|
]). |
43 |
|
|
44 |
|
-include("mongoose.hrl"). |
45 |
|
-include("ejabberd_commands.hrl"). |
46 |
|
-include("mod_roster.hrl"). |
47 |
|
-include("jlib.hrl"). |
48 |
|
-include_lib("exml/include/exml.hrl"). |
49 |
|
|
50 |
|
-type simple_roster() :: {User :: jid:user(), |
51 |
|
Server :: jid:server(), |
52 |
|
Group :: binary(), |
53 |
|
Nick :: binary()}. |
54 |
|
-type jids_nick_subs_ask_grp() :: {Jids :: list(), |
55 |
|
Nick :: binary(), |
56 |
|
Subs :: subs(), |
57 |
|
_Ask, |
58 |
|
_Group}. |
59 |
|
-type subs() :: atom() | binary(). |
60 |
|
-type push_action() :: remove |
61 |
|
| none |
62 |
|
| {add, Nick :: binary(), Subs :: subs(), |
63 |
|
Group :: binary() | string()}. |
64 |
|
|
65 |
|
-type delete_action() :: {'delete', Subs :: [atom()], Asks :: [atom()], |
66 |
|
[jid:user()], Contacts :: [binary()]}. |
67 |
|
-type list_action() :: {'list', Subs :: [atom()], Asks :: [atom()], |
68 |
|
[jid:user()], Contacts :: [binary()]}. |
69 |
|
|
70 |
|
|
71 |
|
%%% |
72 |
|
%%% Register commands |
73 |
|
%%% |
74 |
|
|
75 |
|
-spec commands() -> [ejabberd_commands:cmd(), ...]. |
76 |
|
commands() -> |
77 |
146 |
[ |
78 |
|
#ejabberd_commands{name = add_rosteritem, tags = [roster], |
79 |
|
desc = "Add an item to a user's roster (supports RDBMS)", |
80 |
|
module = ?MODULE, function = add_rosteritem, |
81 |
|
args = [{localuser, binary}, {localserver, binary}, |
82 |
|
{user, binary}, {server, binary}, |
83 |
|
{nick, binary}, {group, binary}, |
84 |
|
{subs, binary}], |
85 |
|
result = {res, restuple}}, |
86 |
|
%%{"", "subs= none, from, to or both"}, |
87 |
|
%%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, |
88 |
|
%%{"", "will add mike@server.com to peter@localhost roster"}, |
89 |
|
#ejabberd_commands{name = delete_rosteritem, tags = [roster], |
90 |
|
desc = "Delete an item from a user's roster (supports RDBMS)", |
91 |
|
module = ?MODULE, function = delete_rosteritem, |
92 |
|
args = [{localuser, binary}, {localserver, binary}, |
93 |
|
{user, binary}, {server, binary}], |
94 |
|
result = {res, restuple}}, |
95 |
|
#ejabberd_commands{name = process_rosteritems, tags = [roster], |
96 |
|
desc = "List or delete rosteritems that" |
97 |
|
" match filtering options (Mnesia only!)", |
98 |
|
longdesc = "Explanation of each argument:\n" |
99 |
|
" - action: what to do with each rosteritem that " |
100 |
|
"matches all the filtering options\n" |
101 |
|
" - subs: subscription type\n" |
102 |
|
" - asks: pending subscription\n" |
103 |
|
" - users: the JIDs of the local user\n" |
104 |
|
" - contacts: the JIDs of the contact in the roster\n" |
105 |
|
"\n" |
106 |
|
"Allowed values in the arguments:\n" |
107 |
|
" ACTION = list | delete\n" |
108 |
|
" SUBS = SUB[:SUB]* | any\n" |
109 |
|
" SUB = none | from | to | both\n" |
110 |
|
" ASKS = ASK[:ASK]* | any\n" |
111 |
|
" ASK = none | out | in\n" |
112 |
|
" USERS = JID[:JID]* | any\n" |
113 |
|
" CONTACTS = JID[:JID]* | any\n" |
114 |
|
" JID = characters valid in a JID, and can use the " |
115 |
|
"Regular expression syntax:" |
116 |
|
" http://www.erlang.org/doc/man/re.html#id212737\n" |
117 |
|
"\n" |
118 |
|
"This example will list roster items with subscription " |
119 |
|
"'none', 'from' or 'to' that have any ask property, of " |
120 |
|
"local users which JID is in the virtual host " |
121 |
|
"'example.org' and that the contact JID is either a " |
122 |
|
"bare server name (without user part) or that has a " |
123 |
|
"user part and the server part contains the word 'icq'" |
124 |
|
":\n list none:from:to any *@example.org *:*@*icq*", |
125 |
|
module = ?MODULE, function = process_rosteritems, |
126 |
|
args = [{action, string}, {subs, string}, |
127 |
|
{asks, string}, {users, string}, |
128 |
|
{contacts, string}], |
129 |
|
result = {res, binary}}, |
130 |
|
#ejabberd_commands{name = get_roster, tags = [roster], |
131 |
|
desc = "Get roster of a local user", |
132 |
|
module = ?MODULE, function = get_roster, |
133 |
|
args = [{user, binary}, {host, binary}], |
134 |
|
result = {contacts, {list, {contact, {tuple, [ |
135 |
|
{jid, binary}, |
136 |
|
{nick, binary}, |
137 |
|
{subscription, binary}, |
138 |
|
{ask, binary}, |
139 |
|
{group, binary} |
140 |
|
]}}}}}, |
141 |
|
#ejabberd_commands{name = push_roster, tags = [roster], |
142 |
|
desc = "Push template roster from file to a user", |
143 |
|
module = ?MODULE, function = push_roster, |
144 |
|
args = [{file, string}, {user, binary}, {host, binary}], |
145 |
|
result = {res, rescode}}, |
146 |
|
#ejabberd_commands{name = push_roster_all, tags = [roster], |
147 |
|
desc = "Push template roster from file to all those users", |
148 |
|
module = ?MODULE, function = push_roster_all, |
149 |
|
args = [{file, string}], |
150 |
|
result = {res, rescode}}, |
151 |
|
#ejabberd_commands{name = push_roster_alltoall, tags = [roster], |
152 |
|
desc = "Add all the users to all the users of Host in Group", |
153 |
|
module = ?MODULE, function = push_alltoall, |
154 |
|
args = [{host, binary}, {group, binary}], |
155 |
|
result = {res, rescode}} |
156 |
|
]. |
157 |
|
|
158 |
|
%%% |
159 |
|
%%% Roster |
160 |
|
%%% |
161 |
|
|
162 |
|
-spec add_rosteritem(LocalUser :: jid:user(), |
163 |
|
LocalServer :: jid:server(), |
164 |
|
User :: jid:user(), |
165 |
|
Server :: jid:server(), |
166 |
|
Nick :: binary(), |
167 |
|
Group :: binary() | string(), |
168 |
|
Subs :: subs()) -> {Res, string()} when |
169 |
|
Res :: user_doest_not_exist | error | bad_subs | ok. |
170 |
|
add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> |
171 |
5 |
LocalJID = jid:make(LocalUser, LocalServer, <<>>), |
172 |
5 |
case ejabberd_auth:does_user_exist(LocalJID) of |
173 |
|
true -> |
174 |
5 |
RemoteJID = jid:make(User, Server, <<>>), |
175 |
5 |
case subscribe(LocalJID, RemoteJID, Nick, Group, Subs, []) of |
176 |
|
ok -> |
177 |
5 |
do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs); |
178 |
|
{error, Reason} -> |
179 |
:-( |
{error, io_lib:format("~p", [Reason])} |
180 |
|
end; |
181 |
|
false -> |
182 |
:-( |
{user_does_not_exist, |
183 |
|
io_lib:format("Cannot add the item because user ~s@~s does not exist", |
184 |
|
[LocalUser, LocalServer])} |
185 |
|
end. |
186 |
|
|
187 |
|
do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs) -> |
188 |
5 |
case lists:member(Subs, possible_subs_binary()) of |
189 |
|
true -> |
190 |
5 |
push_roster_item(LocalJID, RemoteJID, {add, Nick, Subs, Group}), |
191 |
5 |
{ok, io_lib:format("Added the item to the roster of ~s", [jid:to_binary(LocalJID)])}; |
192 |
|
false -> |
193 |
:-( |
{bad_subs, io_lib:format("Sub ~s is incorrect." |
194 |
|
" Choose one of the following:~nnone~nfrom~nto~nboth", |
195 |
|
[binary_to_list(Subs)])} |
196 |
|
end. |
197 |
|
|
198 |
|
|
199 |
|
%% @doc returns result of mnesia or rdbms transaction |
200 |
|
-spec subscribe(LocalJID :: jid:jid(), |
201 |
|
RemoteJID :: jid:jid(), |
202 |
|
Nick :: binary(), |
203 |
|
Group :: binary() | string(), |
204 |
|
Subs :: subs(), |
205 |
|
_Xattrs :: [jlib:binary_pair()]) -> ok | {error, any()}. |
206 |
|
subscribe(LocalJID, RemoteJID, Nick, Group, SubscriptionS, _Xattrs) -> |
207 |
42 |
ItemEl = build_roster_item(RemoteJID, {add, Nick, SubscriptionS, Group}), |
208 |
42 |
QueryEl = #xmlel{ name = <<"query">>, |
209 |
|
attrs = [{<<"xmlns">>, <<"jabber:iq:roster">>}], |
210 |
|
children = [ItemEl]}, |
211 |
42 |
{ok, HostType} = mongoose_domain_api:get_domain_host_type(LocalJID#jid.lserver), |
212 |
42 |
mod_roster:set_items(HostType, LocalJID, QueryEl). |
213 |
|
|
214 |
|
|
215 |
|
-spec delete_rosteritem(LocalUser :: jid:user(), |
216 |
|
LocalServer :: jid:server(), |
217 |
|
User :: jid:user(), |
218 |
|
Server :: jid:server()) -> {Res, string()} when |
219 |
|
Res :: ok | error | user_does_not_exist. |
220 |
|
delete_rosteritem(LocalUser, LocalServer, User, Server) -> |
221 |
3 |
LocalJID = jid:make(LocalUser, LocalServer, <<>>), |
222 |
3 |
case ejabberd_auth:does_user_exist(LocalJID) of |
223 |
|
true -> |
224 |
3 |
RemoteJID = jid:make(User, Server, <<>>), |
225 |
3 |
case unsubscribe(LocalJID, RemoteJID) of |
226 |
|
ok -> |
227 |
3 |
push_roster_item(LocalJID, RemoteJID, remove), |
228 |
3 |
{ok, io_lib:format("The item removed from roster of ~s", |
229 |
|
[jid:to_binary(LocalJID)])}; |
230 |
|
{error, Reason} -> |
231 |
:-( |
{error, io_lib:format("~p", [Reason])} |
232 |
|
end; |
233 |
|
false -> |
234 |
:-( |
{user_does_not_exist, |
235 |
|
io_lib:format("Cannot delete the item because user ~s@~s doest not exist", |
236 |
|
[LocalUser, LocalServer])} |
237 |
|
end. |
238 |
|
|
239 |
|
|
240 |
|
%% @doc returns result of mnesia or rdbms transaction |
241 |
|
-spec unsubscribe(LocalJID :: jid:jid(), RemoteJID :: jid:jid()) -> ok | {error, any()}. |
242 |
|
unsubscribe(LocalJID, RemoteJID) -> |
243 |
3 |
ItemEl = build_roster_item(RemoteJID, remove), |
244 |
3 |
QueryEl = #xmlel{ name = <<"query">>, |
245 |
|
attrs = [{<<"xmlns">>, <<"jabber:iq:roster">>}], |
246 |
|
children = [ItemEl]}, |
247 |
3 |
{ok, HostType} = mongoose_domain_api:get_domain_host_type(LocalJID#jid.lserver), |
248 |
3 |
mod_roster:set_items(HostType, LocalJID, QueryEl). |
249 |
|
|
250 |
|
%% ----------------------------- |
251 |
|
%% Get Roster |
252 |
|
%% ----------------------------- |
253 |
|
|
254 |
|
-spec get_roster(jid:user(), jid:server()) -> |
255 |
|
[jids_nick_subs_ask_grp()]. |
256 |
|
get_roster(User, Server) -> |
257 |
2 |
UserJID = jid:make(User, Server, <<>>), |
258 |
2 |
{ok, HostType} = mongoose_domain_api:get_domain_host_type(UserJID#jid.lserver), |
259 |
2 |
Acc = mongoose_acc:new(#{location => ?LOCATION, |
260 |
|
host_type => HostType, |
261 |
|
lserver => UserJID#jid.lserver, |
262 |
|
element => undefined}), |
263 |
2 |
Acc2 = mongoose_hooks:roster_get(Acc, UserJID), |
264 |
2 |
Items = mongoose_acc:get(roster, items, [], Acc2), |
265 |
2 |
make_roster(Items). |
266 |
|
|
267 |
|
|
268 |
|
%% @doc Note: if a contact is in several groups, the contact is returned |
269 |
|
%% several times, each one in a different group. |
270 |
|
-spec make_roster([mod_roster:roster()]) -> [jids_nick_subs_ask_grp()]. |
271 |
|
make_roster(Roster) -> |
272 |
2 |
lists:foldl( |
273 |
|
fun(Item, Res) -> |
274 |
3 |
JIDS = jid:to_binary(Item#roster.jid), |
275 |
3 |
Nick = Item#roster.name, |
276 |
3 |
Subs = atom_to_list(Item#roster.subscription), |
277 |
3 |
Ask = atom_to_list(Item#roster.ask), |
278 |
3 |
Groups = case Item#roster.groups of |
279 |
:-( |
[] -> [""]; |
280 |
3 |
Gs -> Gs |
281 |
|
end, |
282 |
3 |
ItemsX = [{JIDS, Nick, Subs, Ask, Group} |
283 |
3 |
|| Group <- Groups], |
284 |
3 |
ItemsX ++ Res |
285 |
|
end, |
286 |
|
[], |
287 |
|
Roster). |
288 |
|
|
289 |
|
|
290 |
|
%%----------------------------- |
291 |
|
%% Push Roster from file |
292 |
|
%%----------------------------- |
293 |
|
|
294 |
|
-spec push_roster(file:name(), jid:user(), jid:server()) -> 'ok'. |
295 |
|
push_roster(File, User, Server) -> |
296 |
1 |
{ok, [Roster]} = file:consult(File), |
297 |
1 |
subscribe_roster({User, Server, <<"">>, User}, roster_list_to_binary(Roster)). |
298 |
|
|
299 |
|
|
300 |
|
-spec push_roster_all(file:name()) -> 'ok'. |
301 |
|
push_roster_all(File) -> |
302 |
1 |
{ok, [Roster]} = file:consult(File), |
303 |
1 |
subscribe_all(roster_list_to_binary(Roster)). |
304 |
|
|
305 |
|
|
306 |
|
-spec roster_list_to_binary([mod_roster:roster()]) -> [simple_roster()]. |
307 |
|
roster_list_to_binary(Roster) -> |
308 |
2 |
[{ |
309 |
|
mongoose_bin:string_to_binary(Usr), |
310 |
|
mongoose_bin:string_to_binary(Srv), |
311 |
|
mongoose_bin:string_to_binary(Grp), |
312 |
2 |
mongoose_bin:string_to_binary(Nick)} || {Usr, Srv, Grp, Nick} <- Roster]. |
313 |
|
|
314 |
|
|
315 |
|
-spec subscribe_all([simple_roster()]) -> 'ok'. |
316 |
|
subscribe_all(Roster) -> |
317 |
2 |
subscribe_all(Roster, Roster). |
318 |
|
subscribe_all([], _) -> |
319 |
2 |
ok; |
320 |
|
subscribe_all([User1 | Users], Roster) -> |
321 |
9 |
subscribe_roster(User1, Roster), |
322 |
9 |
subscribe_all(Users, Roster). |
323 |
|
|
324 |
|
|
325 |
|
-spec subscribe_roster(simple_roster(), [simple_roster()]) -> 'ok'. |
326 |
|
subscribe_roster(_, []) -> |
327 |
10 |
ok; |
328 |
|
%% Do not subscribe a user to itself |
329 |
|
subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) -> |
330 |
9 |
subscribe_roster({Name, Server, Group, Nick}, Roster); |
331 |
|
%% Subscribe Name2 to Name1 |
332 |
|
subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) -> |
333 |
37 |
subscribe(jid:make(Name1, Server1, <<>>), |
334 |
|
jid:make(Name2, Server2, <<>>), |
335 |
|
Nick2, Group2, <<"both">>, []), |
336 |
37 |
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). |
337 |
|
|
338 |
|
|
339 |
|
-spec push_alltoall(jid:server(), binary()) -> 'ok'. |
340 |
|
push_alltoall(S, G) -> |
341 |
1 |
Users = ejabberd_auth:get_vh_registered_users(S), |
342 |
1 |
Users2 = build_list_users(G, Users, []), |
343 |
1 |
subscribe_all(Users2), |
344 |
1 |
ok. |
345 |
|
|
346 |
|
|
347 |
|
-spec build_list_users(Group :: binary(), |
348 |
|
[jid:simple_bare_jid()], |
349 |
|
Res :: [simple_roster()]) -> []. |
350 |
|
build_list_users(_Group, [], Res) -> |
351 |
1 |
Res; |
352 |
|
build_list_users(Group, [{User, Server}|Users], Res) -> |
353 |
4 |
build_list_users(Group, Users, [{User, Server, Group, User}|Res]). |
354 |
|
|
355 |
|
|
356 |
|
%% @doc Push to the roster of account LU@LS the contact U@S. |
357 |
|
%% The specific action to perform is defined in Action. |
358 |
|
-spec push_roster_item(jid:jid(), jid:jid(), Action :: push_action()) -> 'ok'. |
359 |
|
push_roster_item(JID, #jid{luser = U, lserver = S} = RemJID, Action) -> |
360 |
8 |
lists:foreach(fun(R) -> |
361 |
6 |
RJID = jid:replace_resource(JID, R), |
362 |
6 |
BroadcastEl = build_broadcast(U, S, Action), |
363 |
6 |
ejabberd_sm:route(RJID, RJID, BroadcastEl), |
364 |
6 |
Item = build_roster_item(RemJID, Action), |
365 |
6 |
ResIQ = build_iq_roster_push(Item), |
366 |
6 |
ejabberd_router:route(RJID, RJID, ResIQ) |
367 |
|
end, ejabberd_sm:get_user_resources(JID)). |
368 |
|
|
369 |
|
-spec build_roster_item(jid:jid(), push_action()) -> exml:element(). |
370 |
|
build_roster_item(#jid{resource = <<>>} = JID, {add, Nick, Subs, Group}) -> |
371 |
46 |
#xmlel{ name = <<"item">>, |
372 |
|
attrs = [{<<"jid">>, jid:to_binary(JID)}, |
373 |
|
{<<"name">>, Nick}, |
374 |
|
{<<"subscription">>, Subs}], |
375 |
|
children = [#xmlel{name = <<"group">>, children = [#xmlcdata{content = Group}]}] |
376 |
|
}; |
377 |
|
build_roster_item(#jid{resource = <<>>} = JID, remove) -> |
378 |
5 |
#xmlel{ name = <<"item">>, |
379 |
|
attrs = [{<<"jid">>, jid:to_binary(JID)}, |
380 |
|
{<<"subscription">>, <<"remove">>}]}; |
381 |
|
build_roster_item(#jid{} = JID, Action) -> |
382 |
:-( |
build_roster_item(jid:replace_resource(JID, <<>>), Action). |
383 |
|
|
384 |
|
|
385 |
|
-spec build_iq_roster_push(jlib:xmlcdata() | exml:element()) -> exml:element(). |
386 |
|
build_iq_roster_push(Item) -> |
387 |
6 |
#xmlel{ name = <<"iq">>, |
388 |
|
attrs = [{<<"type">>, <<"set">>}, {<<"id">>, <<"push">>}], |
389 |
|
children = [#xmlel{ name = <<"query">>, |
390 |
|
attrs = [{<<"xmlns">>, ?NS_ROSTER}], |
391 |
|
children = [Item]}] }. |
392 |
|
|
393 |
|
-spec build_broadcast(U :: jid:user(), S :: jid:server(), |
394 |
|
push_action()) -> ejabberd_c2s:broadcast(). |
395 |
|
build_broadcast(U, S, {add, _Nick, Subs, _Group}) -> |
396 |
4 |
build_broadcast(U, S, list_to_existing_atom(binary_to_list(Subs))); |
397 |
|
build_broadcast(U, S, remove) -> |
398 |
2 |
build_broadcast(U, S, none); |
399 |
|
%% Subs = both | from | to | none |
400 |
|
build_broadcast(U, S, SubsAtom) when is_atom(SubsAtom) -> |
401 |
6 |
{broadcast, {item, {U, S, <<"">>}, SubsAtom}}. |
402 |
|
|
403 |
|
%%----------------------------- |
404 |
|
%% Purge roster items |
405 |
|
%%----------------------------- |
406 |
|
|
407 |
|
-spec process_rosteritems(Act :: string(), SubsS :: string(), AsksS :: string(), |
408 |
|
UsersS :: string(), ContactsS :: string()) -> binary(). |
409 |
|
process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> |
410 |
:-( |
Action = case ActionS of |
411 |
:-( |
"list" -> list; |
412 |
:-( |
"delete" -> delete |
413 |
|
end, |
414 |
|
|
415 |
:-( |
Subs = lists:foldl( |
416 |
:-( |
fun(any, _) -> [none, from, to, both]; |
417 |
:-( |
(Sub, Subs) -> [Sub | Subs] |
418 |
|
end, |
419 |
|
[], |
420 |
:-( |
[list_to_existing_atom(S) || S <- string:tokens(SubsS, ":")] |
421 |
|
), |
422 |
|
|
423 |
:-( |
Asks = lists:foldl( |
424 |
:-( |
fun(any, _) -> [none, out, in]; |
425 |
:-( |
(Ask, Asks) -> [Ask | Asks] |
426 |
|
end, |
427 |
|
[], |
428 |
:-( |
[list_to_existing_atom(S) || S <- string:tokens(AsksS, ":")] |
429 |
|
), |
430 |
|
|
431 |
:-( |
Users = lists:foldl( |
432 |
:-( |
fun(<<"any">>, _) -> [<<".*">>, <<".*@.*">>]; |
433 |
:-( |
(U, Us) -> [U | Us] |
434 |
|
end, |
435 |
|
[], |
436 |
:-( |
[mongoose_bin:string_to_binary(S) || S <- string:tokens(UsersS, ":")] |
437 |
|
), |
438 |
|
|
439 |
:-( |
Contacts = lists:foldl( |
440 |
:-( |
fun(<<"any">>, _) -> [<<".*">>, <<".*@.*">>]; |
441 |
:-( |
(U, Us) -> [U | Us] |
442 |
|
end, |
443 |
|
[], |
444 |
:-( |
[mongoose_bin:string_to_binary(S) || S <- string:tokens(ContactsS, ":")] |
445 |
|
), |
446 |
|
|
447 |
:-( |
case validate_regexps(Users ++ Contacts) of |
448 |
|
<<>> -> |
449 |
:-( |
Options = {Action, Subs, Asks, Users, Contacts}, |
450 |
|
|
451 |
:-( |
case mnesia:table_info(roster, size) of |
452 |
|
0 -> |
453 |
:-( |
<<"Roster table is empty.\n">>; |
454 |
|
NumRosteritems -> |
455 |
:-( |
Msg1 = <<"There are ", (integer_to_binary(NumRosteritems))/binary, |
456 |
|
" roster items in total.\n">>, |
457 |
:-( |
Key = mnesia:dirty_first(roster), |
458 |
:-( |
Msg2 = rip(Key, Options, <<>>), |
459 |
:-( |
<<Msg1/binary, Msg2/binary>> |
460 |
|
end; |
461 |
|
ErrorMsg -> |
462 |
:-( |
ErrorMsg |
463 |
|
end. |
464 |
|
|
465 |
|
validate_regexps(ListOfRegexps) -> |
466 |
:-( |
lists:foldl( |
467 |
|
fun(RegExp, MsgAcc) -> |
468 |
:-( |
case re:compile(RegExp) of |
469 |
:-( |
{ok, _} -> MsgAcc; |
470 |
|
{error, Error} -> |
471 |
:-( |
NewErr = iolist_to_binary(io_lib:format("Wrong regexp ~p: ~p~n", |
472 |
|
[RegExp, Error])), |
473 |
:-( |
<<MsgAcc/binary, NewErr/binary>> |
474 |
|
end |
475 |
|
end, <<>>, ListOfRegexps). |
476 |
|
|
477 |
|
-spec rip('$end_of_table' | any(), delete_action() | list_action(), binary()) -> binary(). |
478 |
|
rip('$end_of_table', _Options, Acc) -> |
479 |
:-( |
Acc; |
480 |
|
rip(Key, Options, Acc) -> |
481 |
:-( |
KeyNext = mnesia:dirty_next(roster, Key), |
482 |
:-( |
{Action, _, _, _, _} = Options, |
483 |
:-( |
Msg = case decide_rip(Key, Options) of |
484 |
|
true -> |
485 |
:-( |
apply_action(Action, Key); |
486 |
|
false -> |
487 |
:-( |
<<>> |
488 |
|
end, |
489 |
:-( |
rip(KeyNext, Options, <<Acc/binary, Msg/binary>>). |
490 |
|
|
491 |
|
apply_action(list, Key) -> |
492 |
:-( |
{User, Server, JID} = Key, |
493 |
:-( |
{RUser, RServer, _} = JID, |
494 |
:-( |
<<"Matches: ", User/binary, "@", Server/binary, " ", RUser/binary, "@", RServer/binary, "\n">>; |
495 |
|
apply_action(delete, Key) -> |
496 |
:-( |
Msg = apply_action(list, Key), |
497 |
:-( |
mnesia:dirty_delete(roster, Key), |
498 |
:-( |
Msg. |
499 |
|
|
500 |
|
decide_rip(Key, {_Action, Subs, Asks, User, Contact}) -> |
501 |
:-( |
case catch mnesia:dirty_read(roster, Key) of |
502 |
|
[RI] -> |
503 |
:-( |
lists:member(RI#roster.subscription, Subs) |
504 |
:-( |
andalso lists:member(RI#roster.ask, Asks) |
505 |
:-( |
andalso decide_rip_jid(RI#roster.us, User) |
506 |
:-( |
andalso decide_rip_jid(RI#roster.jid, Contact); |
507 |
|
_ -> |
508 |
:-( |
false |
509 |
|
end. |
510 |
|
|
511 |
|
%% Returns true if the server of the JID is included in the servers |
512 |
|
decide_rip_jid({UName, UServer, _UResource}, MatchList) -> |
513 |
:-( |
decide_rip_jid({UName, UServer}, MatchList); |
514 |
|
decide_rip_jid({UName, UServer}, MatchList) -> |
515 |
:-( |
lists:any( |
516 |
|
fun(MatchString) -> |
517 |
:-( |
MJID = jid:from_binary(MatchString), |
518 |
:-( |
MName = MJID#jid.luser, |
519 |
:-( |
MServer = MJID#jid.lserver, |
520 |
:-( |
IsServer = is_regexp_match(UServer, MServer), |
521 |
:-( |
case {MName, UName} of |
522 |
:-( |
{<<>>, <<>>} -> IsServer; |
523 |
:-( |
{<<>>, _} -> false; |
524 |
:-( |
_ -> IsServer andalso is_regexp_match(UName, MName) |
525 |
|
end |
526 |
|
end, |
527 |
|
MatchList). |
528 |
|
|
529 |
|
is_regexp_match(String, RegExp) -> |
530 |
:-( |
case catch re:run(String, RegExp) of |
531 |
|
nomatch -> |
532 |
:-( |
false; |
533 |
|
{match, List} -> |
534 |
:-( |
Size = length(binary_to_list(String)), |
535 |
:-( |
case lists:member({0, Size}, List) of |
536 |
|
true -> |
537 |
:-( |
true; |
538 |
|
false -> |
539 |
:-( |
false |
540 |
|
end; |
541 |
|
Error -> |
542 |
:-( |
?LOG_ERROR(#{what => regexp_match_failed, |
543 |
:-( |
regex => RegExp, string => String, reason => Error}), |
544 |
:-( |
false |
545 |
|
end. |
546 |
|
|
547 |
|
possible_subs_binary() -> |
548 |
5 |
[<<"none">>, <<"from">>, <<"to">>, <<"both">>]. |
549 |
|
|