/[smecontribs]/rpms/ejabberd/contribs7/mod_ctlextra.erl
ViewVC logotype

Contents of /rpms/ejabberd/contribs7/mod_ctlextra.erl

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.4 - (show annotations) (download)
Mon Aug 24 18:19:21 2009 UTC (15 years, 2 months ago) by slords
Branch: MAIN
CVS Tags: ejabberd-2_0_5-2_el4_sme, HEAD
Changes since 1.3: +369 -269 lines
New package import

1 %%%----------------------------------------------------------------------
2 %%% File : mod_ctlextra.erl
3 %%% Author : Badlop <badlop@ono.com>
4 %%% Purpose : Adds more commands to ejabberd_ctl
5 %%% Created : 30 Nov 2006 by Badlop <badlop@ono.com>
6 %%% Id : $Id: mod_ctlextra.erl 873 2009-02-09 18:30:21Z badlop $
7 %%%----------------------------------------------------------------------
8
9 -module(mod_ctlextra).
10 -author('badlop@ono.com').
11
12 -behaviour(gen_mod).
13
14 -export([start/2,
15 stop/1,
16 ctl_process/2,
17 ctl_process/3
18 ]).
19
20 -include("ejabberd.hrl").
21 -include("ejabberd_ctl.hrl").
22 -include("jlib.hrl").
23 -include("mod_roster.hrl").
24
25 %% Copied from ejabberd_sm.erl
26 -record(session, {sid, usr, us, priority, info}).
27
28 -compile(export_all).
29
30 %%-------------
31 %% gen_mod
32 %%-------------
33
34 start(Host, _Opts) ->
35 ejabberd_ctl:register_commands(commands_global(), ?MODULE, ctl_process),
36 ejabberd_ctl:register_commands(Host, commands_host(), ?MODULE, ctl_process).
37
38 stop(Host) ->
39 ejabberd_ctl:unregister_commands(commands_global(), ?MODULE, ctl_process),
40 ejabberd_ctl:unregister_commands(Host, commands_host(), ?MODULE, ctl_process).
41
42 commands_global() ->
43 [
44 {"compile file", "recompile and reload file"},
45 {"load-config file", "load config from file"},
46 {"remove-node nodename", "remove an ejabberd node from the database"},
47
48 %% ejabberd_auth
49 {"delete-older-users days", "delete users that have not logged in the last 'days'"},
50 {"delete-older-users-vhost host days", "delete users that not logged in last 'days' in 'host'"},
51 {"set-password user server password", "set password to user@server"},
52
53 %% ejd2odbc
54 {"export2odbc server output", "export Mnesia tables on server to files on output directory"},
55
56 %% mod_shared_roster
57 {"srg-create group host name description display", "create the group with options"},
58 {"srg-delete group host", "delete the group"},
59 {"srg-user-add user server group host", "add user@server to group on host"},
60 {"srg-user-del user server group host", "delete user@server from group on host"},
61 {"srg-list-groups host", "list the shared roster groups from host"},
62 {"srg-get-info group host", "get info of a specific group on host"},
63
64 %% mod_vcard
65 {"vcard-get user host data [data2]", "get data from the vCard of the user"},
66 {"vcard-set user host data [data2] content", "set data to content on the vCard"},
67
68 %% mod_announce
69 %% announce_send_online host message
70 %% announce_send_all host, message
71
72 %% mod_roster
73 {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1's roster"},
74 %%{"", "subs= none, from, to or both"},
75 %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
76 %%{"", "will add mike@server.com to peter@localhost roster"},
77 {"rem-rosteritem user1 server1 user2 server2", "Remove user2@server2 from user1@server1's roster"},
78 {"rosteritem-purge [options]", "Purge all rosteritems that match filtering options"},
79 {"pushroster file user server", "push template roster in file to user@server"},
80 {"pushroster-all file", "push template roster in file to all those users"},
81 {"push-alltoall server group", "adds all the users to all the users in Group"},
82
83 {"status-list status", "list the logged users with status"},
84 {"status-num status", "number of logged users with status"},
85
86 {"stats registeredusers", "number of registered users"},
87 {"stats onlineusers", "number of logged users"},
88 {"stats onlineusersnode", "number of logged users in the ejabberd node"},
89 {"stats uptime-seconds", "uptime of ejabberd node in seconds"},
90
91 %% misc
92 {"get-cookie", "get the Erlang cookie of this node"},
93 {"killsession user server resource", "kill a user session"}
94 ].
95
96 commands_host() ->
97 [
98 %% mod_last
99 {"num-active-users days", "number of users active in the last 'days'"},
100 {"status-list status", "list the logged users with status"},
101 {"status-num status", "number of logged users with status"},
102 {"stats registeredusers", "number of registered users"},
103 {"stats onlineusers", "number of logged users"},
104
105 %% misc
106 {"ban-account username [reason]", "ban account: kick sessions and change password"}
107 ].
108
109
110 %%-------------
111 %% Commands global
112 %%-------------
113
114 ctl_process(_Val, ["delete-older-users", Days]) ->
115 {removed, N, UR} = delete_older_users(list_to_integer(Days)),
116 io:format("Deleted ~p users: ~p~n", [N, UR]),
117 ?STATUS_SUCCESS;
118
119 ctl_process(_Val, ["delete-older-users-vhost", Host, Days]) ->
120 {removed, N, UR} = delete_older_users_vhost(Host, list_to_integer(Days)),
121 io:format("Deleted ~p users: ~p~n", [N, UR]),
122 ?STATUS_SUCCESS;
123
124 ctl_process(_Val, ["export2odbc", Server, Output]) ->
125 Tables = [
126 {export_last, last},
127 {export_offline, offline},
128 {export_passwd, passwd},
129 {export_private_storage, private_storage},
130 {export_roster, roster},
131 {export_vcard, vcard},
132 {export_vcard_search, vcard_search}],
133 Export = fun({TableFun, Table}) ->
134 Filename = filename:join([Output, atom_to_list(Table)++".txt"]),
135 io:format("Trying to export Mnesia table '~p' on server '~s' to file '~s'~n", [Table, Server, Filename]),
136 Res = (catch ejd2odbc:TableFun(Server, Filename)),
137 io:format(" Result: ~p~n", [Res])
138 end,
139 lists:foreach(Export, Tables),
140 ?STATUS_SUCCESS;
141
142 ctl_process(_Val, ["set-password", User, Server, Password]) ->
143 ejabberd_auth:set_password(User, Server, Password),
144 ?STATUS_SUCCESS;
145
146 ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
147 {ok, Res} = vcard_get(User, Server, [Data]),
148 io:format("~s~n", [Res]),
149 ?STATUS_SUCCESS;
150
151 ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
152 {ok, Res} = vcard_get(User, Server, [Data1, Data2]),
153 io:format("~s~n", [Res]),
154 ?STATUS_SUCCESS;
155
156 ctl_process(_Val, ["vcard-set", User, Server, Data1, Content]) ->
157 {ok, Res} = vcard_set(User, Server, [Data1], Content),
158 io:format("~s~n", [Res]),
159 ?STATUS_SUCCESS;
160
161 ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
162 {ok, Res} = vcard_set(User, Server, [Data1, Data2], Content),
163 io:format("~s~n", [Res]),
164 ?STATUS_SUCCESS;
165
166 ctl_process(_Val, ["compile", Module]) ->
167 compile:file(Module),
168 ?STATUS_SUCCESS;
169
170 ctl_process(_Val, ["remove-node", Node]) ->
171 mnesia:del_table_copy(schema, list_to_atom(Node)),
172 ?STATUS_SUCCESS;
173
174 ctl_process(_Val, ["srg-create" | Parameters]) ->
175 [Group, Host, Name, Description, Display] = group_parameters(Parameters, "'"),
176 Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}],
177 {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
178 ?STATUS_SUCCESS;
179
180 ctl_process(_Val, ["srg-delete", Group, Host]) ->
181 {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
182 ?STATUS_SUCCESS;
183
184 ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
185 {atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
186 ?STATUS_SUCCESS;
187
188 ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
189 {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
190 ?STATUS_SUCCESS;
191
192 ctl_process(_Val, ["srg-list-groups", Host]) ->
193 lists:foreach(
194 fun(SrgGroup) ->
195 io:format("~s~n",[SrgGroup])
196 end,
197 lists:sort(mod_shared_roster:list_groups(Host))),
198 ?STATUS_SUCCESS;
199
200 ctl_process(_Val, ["srg-get-info", Group, Host]) ->
201 Opts = mod_shared_roster:get_group_opts(Host,Group),
202 [io:format("~s: ~p~n", [Title, Value]) || {Title , Value} <- Opts],
203
204 Members = mod_shared_roster:get_group_explicit_users(Host,Group),
205 Members_string = [ " " ++ jlib:jid_to_string(jlib:make_jid(MUser, MServer, ""))
206 || {MUser, MServer} <- Members],
207 io:format("members:~s~n", [Members_string]),
208
209 ?STATUS_SUCCESS;
210
211 ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
212 case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
213 {atomic, ok} ->
214 ?STATUS_SUCCESS;
215 {error, Reason} ->
216 io:format("Can't add ~p@~p to ~p@~p: ~p~n",
217 [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
218 ?STATUS_ERROR;
219 {badrpc, Reason} ->
220 io:format("Can't add roster item to user ~p: ~p~n",
221 [LocalUser, Reason]),
222 ?STATUS_BADRPC
223 end;
224
225 ctl_process(_Val, ["rem-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer]) ->
226 case rem_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer) of
227 {atomic, ok} ->
228 ?STATUS_SUCCESS;
229 {error, Reason} ->
230 io:format("Can't remove ~p@~p to ~p@~p: ~p~n",
231 [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
232 ?STATUS_ERROR;
233 {badrpc, Reason} ->
234 io:format("Can't remove roster item to user ~p: ~p~n",
235 [LocalUser, Reason]),
236 ?STATUS_BADRPC
237 end;
238
239 ctl_process(_Val, ["rosteritem-purge"]) ->
240 io:format("Rosteritems that match all the filtering will be removed.~n"),
241 io:format("Options for filtering:~n"),
242 io:format("~n"),
243 io:format(" -subs none|from|to|both~n"),
244 io:format(" Subscription type. By default all values~n"),
245 io:format("~n"),
246 io:format(" -ask none|out|in~n"),
247 io:format(" Pending subscription. By default all values~n"),
248 io:format("~n"),
249 io:format(" -user JID~n"),
250 io:format(" The JID of the local user.~n"),
251 io:format(" Can use these globs: *, ? and [...].~n"),
252 io:format(" By default it is: * *@*~n"),
253 io:format("~n"),
254 io:format(" -contact JID~n"),
255 io:format(" Similar to 'user', but for the contact JID.~n"),
256 io:format("~n"),
257 io:format("Example:~n"),
258 io:format(" ejabberdctl rosteritem-purge -subs none from to -ask out in -contact *@*icq*~n"),
259 io:format("~n"),
260 ?STATUS_SUCCESS;
261 ctl_process(_Val, ["rosteritem-purge" | Options_list]) ->
262 Options_prop_list = lists:foldl(
263 fun(O, R) ->
264 case O of
265 [$- | K] ->
266 [{K, []} | R];
267 V ->
268 [{K, Vs} | RT] = R,
269 [{K, [V|Vs]} | RT]
270 end
271 end,
272 [],
273 Options_list),
274
275 Subs = [list_to_atom(S)
276 || S <- proplists:get_value("subs",
277 Options_prop_list,
278 ["none", "from", "to", "both"])],
279 Asks = [list_to_atom(S)
280 || S <-
281 proplists:get_value("ask",
282 Options_prop_list,
283 ["none", "out", "in"])],
284 User = proplists:get_value("user", Options_prop_list, ["*", "*@*"]),
285 Contact = proplists:get_value("contact", Options_prop_list, ["*", "*@*"]),
286
287 case rosteritem_purge({Subs, Asks, User, Contact}) of
288 {atomic, ok} ->
289 ?STATUS_SUCCESS;
290 {error, Reason} ->
291 io:format("Error purging rosteritems: ~p~n",
292 [Reason]),
293 ?STATUS_ERROR;
294 {badrpc, Reason} ->
295 io:format("BadRPC purging rosteritems: ~p~n",
296 [Reason]),
297 ?STATUS_BADRPC
298 end;
299
300 ctl_process(_Val, ["pushroster", File, User, Server]) ->
301 case pushroster(File, User, Server) of
302 ok ->
303 ?STATUS_SUCCESS;
304 {error, Reason} ->
305 io:format("Can't push roster ~p to ~p@~p: ~p~n",
306 [File, User, Server, Reason]),
307 ?STATUS_ERROR;
308 {badrpc, Reason} ->
309 io:format("Can't push roster ~p: ~p~n",
310 [File, Reason]),
311 ?STATUS_BADRPC
312 end;
313
314 ctl_process(_Val, ["pushroster-all", File]) ->
315 case pushroster_all([File]) of
316 ok ->
317 ?STATUS_SUCCESS;
318 {error, Reason} ->
319 io:format("Can't push roster ~p: ~p~n",
320 [File, Reason]),
321 ?STATUS_ERROR;
322 {badrpc, Reason} ->
323 io:format("Can't push roster ~p: ~p~n",
324 [File, Reason]),
325 ?STATUS_BADRPC
326 end;
327
328 ctl_process(_Val, ["push-alltoall", Server, Group]) ->
329 case push_alltoall(Server, Group) of
330 ok ->
331 ?STATUS_SUCCESS;
332 {error, Reason} ->
333 io:format("Can't push all to all: ~p~n",
334 [Reason]),
335 ?STATUS_ERROR;
336 {badrpc, Reason} ->
337 io:format("Can't push all to all: ~p~n",
338 [Reason]),
339 ?STATUS_BADRPC
340 end;
341
342 ctl_process(_Val, ["load-config", Path]) ->
343 case ejabberd_config:load_file(Path) of
344 {atomic, ok} ->
345 ?STATUS_SUCCESS;
346 {error, Reason} ->
347 io:format("Can't load config file ~p: ~p~n",
348 [filename:absname(Path), Reason]),
349 ?STATUS_ERROR;
350 {badrpc, Reason} ->
351 io:format("Can't load config file ~p: ~p~n",
352 [filename:absname(Path), Reason]),
353 ?STATUS_BADRPC
354 end;
355
356 ctl_process(_Val, ["stats", Stat]) ->
357 Res = case Stat of
358 "uptime-seconds" -> uptime_seconds();
359 "registeredusers" -> length(ejabberd_auth:dirty_get_registered_users());
360 "onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list());
361 "onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list())
362 end,
363 io:format("~p~n", [Res]),
364 ?STATUS_SUCCESS;
365
366 ctl_process(_Val, ["status-num", Status_required]) ->
367 ctl_process(_Val, "all", ["status-num", Status_required]);
368
369 ctl_process(_Val, ["status-list", Status_required]) ->
370 ctl_process(_Val, "all", ["status-list", Status_required]);
371
372 ctl_process(_Val, ["get-cookie"]) ->
373 io:format("~s~n", [atom_to_list(erlang:get_cookie())]),
374 ?STATUS_SUCCESS;
375
376 ctl_process(_Val, ["killsession", User, Server, Resource | Tail]) ->
377 kick_session(User, Server, Resource, prepare_reason(Tail)),
378 ?STATUS_SUCCESS;
379
380 ctl_process(Val, _Args) ->
381 Val.
382
383
384 %%-------------
385 %% Commands vhost
386 %%-------------
387
388 ctl_process(_Val, Host, ["num-active-users", Days]) ->
389 Number = num_active_users(Host, list_to_integer(Days)),
390 io:format("~p~n", [Number]),
391 ?STATUS_SUCCESS;
392
393 ctl_process(_Val, Host, ["stats", Stat]) ->
394 Res = case Stat of
395 "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
396 "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
397 end,
398 io:format("~p~n", [Res]),
399 ?STATUS_SUCCESS;
400
401 ctl_process(_Val, Host, ["status-num", Status_required]) ->
402 Num = length(get_status_list(Host, Status_required)),
403 io:format("~p~n", [Num]),
404 ?STATUS_SUCCESS;
405
406 ctl_process(_Val, Host, ["status-list", Status_required]) ->
407 Res = get_status_list(Host, Status_required),
408 [ io:format("~s@~s ~s ~p \"~s\"~n", [U, S, R, P, St]) || {U, S, R, P, St} <- Res],
409 ?STATUS_SUCCESS;
410
411 ctl_process(_Val, Host, ["ban-account", User | Tail]) ->
412 ban_account(User, Host, prepare_reason(Tail)),
413 ?STATUS_SUCCESS;
414
415 ctl_process(Val, _Host, _Args) ->
416 Val.
417
418
419 %%-------------
420 %% Utils
421 %%-------------
422
423 uptime_seconds() ->
424 trunc(element(1, erlang:statistics(wall_clock))/1000).
425
426 get_status_list(Host, Status_required) ->
427 %% Get list of all logged users
428 Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
429 %% Reformat the list
430 Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
431 Fhost = case Host of
432 "all" ->
433 %% All hosts are requested, so dont filter at all
434 fun(_, _) -> true end;
435 _ ->
436 %% Filter the list, only Host is interesting
437 fun(A, B) -> A == B end
438 end,
439 Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
440 %% For each Pid, get its presence
441 Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
442 %% Filter by status
443 Fstatus = case Status_required of
444 "all" ->
445 fun(_, _) -> true end;
446 _ ->
447 fun(A, B) -> A == B end
448 end,
449 [{User, Server, Resource, Priority, stringize(Status_text)}
450 || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
451 apply(Fstatus, [Status, Status_required])].
452
453 %% Make string more print-friendly
454 stringize(String) ->
455 %% Replace newline characters with other code
456 element(2, regexp:gsub(String, "\n", "\\n")).
457
458 add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
459 subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
460 route_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription),
461 {atomic, ok}.
462
463 subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
464 R = #roster{usj = {LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
465 us = {LocalUser,LocalServer},
466 jid = {RemoteUser,RemoteServer,[]},
467 name = Nick,
468 subscription = Subscription, % none, to=you see him, from=he sees you, both
469 ask = none, % out=send request, in=somebody requests you, none
470 groups = [Group],
471 askmessage = Xattrs, % example: [{"category","conference"}]
472 xs = []},
473 mnesia:transaction(fun() -> mnesia:write(R) end).
474
475 rem_rosteritem(LU, LS, RU, RS) ->
476 unsubscribe(LU, LS, RU, RS),
477 route_rosteritem(LU, LS, RU, RS, "", "", "remove"),
478 {atomic, ok}.
479
480 unsubscribe(LocalUser, LocalServer, RemoteUser, RemoteServer) ->
481 Key = {{LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}},
482 {LocalUser,LocalServer}},
483 mnesia:transaction(fun() -> mnesia:delete(roster, Key, write) end).
484
485 route_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription) ->
486 LJID = jlib:make_jid(LocalUser, LocalServer, ""),
487 RJID = jlib:make_jid(RemoteUser, RemoteServer, ""),
488 ToS = jlib:jid_to_string(LJID),
489 ItemJIDS = jlib:jid_to_string(RJID),
490 GroupXML = {xmlelement, "group", [], [{xmlcdata, Group}]},
491 Item = {xmlelement, "item",
492 [{"jid", ItemJIDS},
493 {"name", Nick},
494 {"subscription", Subscription}],
495 [GroupXML]},
496 Query = {xmlelement, "query", [{"xmlns", ?NS_ROSTER}], [Item]},
497 Packet = {xmlelement, "iq", [{"type", "set"}, {"to", ToS}], [Query]},
498 ejabberd_router:route(LJID, LJID, Packet).
499
500
501 %%-----------------------------
502 %% Ban user
503 %%-----------------------------
504
505 ban_account(User, Server, Reason) ->
506 kick_sessions(User, Server, Reason),
507 set_random_password(User, Server, Reason).
508
509 kick_sessions(User, Server, Reason) ->
510 lists:map(
511 fun(Resource) ->
512 kick_session(User, Server, Resource, Reason)
513 end,
514 get_resources(User, Server)).
515
516 kick_session(User, Server, Resource, Reason) ->
517 ejabberd_router:route(
518 jlib:make_jid("", "", ""),
519 jlib:make_jid(User, Server, Resource),
520 {xmlelement, "broadcast", [], [{exit, Reason}]}).
521
522 get_resources(User, Server) ->
523 lists:map(
524 fun(Session) ->
525 element(3, Session#session.usr)
526 end,
527 get_sessions(User, Server)).
528
529 get_sessions(User, Server) ->
530 LUser = jlib:nodeprep(User),
531 LServer = jlib:nameprep(Server),
532 Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us),
533 true = is_list(Sessions),
534 Sessions.
535
536 set_random_password(User, Server, Reason) ->
537 NewPass = build_random_password(Reason),
538 set_password(User, Server, NewPass).
539
540 build_random_password(Reason) ->
541 Date = jlib:timestamp_to_iso(calendar:universal_time()),
542 RandomString = randoms:get_string(),
543 "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason.
544
545 set_password(User, Server, Password) ->
546 {atomic, ok} = ejabberd_auth:set_password(User, Server, Password).
547
548 prepare_reason([]) ->
549 "Kicked by administrator";
550 prepare_reason([Reason]) ->
551 Reason;
552 prepare_reason(StringList) ->
553 string:join(StringList, "_").
554
555
556 %%-----------------------------
557 %% Purge roster items
558 %%-----------------------------
559
560 rosteritem_purge(Options) ->
561 Num_rosteritems = mnesia:table_info(roster, size),
562 io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
563 Key = mnesia:dirty_first(roster),
564 ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}),
565 {atomic, ok}.
566
567 rip('$end_of_table', _Options, Counters) ->
568 print_progress_line(Counters),
569 ok;
570 rip(Key, Options, {Pr, NT, NV, ND}) ->
571 Key_next = mnesia:dirty_next(roster, Key),
572 ND2 = case decide_rip(Key, Options) of
573 true ->
574 mnesia:dirty_delete(roster, Key),
575 ND+1;
576 false ->
577 ND
578 end,
579 NV2 = NV+1,
580 Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
581 rip(Key_next, Options, {Pr2, NT, NV2, ND2}).
582
583 print_progress_line({Pr, NT, NV, ND}) ->
584 Pr2 = trunc((NV/NT)*100),
585 case Pr == Pr2 of
586 true ->
587 ok;
588 false ->
589 io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
590 end,
591 Pr2.
592
593 decide_rip(Key, {Subs, Asks, User, Contact}) ->
594 case catch mnesia:dirty_read(roster, Key) of
595 [RI] ->
596 lists:member(RI#roster.subscription, Subs)
597 andalso lists:member(RI#roster.ask, Asks)
598 andalso decide_rip_jid(RI#roster.us, User)
599 andalso decide_rip_jid(RI#roster.jid, Contact);
600 _ ->
601 false
602 end.
603
604 %% Returns true if the server of the JID is included in the servers
605 decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
606 decide_rip_jid({UName, UServer}, Match_list);
607 decide_rip_jid({UName, UServer}, Match_list) ->
608 lists:any(
609 fun(Match_string) ->
610 MJID = jlib:string_to_jid(Match_string),
611 MName = MJID#jid.luser,
612 MServer = MJID#jid.lserver,
613 Is_server = is_glob_match(UServer, MServer),
614 case MName of
615 [] when UName == [] ->
616 Is_server;
617 [] ->
618 false;
619 _ ->
620 Is_server
621 andalso is_glob_match(UName, MName)
622 end
623 end,
624 Match_list).
625
626 %% Copied from ejabberd-2.0.0/src/acl.erl
627 is_regexp_match(String, RegExp) ->
628 case regexp:first_match(String, RegExp) of
629 nomatch ->
630 false;
631 {match, _, _} ->
632 true;
633 {error, ErrDesc} ->
634 io:format(
635 "Wrong regexp ~p in ACL: ~p",
636 [RegExp, lists:flatten(regexp:format_error(ErrDesc))]),
637 false
638 end.
639 is_glob_match(String, Glob) ->
640 is_regexp_match(String, regexp:sh_to_awk(Glob)).
641
642
643 %%-----------------------------
644 %% Push Roster from file
645 %%-----------------------------
646
647 pushroster(File, User, Server) ->
648 {ok, [Roster]} = file:consult(File),
649 subscribe_roster({User, Server, "", User}, Roster).
650
651 pushroster_all(File) ->
652 {ok, [Roster]} = file:consult(File),
653 subscribe_all(Roster).
654
655 subscribe_all(Roster) ->
656 subscribe_all(Roster, Roster).
657 subscribe_all([], _) ->
658 ok;
659 subscribe_all([User1 | Users], Roster) ->
660 subscribe_roster(User1, Roster),
661 subscribe_all(Users, Roster).
662
663 subscribe_roster(_, []) ->
664 ok;
665 %% Do not subscribe a user to itself
666 subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
667 subscribe_roster({Name, Server, Group, Nick}, Roster);
668 %% Subscribe Name2 to Name1
669 subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
670 subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
671 subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
672
673 push_alltoall(S, G) ->
674 Users = ejabberd_auth:get_vh_registered_users(S),
675 Users2 = build_list_users(G, Users, []),
676 subscribe_all(Users2).
677
678 build_list_users(_Group, [], Res) ->
679 Res;
680 build_list_users(Group, [{User, Server}|Users], Res) ->
681 build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
682
683 vcard_get(User, Server, Data) ->
684 [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
685 JID = jlib:make_jid(User, Server, ""),
686 IQ = #iq{type = get, xmlns = ?NS_VCARD},
687 IQr = Module:Function(JID, JID, IQ),
688 Res = case IQr#iq.sub_el of
689 [A1] ->
690 case vcard_get(Data, A1) of
691 false -> no_value;
692 Elem -> xml:get_tag_cdata(Elem)
693 end;
694 [] ->
695 no_vcard
696 end,
697 {ok, Res}.
698
699 vcard_get([Data1, Data2], A1) ->
700 case xml:get_subtag(A1, Data1) of
701 false -> false;
702 A2 -> vcard_get([Data2], A2)
703 end;
704
705 vcard_get([Data], A1) ->
706 xml:get_subtag(A1, Data).
707
708 vcard_set(User, Server, Data, Content) ->
709 [{_, Module, Function, _Opts}] = ets:lookup(sm_iqtable, {?NS_VCARD, Server}),
710 JID = jlib:make_jid(User, Server, ""),
711 IQ = #iq{type = get, xmlns = ?NS_VCARD},
712 IQr = Module:Function(JID, JID, IQ),
713
714 %% Get old vcard
715 A4 = case IQr#iq.sub_el of
716 [A1] ->
717 {_, _, _, A2} = A1,
718 update_vcard_els(Data, Content, A2);
719 [] ->
720 update_vcard_els(Data, Content, [])
721 end,
722
723 %% Build new vcard
724 SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
725 IQ2 = #iq{type=set, sub_el = SubEl},
726
727 Module:Function(JID, JID, IQ2),
728 {ok, "done"}.
729
730 update_vcard_els(Data, Content, Els1) ->
731 Els2 = lists:keysort(2, Els1),
732 [Data1 | Data2] = Data,
733 NewEl = case Data2 of
734 [] ->
735 {xmlelement, Data1, [], [{xmlcdata,Content}]};
736 [D2] ->
737 OldEl = case lists:keysearch(Data1, 2, Els2) of
738 {value, A} -> A;
739 false -> {xmlelement, Data1, [], []}
740 end,
741 {xmlelement, _, _, ContentOld1} = OldEl,
742 Content2 = [{xmlelement, D2, [], [{xmlcdata,Content}]}],
743 ContentOld2 = lists:keysort(2, ContentOld1),
744 ContentOld3 = lists:keydelete(D2, 2, ContentOld2),
745 ContentNew = lists:keymerge(2, Content2, ContentOld3),
746 {xmlelement, Data1, [], ContentNew}
747 end,
748 Els3 = lists:keydelete(Data1, 2, Els2),
749 lists:keymerge(2, [NewEl], Els3).
750
751 -record(last_activity, {us, timestamp, status}).
752
753 delete_older_users(Days) ->
754 %% Get the list of registered users
755 Users = ejabberd_auth:dirty_get_registered_users(),
756 delete_older_users(Days, Users).
757
758 delete_older_users_vhost(Host, Days) ->
759 %% Get the list of registered users
760 Users = ejabberd_auth:get_vh_registered_users(Host),
761 delete_older_users(Days, Users).
762
763 delete_older_users(Days, Users) ->
764 %% Convert older time
765 SecOlder = Days*24*60*60,
766
767 %% Get current time
768 {MegaSecs, Secs, _MicroSecs} = now(),
769 TimeStamp_now = MegaSecs * 1000000 + Secs,
770
771 %% For a user, remove if required and answer true
772 F = fun({LUser, LServer}) ->
773 %% Check if the user is logged
774 case ejabberd_sm:get_user_resources(LUser, LServer) of
775 %% If it isnt
776 [] ->
777 %% Look for his last_activity
778 case mnesia:dirty_read(last_activity, {LUser, LServer}) of
779 %% If it is
780 %% existent:
781 [#last_activity{timestamp = TimeStamp}] ->
782 %% get his age
783 Sec = TimeStamp_now - TimeStamp,
784 %% If he is
785 if
786 %% younger than SecOlder:
787 Sec < SecOlder ->
788 %% do nothing
789 false;
790 %% older:
791 true ->
792 %% remove the user
793 ejabberd_auth:remove_user(LUser, LServer),
794 true
795 end;
796 %% nonexistent:
797 [] ->
798 %% remove the user
799 ejabberd_auth:remove_user(LUser, LServer),
800 true
801 end;
802 %% Else
803 _ ->
804 %% do nothing
805 false
806 end
807 end,
808 %% Apply the function to every user in the list
809 Users_removed = lists:filter(F, Users),
810 {removed, length(Users_removed), Users_removed}.
811
812 num_active_users(Host, Days) ->
813 list_last_activity(Host, true, Days).
814
815 %% Code based on ejabberd/src/web/ejabberd_web_admin.erl
816 list_last_activity(Host, Integral, Days) ->
817 {MegaSecs, Secs, _MicroSecs} = now(),
818 TimeStamp = MegaSecs * 1000000 + Secs,
819 TS = TimeStamp - Days * 86400,
820 case catch mnesia:dirty_select(
821 last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
822 [{'>', '$1', TS}],
823 [{'trunc', {'/',
824 {'-', TimeStamp, '$1'},
825 86400}}]}]) of
826 {'EXIT', _Reason} ->
827 [];
828 Vals ->
829 Hist = histogram(Vals, Integral),
830 if
831 Hist == [] ->
832 0;
833 true ->
834 Left = if
835 Days == infinity ->
836 0;
837 true ->
838 Days - length(Hist)
839 end,
840 Tail = if
841 Integral ->
842 lists:duplicate(Left, lists:last(Hist));
843 true ->
844 lists:duplicate(Left, 0)
845 end,
846 lists:nth(Days, Hist ++ Tail)
847 end
848 end.
849 histogram(Values, Integral) ->
850 histogram(lists:sort(Values), Integral, 0, 0, []).
851 histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
852 histogram(T, Integral, Current, Count + 1, Hist);
853 histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
854 if
855 Integral ->
856 histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
857 true ->
858 histogram(Values, Integral, Current + 1, 0, [Count | Hist])
859 end;
860 histogram([], _Integral, _Current, Count, Hist) ->
861 if
862 Count > 0 ->
863 lists:reverse([Count | Hist]);
864 true ->
865 lists:reverse(Hist)
866 end.
867
868 group_parameters(Ps, [Char]) ->
869 {none, Grouped_Ps} = lists:foldl(
870 fun(P, {State, Res}) ->
871 case State of
872 none ->
873 case P of
874 [Char | PTail]->
875 {building, [PTail | Res]};
876 _ ->
877 {none, [P | Res]}
878 end;
879 building ->
880 [ResHead | ResTail] = Res,
881 case lists:last(P) of
882 Char ->
883 P2 = lists:sublist(P, length(P)-1),
884 {none, [ResHead ++ " " ++ P2 | ResTail]};
885 _ ->
886 {building, [ResHead ++ " " ++ P | ResTail]}
887 end
888 end
889 end,
890 {none, []},
891 Ps),
892 lists:reverse(Grouped_Ps).

admin@koozali.org
ViewVC Help
Powered by ViewVC 1.2.1 RSS 2.0 feed