/[collab-maint]/deb-maint/ejabberd/trunk/debian/patches/mod_ctlextra.diff
ViewVC logotype

Contents of /deb-maint/ejabberd/trunk/debian/patches/mod_ctlextra.diff

Parent Directory Parent Directory | Revision Log Revision Log


Revision 8498 - (show annotations) (download)
Wed Mar 5 14:33:20 2008 UTC (5 years, 2 months ago) by sgolovan-guest
File size: 23600 byte(s)
  * Added a few patches to shared roster module and also added mod_ctlextra
    (a module which offers many more or less useful commands to ejabberdctl)
    following the discussion in the following thread:
    http://lists.debian.org/debian-edu/2008/03/msg00017.html
1 Index: ejabberd/src/ejabberd.app
2 ===================================================================
3 --- ejabberd.orig/src/ejabberd.app
4 +++ ejabberd/src/ejabberd.app
5 @@ -29,6 +29,7 @@
6 jd2ejd,
7 jlib,
8 mod_configure,
9 + mod_ctlextra,
10 mod_disco,
11 mod_echo,
12 mod_last,
13 Index: ejabberd/src/mod_ctlextra.erl
14 ===================================================================
15 --- ejabberd.orig/src/mod_ctlextra.erl
16 +++ ejabberd/src/mod_ctlextra.erl
17 @@ -0,0 +1,731 @@
18 +%%%----------------------------------------------------------------------
19 +%%% File : mod_ctlextra.erl
20 +%%% Author :
21 +%%% Purpose : Adds more options for ejabberd_ctl
22 +%%% Created :
23 +%%% Id :
24 +%%%----------------------------------------------------------------------
25 +
26 +-module(mod_ctlextra).
27 +-author('').
28 +-vsn('0.2.5').
29 +
30 +-behaviour(gen_mod).
31 +
32 +-export([
33 + start/2,
34 + stop/1,
35 + ctl_process/2,
36 + ctl_process/3
37 + ]).
38 +
39 +-include("ejabberd.hrl").
40 +-include("ejabberd_ctl.hrl").
41 +-include("jlib.hrl").
42 +
43 +-record(session, {sid, usr, us, priority}). % copied from ejabberd_sm.erl
44 +
45 +start(Host, _Opts) ->
46 + ejabberd_ctl:register_commands([
47 + {"compile file", "recompile and reload file"},
48 + {"load-config file", "load config from file"},
49 + {"remove-node nodename", "remove an ejabberd node from the database"},
50 +
51 + % ejabberd_auth
52 + {"delete-older-users days", "delete users that have not logged in the last 'days'"},
53 + {"set-password user server password", "set password to user@server"},
54 +
55 + % ejd2odbc
56 + {"export2odbc server output", "export Mnesia tables on server to files on output directory"},
57 +
58 + % mod_offline
59 + {"delete-older-messages days", "delete offline messages older than 'days'"},
60 +
61 + % mod_shared_roster
62 + {"srg-create group host name description display", "create the group with options"},
63 + {"srg-delete group host", "delete the group"},
64 + {"srg-user-add user server group host", "add user@server to group on host"},
65 + {"srg-user-del user server group host", "delete user@server from group on host"},
66 +
67 + % mod_vcard
68 + {"vcard-get user host data [data2]", "get data from the vCard of the user"},
69 + {"vcard-set user host data [data2] content", "set data to content on the vCard"},
70 +
71 + % mod_announce
72 + % announce_send_online host message
73 + % announce_send_all host, message
74 +
75 + % mod_muc
76 + % muc-add room opts
77 + % muc-del room
78 + {"muc-purge days", "destroy rooms with not activity on the last 'days'"},
79 + {"muc-online-rooms", "list existing rooms"},
80 +
81 + % mod_roster
82 + {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1"},
83 + %{"", "subs= none, from, to or both"},
84 + %{"", "example: add-roster peter localhost mike server.com MiKe Employees both"},
85 + %{"", "will add mike@server.com to peter@localhost roster"},
86 + {"pushroster file user server", "push template roster in file to user@server"},
87 + {"pushroster-all file", "push template roster in file to all those users"},
88 + {"push-alltoall server group", "adds all the users to all the users in Group"},
89 +
90 + {"status-list status", "list the logged users with status"},
91 + {"status-num status", "number of logged users with status"},
92 +
93 + {"stats registeredusers", "number of registered users"},
94 + {"stats onlineusers", "number of logged users"},
95 +
96 + % misc
97 + {"killsession user server resource", "kill a user session"}
98 + ], ?MODULE, ctl_process),
99 + ejabberd_ctl:register_commands(Host, [
100 + % mod_muc
101 + {"muc-purge days", "destroy rooms with not activity on the last 'days'"},
102 + {"muc-online-rooms", "list existing rooms"},
103 +
104 + % mod_last
105 + {"num-active-users days", "number of users active in the last 'days'"},
106 + {"status-list status", "list the logged users with status"},
107 + {"status-num status", "number of logged users with status"},
108 + {"stats registeredusers", "number of registered users"},
109 + {"stats onlineusers", "number of logged users"}
110 + ], ?MODULE, ctl_process),
111 + ok.
112 +
113 +stop(_Host) ->
114 + ok.
115 +
116 +
117 +ctl_process(_Val, ["blo"]) ->
118 + FResources = "eeeaaa aaa",
119 + io:format("~s", [FResources]),
120 + ?STATUS_SUCCESS;
121 +
122 +ctl_process(_Val, ["delete-older-messages", Days]) ->
123 + mod_offline:remove_old_messages(list_to_integer(Days)),
124 + ?STATUS_SUCCESS;
125 +
126 +ctl_process(_Val, ["delete-older-users", Days]) ->
127 + {removed, N, UR} = delete_older_users(list_to_integer(Days)),
128 + io:format("Deleted ~p users: ~p~n", [N, UR]),
129 + ?STATUS_SUCCESS;
130 +
131 +ctl_process(_Val, ["export2odbc", Server, Output]) ->
132 + Tables = [
133 + {export_last, last},
134 + {export_offline, offline},
135 + {export_passwd, passwd},
136 + {export_private_storage, private_storage},
137 + {export_roster, roster},
138 + {export_vcard, vcard},
139 + {export_vcard_search, vcard_search}],
140 + Export = fun({TableFun, Table}) ->
141 + Filename = filename:join([Output, atom_to_list(Table)++".txt"]),
142 + io:format("Trying to export Mnesia table '~p' on server '~s' to file '~s'~n", [Table, Server, Filename]),
143 + catch ejd2odbc:TableFun(Server, Filename)
144 + end,
145 + lists:foreach(Export, Tables),
146 + ?STATUS_SUCCESS;
147 +
148 +ctl_process(_Val, ["set-password", User, Server, Password]) ->
149 + ejabberd_auth:set_password(User, Server, Password),
150 + ?STATUS_SUCCESS;
151 +
152 +ctl_process(_Val, ["vcard-get", User, Server, Data]) ->
153 + {ok, Res} = vcard_get(User, Server, {Data}),
154 + io:format("~s~n", [Res]),
155 + ?STATUS_SUCCESS;
156 +
157 +ctl_process(_Val, ["vcard-get", User, Server, Data1, Data2]) ->
158 + {ok, Res} = vcard_get(User, Server, {Data1, Data2}),
159 + io:format("~s~n", [Res]),
160 + ?STATUS_SUCCESS;
161 +
162 +ctl_process(_Val, ["vcard-set", User, Server, Data, Content]) ->
163 + {ok, Res} = vcard_set(User, Server, Data, Content),
164 + io:format("~s~n", [Res]),
165 + ?STATUS_SUCCESS;
166 +
167 +ctl_process(_Val, ["vcard-set", User, Server, Data1, Data2, Content]) ->
168 + {ok, Res} = vcard_set(User, Server, Data1, Data2, Content),
169 + io:format("~s~n", [Res]),
170 + ?STATUS_SUCCESS;
171 +
172 +ctl_process(_Val, ["compile", Module]) ->
173 + compile:file(Module),
174 + ?STATUS_SUCCESS;
175 +
176 +ctl_process(_Val, ["remove-node", Node]) ->
177 + mnesia:del_table_copy(schema, list_to_atom(Node)),
178 + ?STATUS_SUCCESS;
179 +
180 +ctl_process(_Val, ["srg-create", Group, Host, Name, Description, Display]) ->
181 + Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}],
182 + {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts),
183 + ?STATUS_SUCCESS;
184 +
185 +ctl_process(_Val, ["srg-delete", Group, Host]) ->
186 + {atomic, ok} = mod_shared_roster:delete_group(Host, Group),
187 + ?STATUS_SUCCESS;
188 +
189 +ctl_process(_Val, ["srg-user-add", User, Server, Group, Host]) ->
190 + {atomic, ok} = mod_shared_roster:add_user_to_group(Host, {User, Server}, Group),
191 + ?STATUS_SUCCESS;
192 +
193 +ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) ->
194 + {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group),
195 + ?STATUS_SUCCESS;
196 +
197 +ctl_process(_Val, ["muc-purge", Days]) ->
198 + {purged, Num_total, Num_purged, Names_purged} = muc_purge(list_to_integer(Days)),
199 + io:format("Purged ~p chatrooms from a total of ~p on the server:~n~p~n", [Num_purged, Num_total, Names_purged]),
200 + ?STATUS_SUCCESS;
201 +
202 +ctl_process(_Val, ["muc-online-rooms"]) ->
203 + format_print_room(all, ets:tab2list(muc_online_room)),
204 + ?STATUS_SUCCESS;
205 +
206 +ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) ->
207 + case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of
208 + {atomic, ok} ->
209 + ?STATUS_SUCCESS;
210 + {error, Reason} ->
211 + io:format("Can't add ~p@~p to ~p@~p: ~p~n",
212 + [RemoteUser, RemoteServer, LocalUser, LocalServer, Reason]),
213 + ?STATUS_ERROR;
214 + {badrpc, Reason} ->
215 + io:format("Can't add roster item to user ~p: ~p~n",
216 + [LocalUser, Reason]),
217 + ?STATUS_BADRPC
218 + end;
219 +
220 +ctl_process(_Val, ["pushroster", File, User, Server]) ->
221 + case pushroster(File, User, Server) of
222 + ok ->
223 + ?STATUS_SUCCESS;
224 + {error, Reason} ->
225 + io:format("Can't push roster ~p to ~p@~p: ~p~n",
226 + [File, User, Server, Reason]),
227 + ?STATUS_ERROR;
228 + {badrpc, Reason} ->
229 + io:format("Can't push roster ~p: ~p~n",
230 + [File, Reason]),
231 + ?STATUS_BADRPC
232 + end;
233 +
234 +ctl_process(_Val, ["pushroster-all", File]) ->
235 + case pushroster_all([File]) of
236 + ok ->
237 + ?STATUS_SUCCESS;
238 + {error, Reason} ->
239 + io:format("Can't push roster ~p: ~p~n",
240 + [File, Reason]),
241 + ?STATUS_ERROR;
242 + {badrpc, Reason} ->
243 + io:format("Can't push roster ~p: ~p~n",
244 + [File, Reason]),
245 + ?STATUS_BADRPC
246 + end;
247 +
248 +ctl_process(_Val, ["push-alltoall", Server, Group]) ->
249 + case push_alltoall(Server, Group) of
250 + ok ->
251 + ?STATUS_SUCCESS;
252 + {error, Reason} ->
253 + io:format("Can't push all to all: ~p~n",
254 + [Reason]),
255 + ?STATUS_ERROR;
256 + {badrpc, Reason} ->
257 + io:format("Can't push all to all: ~p~n",
258 + [Reason]),
259 + ?STATUS_BADRPC
260 + end;
261 +
262 +ctl_process(_Val, ["load-config", Path]) ->
263 + case ejabberd_config:load_file(Path) of
264 + {atomic, ok} ->
265 + ?STATUS_SUCCESS;
266 + {error, Reason} ->
267 + io:format("Can't load config file ~p: ~p~n",
268 + [filename:absname(Path), Reason]),
269 + ?STATUS_ERROR;
270 + {badrpc, Reason} ->
271 + io:format("Can't load config file ~p: ~p~n",
272 + [filename:absname(Path), Reason]),
273 + ?STATUS_BADRPC
274 + end;
275 +
276 +ctl_process(_Val, ["stats", Stat]) ->
277 + Res = case Stat of
278 + "registeredusers" -> mnesia:table_info(passwd, size);
279 + "onlineusers" -> mnesia:table_info(session, size)
280 + end,
281 + io:format("~p~n", [Res]),
282 + ?STATUS_SUCCESS;
283 +
284 +ctl_process(_Val, ["status-num", Status_required]) ->
285 + ctl_process(_Val, "all", ["status-num", Status_required]);
286 +
287 +ctl_process(_Val, ["status-list", Status_required]) ->
288 + ctl_process(_Val, "all", ["status-list", Status_required]);
289 +
290 +ctl_process(_Val, ["killsession", User, Server, Resource]) ->
291 + ejabberd_router:route(
292 + jlib:make_jid("", "", ""),
293 + jlib:make_jid(User, Server, Resource),
294 + {xmlelement, "broadcast", [], [{exit, "killed"}]}),
295 + ?STATUS_SUCCESS;
296 +
297 +ctl_process(Val, _Args) ->
298 + Val.
299 +
300 +
301 +ctl_process(_Val, Host, ["muc-purge", Days]) ->
302 + {purged, Num_total, Num_purged, Names_purged} = muc_purge(Host, list_to_integer(Days)),
303 + io:format("Purged ~p chatrooms from a total of ~p on the host ~p:~n~p~n", [Num_purged, Num_total, Host, Names_purged]),
304 + ?STATUS_SUCCESS;
305 +
306 +ctl_process(_Val, ServerHost, ["muc-online-rooms"]) ->
307 + MUCHost = find_host(ServerHost),
308 + format_print_room(MUCHost, ets:tab2list(muc_online_room)),
309 + ?STATUS_SUCCESS;
310 +
311 +ctl_process(_Val, Host, ["num-active-users", Days]) ->
312 + Number = num_active_users(Host, list_to_integer(Days)),
313 + io:format("~p~n", [Number]),
314 + ?STATUS_SUCCESS;
315 +
316 +ctl_process(_Val, Host, ["stats", Stat]) ->
317 + Res = case Stat of
318 + "registeredusers" -> length(ejabberd_auth:get_vh_registered_users(Host));
319 + "onlineusers" -> length(ejabberd_sm:get_vh_session_list(Host))
320 + end,
321 + io:format("~p~n", [Res]),
322 + ?STATUS_SUCCESS;
323 +
324 +ctl_process(_Val, Host, ["status-num", Status_required]) ->
325 + Num = length(get_status_list(Host, Status_required)),
326 + io:format("~p~n", [Num]),
327 + ?STATUS_SUCCESS;
328 +
329 +ctl_process(_Val, Host, ["status-list", Status_required]) ->
330 + Res = get_status_list(Host, Status_required),
331 + [ io:format("~s@~s ~s ~p \"~s\"~n", [U, S, R, P, St]) || {U, S, R, P, St} <- Res],
332 + ?STATUS_SUCCESS;
333 +
334 +ctl_process(Val, _Host, _Args) ->
335 + Val.
336 +
337 +
338 +%%-------------
339 +%% UTILS
340 +%%-------------
341 +
342 +get_status_list(Host, Status_required) ->
343 + % Get list of all logged users
344 + Sessions = ejabberd_sm:dirty_get_my_sessions_list(),
345 + % Reformat the list
346 + Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions],
347 + Fhost = case Host of
348 + "all" ->
349 + % All hosts are requested, so don't filter at all
350 + fun(_, _) -> true end;
351 + _ ->
352 + % Filter the list, only Host is interesting
353 + fun(A, B) -> A == B end
354 + end,
355 + Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])],
356 + % For each Pid, get its presence
357 + Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3],
358 + % Filter by status
359 + Fstatus = case Status_required of
360 + "all" ->
361 + fun(_, _) -> true end;
362 + _ ->
363 + fun(A, B) -> A == B end
364 + end,
365 + [{User, Server, Resource, Priority, stringize(Status_text)}
366 + || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4,
367 + apply(Fstatus, [Status, Status_required])].
368 +
369 +% Make string more print-friendly
370 +stringize(String) ->
371 + % Replace newline characters with other code
372 + element(2, regexp:gsub(String, "\n", "\\n")).
373 +
374 +add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) ->
375 + subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs),
376 + % TODO: if the server is not local and Subs=to or both: send subscription request
377 + % TODO: check if the 'remote server' is a virtual host here, else do nothing
378 + %add_rosteritem2(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs, Host).
379 + subscribe(RU, RS, LU, LS, LU, "", invert_subs(Subscription), Xattrs).
380 +
381 +invert_subs(none) -> none;
382 +invert_subs(to) -> none;
383 +invert_subs(from) -> to;
384 +invert_subs(both) -> both.
385 +
386 +subscribe(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subscription, Xattrs) ->
387 + mnesia:transaction(
388 + fun() ->
389 + mnesia:write({
390 + roster,
391 + {LocalUser,LocalServer,{RemoteUser,RemoteServer,[]}}, % usj
392 + {LocalUser,LocalServer}, % us
393 + {RemoteUser,RemoteServer,[]}, % jid
394 + Nick, % name: "Mom", []
395 + Subscription, % subscription: none, to=you see him, from=he sees you, both
396 + none, % ask: out=send request, in=somebody requests you, none
397 + [Group], % groups: ["Family"]
398 + Xattrs, % xattrs: [{"category","conference"}]
399 + [] % xs: []
400 + })
401 + end).
402 +
403 +pushroster(File, User, Server) ->
404 + {ok, [Roster]} = file:consult(File),
405 + subscribe_roster({User, Server, "", User}, Roster).
406 +
407 +pushroster_all(File) ->
408 + {ok, [Roster]} = file:consult(File),
409 + subscribe_all(Roster).
410 +
411 +subscribe_all(Roster) ->
412 + subscribe_all(Roster, Roster).
413 +subscribe_all([], _) ->
414 + ok;
415 +subscribe_all([User1 | Users], Roster) ->
416 + subscribe_roster(User1, Roster),
417 + subscribe_all(Users, Roster).
418 +
419 +subscribe_roster(_, []) ->
420 + ok;
421 +% Do not subscribe a user to itself
422 +subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) ->
423 + subscribe_roster({Name, Server, Group, Nick}, Roster);
424 +% Subscribe Name2 to Name1
425 +subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) ->
426 + subscribe(Name1, Server1, Name2, Server2, Nick2, Group2, both, []),
427 + subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
428 +
429 +push_alltoall(S, G) ->
430 + Users = ejabberd_auth:get_vh_registered_users(S),
431 + Users2 = build_list_users(G, Users, []),
432 + subscribe_all(Users2).
433 +
434 +build_list_users(_Group, [], Res) ->
435 + Res;
436 +build_list_users(Group, [{User, Server}|Users], Res) ->
437 + build_list_users(Group, Users, [{User, Server, Group, User}|Res]).
438 +
439 +vcard_get(User, Server, DataX) ->
440 + [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}),
441 + Elem = vcard_get(DataX, A1),
442 + {ok, xml:get_tag_cdata(Elem)}.
443 +
444 +vcard_get({Data1, Data2}, A1) ->
445 + A2 = xml:get_subtag(A1, Data1),
446 + A3 = xml:get_subtag(A2, Data2),
447 + case A3 of
448 + "" -> A2;
449 + _ -> A3
450 + end;
451 +
452 +vcard_get({Data}, A1) ->
453 + xml:get_subtag(A1, Data).
454 +
455 +vcard_set(User, Server, Data1, Data2, Content) ->
456 + Content2 = {xmlelement, Data2, [], [{xmlcdata,Content}]},
457 + R = {xmlelement, Data1, [], [Content2]},
458 + vcard_set2(User, Server, R, Data1).
459 +
460 +vcard_set(User, Server, Data, Content) ->
461 + R = {xmlelement, Data, [], [{xmlcdata,Content}]},
462 + vcard_set2(User, Server, R, Data).
463 +
464 +vcard_set2(User, Server, R, Data) ->
465 + % Get old vcard
466 + A4 = case mnesia:dirty_read(vcard, {User, Server}) of
467 + [] ->
468 + [R];
469 + [{_, _, A1}] ->
470 + {_, _, _, A2} = A1,
471 + A3 = lists:keydelete(Data, 2, A2),
472 + [R | A3]
473 + end,
474 +
475 + % Build new vcard
476 + SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4},
477 + IQ = #iq{type=set, sub_el = SubEl},
478 + JID = jlib:make_jid(User, Server, ""),
479 +
480 + mod_vcard:process_sm_iq(JID, JID, IQ),
481 + {ok, "done"}.
482 +
483 +
484 +-record(last_activity, {us, timestamp, status}).
485 +
486 +delete_older_users(Days) ->
487 + % Convert older time
488 + SecOlder = Days*24*60*60,
489 +
490 + % Get current time
491 + {MegaSecs, Secs, _MicroSecs} = now(),
492 + TimeStamp_now = MegaSecs * 1000000 + Secs,
493 +
494 + % Get the list of registered users
495 + Users = ejabberd_auth:dirty_get_registered_users(),
496 +
497 + % For a user, remove if required and answer true
498 + F = fun({LUser, LServer}) ->
499 + % Check if the user is logged
500 + case ejabberd_sm:get_user_resources(LUser, LServer) of
501 + % If it isn't
502 + [] ->
503 + % Look for his last_activity
504 + case mnesia:dirty_read(last_activity, {LUser, LServer}) of
505 + % If it is
506 + % existent:
507 + [#last_activity{timestamp = TimeStamp}] ->
508 + % get his age
509 + Sec = TimeStamp_now - TimeStamp,
510 + % If he is
511 + if
512 + % younger than SecOlder:
513 + Sec < SecOlder ->
514 + % do nothing
515 + false;
516 + % older:
517 + true ->
518 + % remove the user
519 + ejabberd_auth:remove_user(LUser, LServer),
520 + true
521 + end;
522 + % nonexistent:
523 + [] ->
524 + % remove the user
525 + ejabberd_auth:remove_user(LUser, LServer),
526 + true
527 + end;
528 + % Else
529 + _ ->
530 + % do nothing
531 + false
532 + end
533 + end,
534 + % Apply the function to every user in the list
535 + Users_removed = lists:filter(F, Users),
536 + {removed, length(Users_removed), Users_removed}.
537 +
538 +num_active_users(Host, Days) ->
539 + list_last_activity(Host, true, Days).
540 +
541 +% Code based on ejabberd/src/web/ejabberd_web_admin.erl
542 +list_last_activity(Host, Integral, Days) ->
543 + {MegaSecs, Secs, _MicroSecs} = now(),
544 + TimeStamp = MegaSecs * 1000000 + Secs,
545 + TS = TimeStamp - Days * 86400,
546 + case catch mnesia:dirty_select(
547 + last_activity, [{{last_activity, {'_', Host}, '$1', '_'},
548 + [{'>', '$1', TS}],
549 + [{'trunc', {'/',
550 + {'-', TimeStamp, '$1'},
551 + 86400}}]}]) of
552 + {'EXIT', _Reason} ->
553 + [];
554 + Vals ->
555 + Hist = histogram(Vals, Integral),
556 + if
557 + Hist == [] ->
558 + 0;
559 + true ->
560 + Left = if
561 + Days == infinity ->
562 + 0;
563 + true ->
564 + Days - length(Hist)
565 + end,
566 + Tail = if
567 + Integral ->
568 + lists:duplicate(Left, lists:last(Hist));
569 + true ->
570 + lists:duplicate(Left, 0)
571 + end,
572 + lists:nth(Days, Hist ++ Tail)
573 + end
574 + end.
575 +histogram(Values, Integral) ->
576 + histogram(lists:sort(Values), Integral, 0, 0, []).
577 +histogram([H | T], Integral, Current, Count, Hist) when Current == H ->
578 + histogram(T, Integral, Current, Count + 1, Hist);
579 +histogram([H | _] = Values, Integral, Current, Count, Hist) when Current < H ->
580 + if
581 + Integral ->
582 + histogram(Values, Integral, Current + 1, Count, [Count | Hist]);
583 + true ->
584 + histogram(Values, Integral, Current + 1, 0, [Count | Hist])
585 + end;
586 +histogram([], _Integral, _Current, Count, Hist) ->
587 + if
588 + Count > 0 ->
589 + lists:reverse([Count | Hist]);
590 + true ->
591 + lists:reverse(Hist)
592 + end.
593 +
594 +
595 +format_print_room(Host1, Rooms)->
596 + lists:foreach(
597 + fun({_, {Roomname, Host},_}) ->
598 + case Host1 of
599 + all ->
600 + io:format("~s ~s ~n", [Roomname, Host]);
601 + Host ->
602 + io:format("~s ~s ~n", [Roomname, Host]);
603 + _ ->
604 + ok
605 + end
606 + end,
607 + Rooms).
608 +
609 +
610 +%%----------------------------
611 +%% Purge MUC
612 +%%----------------------------
613 +
614 +% Required for muc_purge
615 +% Copied from mod_muc/mod_muc_room.erl
616 +-define(DICT, dict).
617 +-record(muc_online_room, {name_host, pid}).
618 +-record(lqueue, {queue, len, max}).
619 +-record(config, {title = "",
620 + allow_change_subj = true,
621 + allow_query_users = true,
622 + allow_private_messages = true,
623 + public = true,
624 + public_list = true,
625 + persistent = false,
626 + moderated = false, % TODO
627 + members_by_default = true,
628 + members_only = false,
629 + allow_user_invites = false,
630 + password_protected = false,
631 + password = "",
632 + anonymous = true,
633 + logging = false
634 + }).
635 +-record(state, {room,
636 + host,
637 + server_host,
638 + access,
639 + jid,
640 + config = #config{},
641 + users,
642 + affiliations,
643 + history,
644 + subject = "",
645 + subject_author = "",
646 + just_created = false}).
647 +
648 +muc_purge(Days) ->
649 + ServerHost = global,
650 + Host = global,
651 + muc_purge2(ServerHost, Host, Days).
652 +
653 +muc_purge(ServerHost, Days) ->
654 + Host = find_host(ServerHost),
655 + muc_purge2(ServerHost, Host, Days).
656 +
657 +muc_purge2(ServerHost, Host, Last_allowed) ->
658 + Room_names = get_room_names(Host),
659 +
660 + Decide = fun(N) -> decide(N, Last_allowed) end,
661 + Rooms_to_delete = lists:filter(Decide, Room_names),
662 +
663 + Num_rooms = length(Room_names),
664 + Num_rooms_to_delete = length(Rooms_to_delete),
665 +
666 + Rooms_to_delete_full = fill_serverhost(Rooms_to_delete, ServerHost),
667 +
668 + Delete = fun({N, H, SH}) ->
669 + mod_muc:room_destroyed(H, N, SH),
670 + mod_muc:forget_room(H, N)
671 + end,
672 + lists:foreach(Delete, Rooms_to_delete_full),
673 + {purged, Num_rooms, Num_rooms_to_delete, Rooms_to_delete}.
674 +
675 +fill_serverhost(Rooms_to_delete, global) ->
676 + ServerHosts1 = ?MYHOSTS,
677 + ServerHosts2 = [ {ServerHost, find_host(ServerHost)} || ServerHost <- ServerHosts1],
678 + [ {Name, Host, find_serverhost(Host, ServerHosts2)} || {Name, Host} <- Rooms_to_delete];
679 +fill_serverhost(Rooms_to_delete, ServerHost) ->
680 + [ {Name, Host, ServerHost}|| {Name, Host} <- Rooms_to_delete].
681 +
682 +find_serverhost(Host, ServerHosts) ->
683 + {value, {ServerHost, Host}} = lists:keysearch(Host, 2, ServerHosts),
684 + ServerHost.
685 +
686 +find_host(ServerHost) ->
687 + gen_mod:get_module_opt(ServerHost, mod_muc, host, "conference." ++ ServerHost).
688 +
689 +decide({Room_name, Host}, Last_allowed) ->
690 + Room_pid = get_room_pid(Room_name, Host),
691 +
692 + C = get_room_config(Room_pid),
693 + Persistent = C#config.persistent,
694 +
695 + S = get_room_state(Room_pid),
696 + Just_created = S#state.just_created,
697 +
698 + Room_users = S#state.users,
699 + Num_users = length(?DICT:to_list(Room_users)),
700 +
701 + History = (S#state.history)#lqueue.queue,
702 + Ts_now = calendar:now_to_universal_time(now()),
703 + Ts_uptime = element(1, erlang:statistics(wall_clock))/1000,
704 + {Have_history, Last} = case queue:is_empty(History) of
705 + true ->
706 + {false, Ts_uptime};
707 + false ->
708 + Last_message = queue:last(History),
709 + {_, _, _, Ts_last, _} = Last_message,
710 + Ts_diff =
711 + calendar:datetime_to_gregorian_seconds(Ts_now)
712 + - calendar:datetime_to_gregorian_seconds(Ts_last),
713 + {true, Ts_diff}
714 + end,
715 +
716 + case {Persistent, Just_created, Num_users, Have_history, seconds_to_days(Last)} of
717 + {true, false, 0, _, Last_days}
718 + when Last_days > Last_allowed ->
719 + true;
720 + _ ->
721 + false
722 + end.
723 +
724 +seconds_to_days(S) ->
725 + round(S) div 60*60*24.
726 +
727 +get_room_names(Host) ->
728 + Get_room_names = fun(Room_reg, Names) ->
729 + case {Host, Room_reg#muc_online_room.name_host} of
730 + {Host, {Name1, Host}} ->
731 + [{Name1, Host} | Names];
732 + {global, {Name1, Host1}} ->
733 + [{Name1, Host1} | Names];
734 + _ ->
735 + Names
736 + end
737 + end,
738 + ets:foldr(Get_room_names, [], muc_online_room).
739 +
740 +get_room_pid(Name, Host) ->
741 + [Room_ets] = ets:lookup(muc_online_room, {Name, Host}),
742 + Room_ets#muc_online_room.pid.
743 +
744 +get_room_config(Room_pid) ->
745 + gen_fsm:sync_send_all_state_event(Room_pid, get_config).
746 +
747 +get_room_state(Room_pid) ->
748 + gen_fsm:sync_send_all_state_event(Room_pid, get_state).

  ViewVC Help
Powered by ViewVC 1.1.5