From 84a00d01b2e0650b5bf6680fc4fca161b8ca0e0d Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Aug 29 2008 20:16:17 +0000 Subject: Ver. 2.0.2 --- diff --git a/.cvsignore b/.cvsignore index 613122b..7b90d25 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1 +1 @@ -ejabberd-2.0.2-beta1.tar.gz +ejabberd-2.0.2.tar.gz diff --git a/ejabberd-ejabberdctl_fix.diff b/ejabberd-ejabberdctl_fix.diff index aca693a..3dc8b69 100644 --- a/ejabberd-ejabberdctl_fix.diff +++ b/ejabberd-ejabberdctl_fix.diff @@ -5,7 +5,7 @@ # define default environment variables NODE=ejabberd -HOST=localhost -+HOST=HOST=`hostname -s` ++HOST=`hostname -s` ERLANG_NODE=$NODE@$HOST ERL=@erl@ -ROOTDIR=@rootdir@ diff --git a/ejabberd.spec b/ejabberd.spec index 98fc108..9ab7f12 100644 --- a/ejabberd.spec +++ b/ejabberd.spec @@ -1,17 +1,18 @@ Name: ejabberd Version: 2.0.2 -Release: 0.3.beta1%{?dist} +Release: 1%{?dist} Summary: A distributed, fault-tolerant Jabber/XMPP server Group: Applications/Internet License: GPLv2+ URL: http://www.ejabberd.im/ -Source0: http://www.process-one.net/downloads/%{name}/%{name}-%{version}-beta1.tar.gz +Source0: http://www.process-one.net/downloads/%{name}/%{version}/%{name}-%{version}.tar.gz Source1: ejabberd.init Source2: ejabberd.logrotate -# http://ejabberd.jabber.ru/ejabberdctl-extra -Source4: https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl +# http://www.ejabberd.im/mod_ctlextra +# svn export -r 557 https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl +Source4: mod_ctlextra.erl # The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html Source5: ejabberd_auth_ad.erl @@ -19,7 +20,7 @@ Source6: mod_shared_roster_ad.erl Source7: mod_vcard_ad.erl Source9: ejabberdctl.pam Source10: ejabberdctl.apps -Source11: ejabberd.pam +Source11: ejabberd.pam # Fedora-specific stuff - fixing paths Patch1: ejabberd-build.patch @@ -104,7 +105,7 @@ if [ "$1" -ge "1" ]; then fi %prep -%setup -q -n %{name}-%{version}-beta1 +%setup -q %patch1 -p0 -b .fix_paths %patch2 -p0 -b .fix_user %patch3 -p0 -b .pam_name @@ -207,6 +208,9 @@ rm -rf %{buildroot} %endif %changelog +* Fri Aug 29 2008 Peter Lemenkov 2.0.2-1 +- Ver. 2.0.2 + * Sat Aug 9 2008 Peter Lemenkov 2.0.2-0.3.beta1 - PAM support (BZ# 452803) diff --git a/import.log b/import.log index a459927..3a13615 100644 --- a/import.log +++ b/import.log @@ -2,3 +2,4 @@ ejabberd-2_0_1-3_fc9:HEAD:ejabberd-2.0.1-3.fc9.src.rpm:1214150816 ejabberd-2_0_1-4_fc9:HEAD:ejabberd-2.0.1-4.fc9.src.rpm:1214160895 ejabberd-2_0_2-0_1_beta1_fc9:HEAD:ejabberd-2.0.2-0.1.beta1.fc9.src.rpm:1218295749 ejabberd-2_0_2-0_3_beta1_fc9:HEAD:ejabberd-2.0.2-0.3.beta1.fc9.src.rpm:1218350775 +ejabberd-2_0_2-1_fc9:HEAD:ejabberd-2.0.2-1.fc9.src.rpm:1220040866 diff --git a/mod_ctlextra.erl b/mod_ctlextra.erl index c723ad6..8753057 100644 --- a/mod_ctlextra.erl +++ b/mod_ctlextra.erl @@ -3,12 +3,11 @@ %%% Author : Badlop %%% Purpose : Adds more commands to ejabberd_ctl %%% Created : 30 Nov 2006 by Badlop -%%% Id : $Id$ +%%% Id : $Id: mod_ctlextra.erl 557 2008-04-25 22:01:13Z badlop $ %%%---------------------------------------------------------------------- -module(mod_ctlextra). -author('badlop@ono.com'). --vsn('$Revision$ '). -behaviour(gen_mod). @@ -23,87 +22,96 @@ -include("jlib.hrl"). -include("mod_roster.hrl"). --record(session, {sid, usr, us, priority}). % copied from ejabberd_sm.erl +%% Copied from ejabberd_sm.erl +-record(session, {sid, usr, us, priority, info}). + +-compile(export_all). + +%%------------- +%% gen_mod +%%------------- start(Host, _Opts) -> - ejabberd_ctl:register_commands([ - {"compile file", "recompile and reload file"}, - {"load-config file", "load config from file"}, - {"remove-node nodename", "remove an ejabberd node from the database"}, - - %% ejabberd_auth - {"delete-older-users days", "delete users that have not logged in the last 'days'"}, - {"set-password user server password", "set password to user@server"}, - - %% ejd2odbc - {"export2odbc server output", "export Mnesia tables on server to files on output directory"}, - - %% mod_offline - {"delete-older-messages days", "delete offline messages older than 'days'"}, - - %% mod_shared_roster - {"srg-create group host name description display", "create the group with options"}, - {"srg-delete group host", "delete the group"}, - {"srg-user-add user server group host", "add user@server to group on host"}, - {"srg-user-del user server group host", "delete user@server from group on host"}, - - %% mod_vcard - {"vcard-get user host data [data2]", "get data from the vCard of the user"}, - {"vcard-set user host data [data2] content", "set data to content on the vCard"}, - - %% mod_announce - %% announce_send_online host message - %% announce_send_all host, message - - %% mod_muc - %% muc-add room opts - %% muc-del room - {"muc-purge days", "destroy rooms with not activity on the last 'days'"}, - {"muc-online-rooms", "list existing rooms"}, - - %% mod_roster - {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1's roster"}, - %%{"", "subs= none, from, to or both"}, - %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, - %%{"", "will add mike@server.com to peter@localhost roster"}, - {"rem-rosteritem user1 server1 user2 server2", "Remove user2@server2 from user1@server1's roster"}, - {"pushroster file user server", "push template roster in file to user@server"}, - {"pushroster-all file", "push template roster in file to all those users"}, - {"push-alltoall server group", "adds all the users to all the users in Group"}, - - {"status-list status", "list the logged users with status"}, - {"status-num status", "number of logged users with status"}, - - {"stats registeredusers", "number of registered users"}, - {"stats onlineusers", "number of logged users"}, - {"stats uptime-seconds", "uptime of ejabberd node in seconds"}, - - %% misc - {"get-cookie", "get the Erlang cookie of this node"}, - {"killsession user server resource", "kill a user session"} - ], ?MODULE, ctl_process), - ejabberd_ctl:register_commands(Host, [ - %% mod_muc - {"muc-purge days", "destroy rooms with not activity on the last 'days'"}, - {"muc-online-rooms", "list existing rooms"}, - - %% mod_last - {"num-active-users days", "number of users active in the last 'days'"}, - {"status-list status", "list the logged users with status"}, - {"status-num status", "number of logged users with status"}, - {"stats registeredusers", "number of registered users"}, - {"stats onlineusers", "number of logged users"} - ], ?MODULE, ctl_process), - ok. - -stop(_Host) -> - ok. - - -ctl_process(_Val, ["blo"]) -> - FResources = "eeeaaa aaa", - io:format("~s", [FResources]), - ?STATUS_SUCCESS; + ejabberd_ctl:register_commands(commands_global(), ?MODULE, ctl_process), + ejabberd_ctl:register_commands(Host, commands_host(), ?MODULE, ctl_process). + +stop(Host) -> + ejabberd_ctl:unregister_commands(commands_global(), ?MODULE, ctl_process), + ejabberd_ctl:unregister_commands(Host, commands_host(), ?MODULE, ctl_process). + +commands_global() -> + [ + {"compile file", "recompile and reload file"}, + {"load-config file", "load config from file"}, + {"remove-node nodename", "remove an ejabberd node from the database"}, + + %% ejabberd_auth + {"delete-older-users days", "delete users that have not logged in the last 'days'"}, + {"set-password user server password", "set password to user@server"}, + + %% ejd2odbc + {"export2odbc server output", "export Mnesia tables on server to files on output directory"}, + + %% mod_offline + {"delete-older-messages days", "delete offline messages older than 'days'"}, + + %% mod_shared_roster + {"srg-create group host name description display", "create the group with options"}, + {"srg-delete group host", "delete the group"}, + {"srg-user-add user server group host", "add user@server to group on host"}, + {"srg-user-del user server group host", "delete user@server from group on host"}, + {"srg-list-groups host", "list the shared roster groups from host"}, + {"srg-get-info group host", "get info of a specific group on host"}, + + %% mod_vcard + {"vcard-get user host data [data2]", "get data from the vCard of the user"}, + {"vcard-set user host data [data2] content", "set data to content on the vCard"}, + + %% mod_announce + %% announce_send_online host message + %% announce_send_all host, message + + %% mod_roster + {"add-rosteritem user1 server1 user2 server2 nick group subs", "Add user2@server2 to user1@server1's roster"}, + %%{"", "subs= none, from, to or both"}, + %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, + %%{"", "will add mike@server.com to peter@localhost roster"}, + {"rem-rosteritem user1 server1 user2 server2", "Remove user2@server2 from user1@server1's roster"}, + {"rosteritem-purge [options]", "Purge all rosteritems that match filtering options"}, + {"pushroster file user server", "push template roster in file to user@server"}, + {"pushroster-all file", "push template roster in file to all those users"}, + {"push-alltoall server group", "adds all the users to all the users in Group"}, + + {"status-list status", "list the logged users with status"}, + {"status-num status", "number of logged users with status"}, + + {"stats registeredusers", "number of registered users"}, + {"stats onlineusers", "number of logged users"}, + {"stats onlineusersnode", "number of logged users in the ejabberd node"}, + {"stats uptime-seconds", "uptime of ejabberd node in seconds"}, + + %% misc + {"get-cookie", "get the Erlang cookie of this node"}, + {"killsession user server resource", "kill a user session"} + ]. + +commands_host() -> + [ + %% mod_last + {"num-active-users days", "number of users active in the last 'days'"}, + {"status-list status", "list the logged users with status"}, + {"status-num status", "number of logged users with status"}, + {"stats registeredusers", "number of registered users"}, + {"stats onlineusers", "number of logged users"}, + + %% misc + {"ban-account username [reason]", "ban account: kick sessions and change password"} + ]. + + +%%------------- +%% Commands global +%%------------- ctl_process(_Val, ["delete-older-messages", Days]) -> mod_offline:remove_old_messages(list_to_integer(Days)), @@ -126,7 +134,8 @@ ctl_process(_Val, ["export2odbc", Server, Output]) -> Export = fun({TableFun, Table}) -> Filename = filename:join([Output, atom_to_list(Table)++".txt"]), io:format("Trying to export Mnesia table '~p' on server '~s' to file '~s'~n", [Table, Server, Filename]), - catch ejd2odbc:TableFun(Server, Filename) + Res = (catch ejd2odbc:TableFun(Server, Filename)), + io:format(" Result: ~p~n", [Res]) end, lists:foreach(Export, Tables), ?STATUS_SUCCESS; @@ -163,7 +172,8 @@ ctl_process(_Val, ["remove-node", Node]) -> mnesia:del_table_copy(schema, list_to_atom(Node)), ?STATUS_SUCCESS; -ctl_process(_Val, ["srg-create", Group, Host, Name, Description, Display]) -> +ctl_process(_Val, ["srg-create" | Parameters]) -> + [Group, Host, Name, Description, Display] = group_parameters(Parameters, "'"), Opts = [{name, Name}, {displayed_groups, [Display]}, {description, Description}], {atomic, ok} = mod_shared_roster:create_group(Host, Group, Opts), ?STATUS_SUCCESS; @@ -180,13 +190,23 @@ ctl_process(_Val, ["srg-user-del", User, Server, Group, Host]) -> {atomic, ok} = mod_shared_roster:remove_user_from_group(Host, {User, Server}, Group), ?STATUS_SUCCESS; -ctl_process(_Val, ["muc-purge", Days]) -> - {purged, Num_total, Num_purged, Names_purged} = muc_purge(list_to_integer(Days)), - io:format("Purged ~p chatrooms from a total of ~p on the server:~n~p~n", [Num_purged, Num_total, Names_purged]), +ctl_process(_Val, ["srg-list-groups", Host]) -> + lists:foreach( + fun(SrgGroup) -> + io:format("~s~n",[SrgGroup]) + end, + lists:sort(mod_shared_roster:list_groups(Host))), ?STATUS_SUCCESS; -ctl_process(_Val, ["muc-online-rooms"]) -> - format_print_room(all, ets:tab2list(muc_online_room)), +ctl_process(_Val, ["srg-get-info", Group, Host]) -> + Opts = mod_shared_roster:get_group_opts(Host,Group), + [io:format("~s: ~p~n", [Title, Value]) || {Title , Value} <- Opts], + + Members = mod_shared_roster:get_group_explicit_users(Host,Group), + Members_string = [ " " ++ jlib:jid_to_string(jlib:make_jid(MUser, MServer, "")) + || {MUser, MServer} <- Members], + io:format("members:~s~n", [Members_string]), + ?STATUS_SUCCESS; ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) -> @@ -217,6 +237,67 @@ ctl_process(_Val, ["rem-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteS ?STATUS_BADRPC end; +ctl_process(_Val, ["rosteritem-purge"]) -> + io:format("Rosteritems that match all the filtering will be removed.~n"), + io:format("Options for filtering:~n"), + io:format("~n"), + io:format(" -subs none|from|to|both~n"), + io:format(" Subscription type. By default all values~n"), + io:format("~n"), + io:format(" -ask none|out|in~n"), + io:format(" Pending subscription. By default all values~n"), + io:format("~n"), + io:format(" -user JID~n"), + io:format(" The JID of the local user.~n"), + io:format(" Can use these globs: *, ? and [...].~n"), + io:format(" By default it is: * *@*~n"), + io:format("~n"), + io:format(" -contact JID~n"), + io:format(" Similar to 'user', but for the contact JID.~n"), + io:format("~n"), + io:format("Example:~n"), + io:format(" ejabberdctl rosteritem-purge -subs none from to -ask out in -contact *@*icq*~n"), + io:format("~n"), + ?STATUS_SUCCESS; +ctl_process(_Val, ["rosteritem-purge" | Options_list]) -> + Options_prop_list = lists:foldl( + fun(O, R) -> + case O of + [$- | K] -> + [{K, []} | R]; + V -> + [{K, Vs} | RT] = R, + [{K, [V|Vs]} | RT] + end + end, + [], + Options_list), + + Subs = [list_to_atom(S) + || S <- proplists:get_value("subs", + Options_prop_list, + ["none", "from", "to", "both"])], + Asks = [list_to_atom(S) + || S <- + proplists:get_value("ask", + Options_prop_list, + ["none", "out", "in"])], + User = proplists:get_value("user", Options_prop_list, ["*", "*@*"]), + Contact = proplists:get_value("contact", Options_prop_list, ["*", "*@*"]), + + case rosteritem_purge({Subs, Asks, User, Contact}) of + {atomic, ok} -> + ?STATUS_SUCCESS; + {error, Reason} -> + io:format("Error purging rosteritems: ~p~n", + [Reason]), + ?STATUS_ERROR; + {badrpc, Reason} -> + io:format("BadRPC purging rosteritems: ~p~n", + [Reason]), + ?STATUS_BADRPC + end; + ctl_process(_Val, ["pushroster", File, User, Server]) -> case pushroster(File, User, Server) of ok -> @@ -277,7 +358,8 @@ ctl_process(_Val, ["stats", Stat]) -> Res = case Stat of "uptime-seconds" -> uptime_seconds(); "registeredusers" -> mnesia:table_info(passwd, size); - "onlineusers" -> mnesia:table_info(session, size) + "onlineusersnode" -> length(ejabberd_sm:dirty_get_my_sessions_list()); + "onlineusers" -> length(ejabberd_sm:dirty_get_sessions_list()) end, io:format("~p~n", [Res]), ?STATUS_SUCCESS; @@ -292,26 +374,17 @@ ctl_process(_Val, ["get-cookie"]) -> io:format("~s~n", [atom_to_list(erlang:get_cookie())]), ?STATUS_SUCCESS; -ctl_process(_Val, ["killsession", User, Server, Resource]) -> - ejabberd_router:route( - jlib:make_jid("", "", ""), - jlib:make_jid(User, Server, Resource), - {xmlelement, "broadcast", [], [{exit, "killed"}]}), +ctl_process(_Val, ["killsession", User, Server, Resource | Tail]) -> + kick_session(User, Server, Resource, prepare_reason(Tail)), ?STATUS_SUCCESS; ctl_process(Val, _Args) -> Val. -ctl_process(_Val, Host, ["muc-purge", Days]) -> - {purged, Num_total, Num_purged, Names_purged} = muc_purge(Host, list_to_integer(Days)), - io:format("Purged ~p chatrooms from a total of ~p on the host ~p:~n~p~n", [Num_purged, Num_total, Host, Names_purged]), - ?STATUS_SUCCESS; - -ctl_process(_Val, ServerHost, ["muc-online-rooms"]) -> - MUCHost = find_host(ServerHost), - format_print_room(MUCHost, ets:tab2list(muc_online_room)), - ?STATUS_SUCCESS; +%%------------- +%% Commands vhost +%%------------- ctl_process(_Val, Host, ["num-active-users", Days]) -> Number = num_active_users(Host, list_to_integer(Days)), @@ -336,14 +409,21 @@ ctl_process(_Val, Host, ["status-list", Status_required]) -> [ io:format("~s@~s ~s ~p \"~s\"~n", [U, S, R, P, St]) || {U, S, R, P, St} <- Res], ?STATUS_SUCCESS; +ctl_process(_Val, Host, ["ban-account", User | Tail]) -> + ban_account(User, Host, prepare_reason(Tail)), + ?STATUS_SUCCESS; + ctl_process(Val, _Host, _Args) -> Val. %%------------- -%% UTILS +%% Utils %%------------- +uptime_seconds() -> + trunc(element(1, erlang:statistics(wall_clock))/1000). + get_status_list(Host, Status_required) -> %% Get list of all logged users Sessions = ejabberd_sm:dirty_get_my_sessions_list(), @@ -418,6 +498,153 @@ route_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Packet = {xmlelement, "iq", [{"type", "set"}, {"to", ToS}], [Query]}, ejabberd_router:route(LJID, LJID, Packet). + +%%----------------------------- +%% Ban user +%%----------------------------- + +ban_account(User, Server, Reason) -> + kick_sessions(User, Server, Reason), + set_random_password(User, Server, Reason). + +kick_sessions(User, Server, Reason) -> + lists:map( + fun(Resource) -> + kick_session(User, Server, Resource, Reason) + end, + get_resources(User, Server)). + +kick_session(User, Server, Resource, Reason) -> + ejabberd_router:route( + jlib:make_jid("", "", ""), + jlib:make_jid(User, Server, Resource), + {xmlelement, "broadcast", [], [{exit, Reason}]}). + +get_resources(User, Server) -> + lists:map( + fun(Session) -> + element(3, Session#session.usr) + end, + get_sessions(User, Server)). + +get_sessions(User, Server) -> + LUser = jlib:nodeprep(User), + LServer = jlib:nameprep(Server), + Sessions = mnesia:dirty_index_read(session, {LUser, LServer}, #session.us), + true = is_list(Sessions), + Sessions. + +set_random_password(User, Server, Reason) -> + NewPass = build_random_password(Reason), + set_password(User, Server, NewPass). + +build_random_password(Reason) -> + Date = jlib:timestamp_to_iso(calendar:universal_time()), + RandomString = randoms:get_string(), + "BANNED_ACCOUNT--" ++ Date ++ "--" ++ RandomString ++ "--" ++ Reason. + +set_password(User, Server, Password) -> + {atomic, ok} = ejabberd_auth:set_password(User, Server, Password). + +prepare_reason([]) -> + "Kicked by administrator"; +prepare_reason([Reason]) -> + Reason; +prepare_reason(StringList) -> + string:join(StringList, "_"). + + +%%----------------------------- +%% Purge roster items +%%----------------------------- + +rosteritem_purge(Options) -> + Num_rosteritems = mnesia:table_info(roster, size), + io:format("There are ~p roster items in total.~n", [Num_rosteritems]), + Key = mnesia:dirty_first(roster), + ok = rip(Key, Options, {0, Num_rosteritems, 0, 0}), + {atomic, ok}. + +rip('$end_of_table', _Options, Counters) -> + print_progress_line(Counters), + ok; +rip(Key, Options, {Pr, NT, NV, ND}) -> + Key_next = mnesia:dirty_next(roster, Key), + ND2 = case decide_rip(Key, Options) of + true -> + mnesia:dirty_delete(roster, Key), + ND+1; + false -> + ND + end, + NV2 = NV+1, + Pr2 = print_progress_line({Pr, NT, NV2, ND2}), + rip(Key_next, Options, {Pr2, NT, NV2, ND2}). + +print_progress_line({Pr, NT, NV, ND}) -> + Pr2 = trunc((NV/NT)*100), + case Pr == Pr2 of + true -> + ok; + false -> + io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND]) + end, + Pr2. + +decide_rip(Key, {Subs, Asks, User, Contact}) -> + case catch mnesia:dirty_read(roster, Key) of + [RI] -> + lists:member(RI#roster.subscription, Subs) + andalso lists:member(RI#roster.ask, Asks) + andalso decide_rip_jid(RI#roster.us, User) + andalso decide_rip_jid(RI#roster.jid, Contact); + _ -> + false + end. + +%% Returns true if the server of the JID is included in the servers +decide_rip_jid({UName, UServer, _UResource}, Match_list) -> + decide_rip_jid({UName, UServer}, Match_list); +decide_rip_jid({UName, UServer}, Match_list) -> + lists:any( + fun(Match_string) -> + MJID = jlib:string_to_jid(Match_string), + MName = MJID#jid.luser, + MServer = MJID#jid.lserver, + Is_server = is_glob_match(UServer, MServer), + case MName of + [] when UName == [] -> + Is_server; + [] -> + false; + _ -> + Is_server + andalso is_glob_match(UName, MName) + end + end, + Match_list). + +%% Copied from ejabberd-2.0.0/src/acl.erl +is_regexp_match(String, RegExp) -> + case regexp:first_match(String, RegExp) of + nomatch -> + false; + {match, _, _} -> + true; + {error, ErrDesc} -> + io:format( + "Wrong regexp ~p in ACL: ~p", + [RegExp, lists:flatten(regexp:format_error(ErrDesc))]), + false + end. +is_glob_match(String, Glob) -> + is_regexp_match(String, regexp:sh_to_awk(Glob)). + + +%%----------------------------- +%% Push Roster from file +%%----------------------------- + pushroster(File, User, Server) -> {ok, [Roster]} = file:consult(File), subscribe_roster({User, Server, "", User}, Roster). @@ -632,161 +859,28 @@ histogram([], _Integral, _Current, Count, Hist) -> lists:reverse(Hist) end. - -format_print_room(Host1, Rooms)-> - lists:foreach( - fun({_, {Roomname, Host},_}) -> - case Host1 of - all -> - io:format("~s ~s ~n", [Roomname, Host]); - Host -> - io:format("~s ~s ~n", [Roomname, Host]); - _ -> - ok - end - end, - Rooms). - - -%%---------------------------- -%% Purge MUC -%%---------------------------- - -%% Required for muc_purge -%% Copied from mod_muc/mod_muc_room.erl --define(DICT, dict). --record(muc_online_room, {name_host, pid}). --record(lqueue, {queue, len, max}). --record(config, {title = "", - allow_change_subj = true, - allow_query_users = true, - allow_private_messages = true, - public = true, - public_list = true, - persistent = false, - moderated = false, % TODO - members_by_default = true, - members_only = false, - allow_user_invites = false, - password_protected = false, - password = "", - anonymous = true, - logging = false - }). --record(state, {room, - host, - server_host, - access, - jid, - config = #config{}, - users, - affiliations, - history, - subject = "", - subject_author = "", - just_created = false}). - -muc_purge(Days) -> - ServerHost = global, - Host = global, - muc_purge2(ServerHost, Host, Days). - -muc_purge(ServerHost, Days) -> - Host = find_host(ServerHost), - muc_purge2(ServerHost, Host, Days). - -muc_purge2(ServerHost, Host, Last_allowed) -> - Room_names = get_room_names(Host), - - Decide = fun(N) -> decide(N, Last_allowed) end, - Rooms_to_delete = lists:filter(Decide, Room_names), - - Num_rooms = length(Room_names), - Num_rooms_to_delete = length(Rooms_to_delete), - - Rooms_to_delete_full = fill_serverhost(Rooms_to_delete, ServerHost), - - Delete = fun({N, H, SH}) -> - mod_muc:room_destroyed(H, N, SH), - mod_muc:forget_room(H, N) - end, - lists:foreach(Delete, Rooms_to_delete_full), - {purged, Num_rooms, Num_rooms_to_delete, Rooms_to_delete}. - -fill_serverhost(Rooms_to_delete, global) -> - ServerHosts1 = ?MYHOSTS, - ServerHosts2 = [ {ServerHost, find_host(ServerHost)} || ServerHost <- ServerHosts1], - [ {Name, Host, find_serverhost(Host, ServerHosts2)} || {Name, Host} <- Rooms_to_delete]; -fill_serverhost(Rooms_to_delete, ServerHost) -> - [ {Name, Host, ServerHost}|| {Name, Host} <- Rooms_to_delete]. - -find_serverhost(Host, ServerHosts) -> - {value, {ServerHost, Host}} = lists:keysearch(Host, 2, ServerHosts), - ServerHost. - -find_host(ServerHost) -> - gen_mod:get_module_opt(ServerHost, mod_muc, host, "conference." ++ ServerHost). - -decide({Room_name, Host}, Last_allowed) -> - Room_pid = get_room_pid(Room_name, Host), - - C = get_room_config(Room_pid), - Persistent = C#config.persistent, - - S = get_room_state(Room_pid), - Just_created = S#state.just_created, - - Room_users = S#state.users, - Num_users = length(?DICT:to_list(Room_users)), - - History = (S#state.history)#lqueue.queue, - Ts_now = calendar:now_to_universal_time(now()), - Ts_uptime = element(1, erlang:statistics(wall_clock))/1000, - {Have_history, Last} = case queue:is_empty(History) of - true -> - {false, Ts_uptime}; - false -> - Last_message = queue:last(History), - {_, _, _, Ts_last, _} = Last_message, - Ts_diff = - calendar:datetime_to_gregorian_seconds(Ts_now) - - calendar:datetime_to_gregorian_seconds(Ts_last), - {true, Ts_diff} - end, - - case {Persistent, Just_created, Num_users, Have_history, seconds_to_days(Last)} of - {true, false, 0, _, Last_days} - when Last_days > Last_allowed -> - true; - _ -> - false - end. - -seconds_to_days(S) -> - round(S) div 60*60*24. - -uptime_seconds() -> - trunc(element(1, erlang:statistics(wall_clock))/1000). - -get_room_names(Host) -> - Get_room_names = fun(Room_reg, Names) -> - case {Host, Room_reg#muc_online_room.name_host} of - {Host, {Name1, Host}} -> - [{Name1, Host} | Names]; - {global, {Name1, Host1}} -> - [{Name1, Host1} | Names]; - _ -> - Names - end - end, - ets:foldr(Get_room_names, [], muc_online_room). - -get_room_pid(Name, Host) -> - [Room_ets] = ets:lookup(muc_online_room, {Name, Host}), - Room_ets#muc_online_room.pid. - -get_room_config(Room_pid) -> - gen_fsm:sync_send_all_state_event(Room_pid, get_config). - -get_room_state(Room_pid) -> - gen_fsm:sync_send_all_state_event(Room_pid, get_state). +group_parameters(Ps, [Char]) -> + {none, Grouped_Ps} = lists:foldl( + fun(P, {State, Res}) -> + case State of + none -> + case P of + [Char | PTail]-> + {building, [PTail | Res]}; + _ -> + {none, [P | Res]} + end; + building -> + [ResHead | ResTail] = Res, + case lists:last(P) of + Char -> + P2 = lists:sublist(P, length(P)-1), + {none, [ResHead ++ " " ++ P2 | ResTail]}; + _ -> + {building, [ResHead ++ " " ++ P | ResTail]} + end + end + end, + {none, []}, + Ps), + lists:reverse(Grouped_Ps). diff --git a/sources b/sources index 0dcfbd1..e51d308 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -1c1fb0b0287ba4547b1683f7c94b63bc ejabberd-2.0.2-beta1.tar.gz +eef7ab12fc1e3ed34669bb2d5e1d0448 ejabberd-2.0.2.tar.gz