From 4c383c765ddd877defbb74cc123d104393081fd6 Mon Sep 17 00:00:00 2001 From: Jeffrey C. Ollie Date: Jul 31 2007 12:50:20 +0000 Subject: Update to fix a number of bugs. --- diff --git a/ejabberd.init b/ejabberd.init index d07c0ab..2714538 100644 --- a/ejabberd.init +++ b/ejabberd.init @@ -1,12 +1,19 @@ #!/bin/bash # -# ejabberd Starts, Stops and Reloads ejabberd. -# +# ejabberd Start and stop ejabberd. + # chkconfig: - 40 60 # description: ejabberd # processname: ejabberd # pidfile: /var/run/ejabberd.pid +### BEGIN INIT INFO +# Required-Start: network +# Required-Stop: network +# Short-Description: Start and stop ejabberd +# Description: A distributed, fault-tolerant Jabber/XMPP server +### END INIT INFO + . /etc/rc.d/init.d/functions node=`hostname | cut -d. -f1` @@ -32,7 +39,7 @@ start() { stop() { # Stop daemons. echo -n "Shutting down ejabberd: " - runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@$node stop" && success || failure + /sbin/runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@$node stop" && success || failure RETVAL=$? [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ejabberd echo @@ -41,6 +48,7 @@ stop() { restart() { stop + sleep 5 start } @@ -55,14 +63,20 @@ case "$1" in restart) restart ;; + force-reload) + restart + ;; condrestart) [ -f /var/lock/subsys/ejabberd ] && restart || : ;; + try-restart) + [ -f /var/lock/subsys/ejabberd ] && restart || : + ;; status) - runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@$node status" + /sbin/runuser -s /bin/bash - ejabberd -c "erl -pa @libdir@/ejabberd-@version@/ebin -noinput -sname ejabberdctl -s ejabberd_ctl -extra ejabberd@$node status" ;; *) - echo "Usage: ejabberd {start|stop|restart|reload|condrestart|status}" + echo "Usage: ejabberd {start|stop|restart|force-reload|condrestart|try-restart|status}" exit 1 esac diff --git a/ejabberd.spec b/ejabberd.spec index 2d35e82..1d47d90 100644 --- a/ejabberd.spec +++ b/ejabberd.spec @@ -1,6 +1,6 @@ Name: ejabberd Version: 1.1.3 -Release: 1%{?dist} +Release: 3%{?dist} Summary: A distributed, fault-tolerant Jabber/XMPP server Group: Applications/Internet @@ -12,13 +12,19 @@ Source2: ejabberd.logrotate Source3: inetrc # http://ejabberd.jabber.ru/ejabberdctl-extra -Source4: http://ejabberd.jabber.ru/files/efiles/mod_ctlextra.erl +Source4: https://svn.process-one.net/ejabberd-modules/mod_ctlextra/trunk/src/mod_ctlextra.erl # The following were extracted from a patch found on http://realloc.spb.ru/share/ejabberdad.html Source5: ejabberd_auth_ad.erl Source6: mod_shared_roster_ad.erl Source7: mod_vcard_ad.erl +Source8: ejabberdctl +Source9: ejabberdctl.pam +Source10: ejabberdctl.apps + +Patch1: ejabberd-1.1.3-ldapfix.patch + BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: expat-devel @@ -27,6 +33,7 @@ BuildRequires: erlang BuildRequires: hevea Requires: erlang +Requires: usermode Requires(pre): fedora-usermgmt Requires(post): /sbin/chkconfig @@ -69,6 +76,8 @@ fi %prep %setup -q +%patch1 -p1 + %{__perl} -pi -e "s!/var/lib/ejabberd!%{_libdir}/ejabberd-%{version}!g" src/Makefile.in %{__perl} -pi -e "s!/etc!%{_sysconfdir}!g" src/Makefile.in %{__perl} -pi -e "s!\@prefix\@!!g" src/Makefile.in @@ -101,15 +110,21 @@ chmod a+x %{buildroot}%{_libdir}/ejabberd-%{version}/priv/lib/*.so mkdir -p %{buildroot}/var/log/ejabberd mkdir -p %{buildroot}/var/lib/ejabberd/spool +mkdir -p %{buildroot}%{_bindir} +ln -s consolehelper %{buildroot}%{_bindir}/ejabberdctl +install -D -p -m 0755 %{S:8} %{buildroot}%{_sbindir}/ejabberdctl +install -D -p -m 0644 %{S:9} %{buildroot}%{_sysconfdir}/pam.d/ejabberdctl +install -D -p -m 0644 %{S:10} %{buildroot}%{_sysconfdir}/security/console.apps/ejabberdctl + mkdir -p %{buildroot}%{_initrddir} -cp %{S:1} %{buildroot}%{_initrddir}/ejabberd +install -p -m 0755 %{S:1} %{buildroot}%{_initrddir}/ejabberd chmod a+x %{buildroot}%{_initrddir}/ejabberd mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d -cp %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd +cp -p %{S:2} %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd -%{__perl} -pi -e 's!\@libdir\@!%{_libdir}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd -%{__perl} -pi -e 's!\@version\@!%{version}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd +%{__perl} -pi -e 's!\@libdir\@!%{_libdir}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd %{buildroot}%{_sbindir}/ejabberdctl +%{__perl} -pi -e 's!\@version\@!%{version}!g' %{buildroot}%{_initrddir}/ejabberd %{buildroot}%{_sysconfdir}/logrotate.d/ejabberd %{buildroot}%{_sbindir}/ejabberdctl cp %{S:3} %{buildroot}%{_sysconfdir}/ejabberd/inetrc @@ -127,6 +142,11 @@ rm -rf %{buildroot} %{_initrddir}/ejabberd %config(noreplace) %{_sysconfdir}/logrotate.d/ejabberd +%config(noreplace) %{_sysconfdir}/pam.d/ejabberdctl +%config(noreplace) %{_sysconfdir}/security/console.apps/ejabberdctl +%{_bindir}/ejabberdctl +%{_sbindir}/ejabberdctl + %dir %{_libdir}/ejabberd-%{version} %dir %{_libdir}/ejabberd-%{version}/ebin %{_libdir}/ejabberd-%{version}/ebin/*.app @@ -150,6 +170,16 @@ rm -rf %{buildroot} %doc ChangeLog COPYING TODO doc/*.pdf doc/*.html doc/*.png doc/release_notes_* %changelog +* Thu Jul 26 2007 Jeffrey C. Ollie - 1.1.3-3 +- Add ejabberdctl (#199873) +- Add patch to fix LDAP authentication. (#248268) +- Add a sleep in init script between stop/start when restarting. +- LSB compliance cleanups for init script. (#246917) +- Don't mention "reload" in the init script usage string. (#227254) + +* Tue Jul 24 2007 Jeffrey C. Ollie - 1.1.3-2 +- Update mod_ctlextra + * Fri Feb 2 2007 Jeffrey C. Ollie - 1.1.3-1 - Update to 1.1.3 diff --git a/mod_ctlextra.erl b/mod_ctlextra.erl index dfff6ce..32bc6cb 100644 --- a/mod_ctlextra.erl +++ b/mod_ctlextra.erl @@ -1,31 +1,14 @@ %%%---------------------------------------------------------------------- %%% File : mod_ctlextra.erl -%%% Author : +%%% Author : Badlop %%% Purpose : Adds more options for ejabberd_ctl %%% Created : -%%% Id : +%%% Id : $Id: mod_ctlextra.erl 148 2007-06-19 12:31:44Z badlop $ %%%---------------------------------------------------------------------- -%%% mod_ctlextra v0.2.2 for ejabberd 1.1.1 (18/May/2006) -%%% -%%% INSTALL: -%%% 1. Copy mod_ctlextra.erl to your ejabberd/src -%%% 2. Add to your ejabberd.cfg, on the modules section: -%%% {mod_ctlextra, []}, -%%% 3. Recompile and restart ejabberd -%%% -%%% USAGE: -%%% - Now you have several new options for ejabberd-ctl -%%% - Example for vcard: ejabberdctl eja@host vcard-get joe myjab.net email -%%% - The file used by 'pushroster' and 'pushroster-all' must be placed on -%%% the same directory where the .beam files are. Example content: -%%% [{"bob", "myserver", "workers", "Bob"}, -%%% {"mart", "myserver", "workers", "Mart"}, -%%% {"Rich", "myserver", "bosses", "Rich"}]. - -module(mod_ctlextra). -author(''). --vsn('Version 0.2.1'). +-vsn('$Revision: 148 $'). -behaviour(gen_mod). @@ -36,9 +19,12 @@ ctl_process/3 ]). +-include("ejabberd.hrl"). -include("ejabberd_ctl.hrl"). -include("jlib.hrl"). +-record(session, {sid, usr, us, priority}). % copied from ejabberd_sm.erl + start(Host, _Opts) -> ejabberd_ctl:register_commands([ {"compile file", "recompile and reload file"}, @@ -50,7 +36,7 @@ start(Host, _Opts) -> {"set-password user server password", "set password to user@server"}, % ejd2odbc - {"export2odbc server output", "export all possible tables on server to output"}, + {"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'"}, @@ -72,7 +58,8 @@ start(Host, _Opts) -> % mod_muc % muc-add room opts % muc-del room - % muc-del-older 90 : delete rooms older than X days (with no activity (chat, presence, logins) in 6 months) + {"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"}, @@ -83,15 +70,25 @@ start(Host, _Opts) -> {"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"}, % 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), @@ -115,14 +112,21 @@ ctl_process(_Val, ["delete-older-users", Days]) -> io:format("Deleted ~p users: ~p~n", [N, UR]), ?STATUS_SUCCESS; -ctl_process(_Val, ["export2odbc", Server, Output1]) -> - Output = list_to_atom(Output1), - ejd2odbc:export_passwd(Server, Output), - ejd2odbc:export_roster(Server, Output), - ejd2odbc:export_offline(Server, Output), - ejd2odbc:export_last(Server, Output), - ejd2odbc:export_vcard(Server, Output), - ejd2odbc:export_vcard_search(Server, Output), +ctl_process(_Val, ["export2odbc", Server, Output]) -> + Tables = [ + {export_last, last}, + {export_offline, offline}, + {export_passwd, passwd}, + {export_private_storage, private_storage}, + {export_roster, roster}, + {export_vcard, vcard}, + {export_vcard_search, vcard_search}], + 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) + end, + lists:foreach(Export, Tables), ?STATUS_SUCCESS; ctl_process(_Val, ["set-password", User, Server, Password]) -> @@ -174,6 +178,15 @@ 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]), + ?STATUS_SUCCESS; + +ctl_process(_Val, ["muc-online-rooms"]) -> + format_print_room(all, ets:tab2list(muc_online_room)), + ?STATUS_SUCCESS; + ctl_process(_Val, ["add-rosteritem", LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, Subs]) -> case add_rosteritem(LocalUser, LocalServer, RemoteUser, RemoteServer, Nick, Group, list_to_atom(Subs), []) of {atomic, ok} -> @@ -252,6 +265,16 @@ ctl_process(_Val, ["stats", Stat]) -> io:format("~p~n", [Res]), ?STATUS_SUCCESS; +ctl_process(_Val, ["status-num", Status_required]) -> + ctl_process(_Val, "all", ["status-num", Status_required]); + +ctl_process(_Val, ["status-list", Status_required]) -> + ctl_process(_Val, "all", ["status-list", Status_required]); + +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("", "", ""), @@ -263,6 +286,16 @@ 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; + ctl_process(_Val, Host, ["num-active-users", Days]) -> Number = num_active_users(Host, list_to_integer(Days)), io:format("~p~n", [Number]), @@ -276,6 +309,16 @@ ctl_process(_Val, Host, ["stats", Stat]) -> io:format("~p~n", [Res]), ?STATUS_SUCCESS; +ctl_process(_Val, Host, ["status-num", Status_required]) -> + Num = length(get_status_list(Host, Status_required)), + io:format("~p~n", [Num]), + ?STATUS_SUCCESS; + +ctl_process(_Val, Host, ["status-list", Status_required]) -> + Res = get_status_list(Host, 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, _Args) -> Val. @@ -284,6 +327,38 @@ ctl_process(Val, _Host, _Args) -> %% UTILS %%------------- +get_status_list(Host, Status_required) -> + % Get list of all logged users + Sessions = ejabberd_sm:dirty_get_my_sessions_list(), + % Reformat the list + Sessions2 = [ {Session#session.usr, Session#session.sid, Session#session.priority} || Session <- Sessions], + Fhost = case Host of + "all" -> + % All hosts are requested, so don't filter at all + fun(_, _) -> true end; + _ -> + % Filter the list, only Host is interesting + fun(A, B) -> A == B end + end, + Sessions3 = [ {Pid, Server, Priority} || {{_User, Server, _Resource}, {_, Pid}, Priority} <- Sessions2, apply(Fhost, [Server, Host])], + % For each Pid, get its presence + Sessions4 = [ {ejabberd_c2s:get_presence(Pid), Server, Priority} || {Pid, Server, Priority} <- Sessions3], + % Filter by status + Fstatus = case Status_required of + "all" -> + fun(_, _) -> true end; + _ -> + fun(A, B) -> A == B end + end, + [{User, Server, Resource, Priority, stringize(Status_text)} + || {{User, Resource, Status, Status_text}, Server, Priority} <- Sessions4, + apply(Fstatus, [Status, Status_required])]. + +% Make string more print-friendly +stringize(String) -> + % Replace newline characters with other code + element(2, regexp:gsub(String, "\n", "\\n")). + add_rosteritem(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs) -> subscribe(LU, LS, RU, RS, Nick, Group, Subscription, Xattrs), % TODO: if the server is not local and Subs=to or both: send subscription request @@ -376,11 +451,14 @@ vcard_set(User, Server, Data, Content) -> vcard_set2(User, Server, R, Data) -> % Get old vcard - [{_, _, A1}] = mnesia:dirty_read(vcard, {User, Server}), - {_, _, _, A2} = A1, - - A3 = lists:keydelete(Data, 2, A2), - A4 = [R | A3], + A4 = case mnesia:dirty_read(vcard, {User, Server}) of + [] -> + [R]; + [{_, _, A1}] -> + {_, _, _, A2} = A1, + A3 = lists:keydelete(Data, 2, A2), + [R | A3] + end, % Build new vcard SubEl = {xmlelement, "vCard", [{"xmlns","vcard-temp"}], A4}, @@ -500,3 +578,159 @@ histogram([], _Integral, _Current, Count, Hist) -> true -> 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. + +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).