--- ejabberd_ctl.erl.orig	2005-05-23 21:51:32.000000000 +0200
+++ ejabberd_ctl.erl	2005-05-28 19:01:33.000000000 +0200
@@ -9,7 +9,7 @@
 -module(ejabberd_ctl).
 -author('alexey@sevcom.net').
 
--export([start/0, dump_to_textfile/1]).
+-export([start/0, dump_to_textfile/1, add_rosteritem/7, pushroster/2, pushroster_all/1, push_alltoall/2]).
 
 -define(STATUS_SUCCESS, 0).
 -define(STATUS_ERROR,   1).
@@ -228,6 +228,84 @@
 	_ ->
 	    ?STATUS_SUCCESS
     end;
+ 
+process(Node, ["delete-older-messages", Days]) ->
+    case rpc:call(Node, mod_offline, remove_old_messages, [list_to_integer(Days)]) of
+	{badrpc, Reason} ->
+	    io:format("Can't delete old messages at node ~p: ~p~n",
+		      [Node, Reason]),
+	    ?STATUS_BADRPC;
+	_ ->
+	    ?STATUS_SUCCESS
+    end;
+
+process(Node, ["import", Path]) ->
+    case rpc:call(Node, jd2ejd, import_dir, [Path]) of
+	    {atomic, ok} ->
+            ?STATUS_SUCCESS;
+        _ ->
+            Reason = "unknown reason",
+            io:format("Can't store backup in ~p on node ~p: ~p~n",
+                [Path, Node, Reason]),
+            ?STATUS_ERROR
+    end;
+
+process(Node, ["add-rosteritem", Localuser, User, Server, Nick, Group, Subs]) ->
+    case rpc:call(Node, ejabberd_ctl, add_rosteritem, 
+                  [Localuser, User, Server, Nick, Group, list_to_atom(Subs), []]) of
+	{atomic, ok} ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't add roster item to user ~p on node ~p: ~p~n",
+		      [Localuser, Node, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't add roster item to user ~p on node ~p: ~p~n",
+		      [Localuser, Node, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+process(Node, ["pushroster", Localuser, File]) ->
+    case rpc:call(Node, ejabberd_ctl, pushroster, [Localuser, File]) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push roster ~p on node ~p: ~p~n",
+		      [File, Node, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push roster ~p on node ~p: ~p~n",
+		      [File, Node, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+process(Node, ["pushroster-all", File]) ->
+    case rpc:call(Node, ejabberd_ctl, pushroster_all, [File]) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push roster ~p on node ~p: ~p~n",
+		      [File, Node, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push roster ~p on node ~p: ~p~n",
+		      [File, Node, Reason]),
+	    ?STATUS_BADRPC
+    end;
+
+process(Node, ["push-alltoall", Server, Group]) ->
+    case rpc:call(Node, ejabberd_ctl, push_alltoall, [Server, Group]) of
+	ok ->
+	    ?STATUS_SUCCESS;
+	{error, Reason} ->
+	    io:format("Can't push all to all on node ~p: ~p~n",
+		      [Node, Reason]),
+	    ?STATUS_ERROR;
+	{badrpc, Reason} ->
+	    io:format("Can't push all to all on node ~p: ~p~n",
+		      [Node, Reason]),
+	    ?STATUS_BADRPC
+    end;
 
 process(_Node, _Args) ->
     print_usage(),
@@ -240,12 +318,14 @@
       "Usage: ejabberdctl node command~n"
       "~n"
       "Available commands:~n"
+      "~n"
+      " - SERVER:~n"
       "  status\t\t\tget ejabberd status~n"
       "  stop\t\t\t\tstop ejabberd~n"
       "  restart\t\t\trestart ejabberd~n"
       "  reopen-log\t\t\treopen log file~n"
-      "  register user server password\tregister a user~n"
-      "  unregister user server\tunregister a user~n"
+      "~n"
+      " - MAINTANCE:~n"
       "  backup file\t\t\tstore a database backup to file~n"
       "  restore file\t\t\trestore a database backup from file~n"
       "  install-fallback file\t\tinstall a database fallback from file~n"
@@ -253,8 +333,25 @@
       "  load file\t\t\trestore a database from a text file~n"
       "  import-file file\t\timport user data from jabberd 1.4 spool file~n"
       "  import-dir dir\t\timport user data from jabberd 1.4 spool directory~n"
-      "  registered-users\t\tlist all registered users~n"
       "  delete-expired-messages\tdelete expired offline messages from database~n"
+      "  delete-older-messages days\tdelete offline messages older than 'days'~n"      
+      "~n"
+      " - USERS:~n"
+      "  register user server password\tregister a user~n"
+      "  unregister user server\tunregister a user~n"
+      "  registered-users\t\tlist all registered users~n"
+      "  set-password user password\tset passowrd to user~n"
+      "~n"
+      " - ROSTERS:~n"
+      "  add-rosteritem localuser user server nick group subs~n"
+      "       Adds user@server to localuser~n"
+      "       subs= none, from, to or both~n"
+      "       example: add-roster peter mike server.com MiKe Employees both~n"
+      "       will add mike@server.com to peter roster~n"
+      "  pushroster localuser file\tpush template roster in file to localuser~n"
+      "  pushroster-all file\t\tpush template roster in file to all those users~n"
+      "  push-alltoall server group\t\tadds all the users to all the users in Group~n"
+
       "~n"
       "Example:~n"
       "  ejabberdctl ejabberd@host restart~n"
@@ -294,3 +391,84 @@
     lists:foreach(
       fun(Term) -> io:format(F,"~p.~n", [setelement(1, Term, T)]) end, All).
 
+add_rosteritem(LU, User, Server, Nick, Group, Subscription, Xattrs) ->
+  Host = Server,
+  subscribe(LU, User, Server, Nick, Group, Subscription, Xattrs),
+  % TODO: if the server is not local and Subs=to or both: send subscription request
+  add_rosteritem2(User, LU, Server, LU, "", invert_subs(Subscription), Xattrs, Host).
+
+add_rosteritem2(LU, User, Server, Nick, Group, Subscription, Xattrs, Server) ->
+  subscribe(LU, User, Server, Nick, Group, Subscription, Xattrs).
+
+invert_subs(none) -> none;
+invert_subs(to) -> none;
+invert_subs(from) -> to;
+invert_subs(both) -> both.
+
+subscribe(LU, User, Server, Nick, Group, Subscription, Xattrs) ->
+	LS = Server, % the LocalServer is the same as the remote Server
+    mnesia:transaction(
+        fun() ->
+            mnesia:write({
+                roster,
+                {LU,LS,{User,Server,[]}}, % usj
+                {LU,LS},                 % us
+                {User,Server,[]},      % jid
+                Nick,                  % name: "Mom", []
+                Subscription,  % subscription: none, to=you see him, from=he sees you, both
+                none,          % ask: out=send request, in=somebody requests you, none
+                [Group],       % groups: ["Family"]
+                Xattrs,        % xattrs: [{"category","conference"}]
+                []             % xs: []
+            })
+    end).
+
+%% Pushroster
+pushroster(Localuser, File) ->
+  {ok, [{Serv, Users}]} = file:consult(File),
+  Users2 = put_nick(Users),
+  subscribe_roster(Serv, {Localuser, "gr", "ni"}, Users2).
+
+%% Pushroster_all
+pushroster_all(File) ->
+  {ok, [{Serv, Users}]} = file:consult(File),
+  Users2 = put_nick(Users),
+  subscribe_all(Serv, Users2).
+
+put_nick(Users) -> put_nick(Users, []).
+put_nick([{N, G, Nick}| Users], R) -> put_nick(Users, [{N, G, Nick}| R]);
+put_nick([{N, G}| Users], R) -> put_nick(Users, [{N, G, N}| R]);
+put_nick([], R) -> R.
+
+subscribe_all(Serv, Users2) ->
+  subscribe_all(Serv, Users2, Users2).
+
+subscribe_all(_, [], _) ->
+  ok;
+
+subscribe_all(Serv, [User1 | Users], Roster) ->
+  subscribe_roster(Serv, User1, Roster),
+  subscribe_all(Serv, Users, Roster).
+
+subscribe_roster(_, _, []) ->
+  ok;
+
+% Do not subscribe a user to itself
+subscribe_roster(Serv, {Name, Group, Nick}, [{Name, _, _} | Roster]) ->
+  subscribe_roster(Serv, {Name, Group, Nick}, Roster);
+
+% Subscribe Name2 to Name1
+subscribe_roster(Serv, {Name1, Group1, Nick1}, [{Name2, Group2, Nick2} | Roster]) ->
+  subscribe(Name1, Name2, Serv, Nick2, Group2, both, []),
+  subscribe_roster(Serv, {Name1, Group1, Nick1}, Roster).
+
+push_alltoall(S, G) ->
+  Users = ejabberd_auth:get_vh_registered_users(S),
+  Users2 = build_list_users(G, Users, []),
+  subscribe_all(S, Users2).
+
+build_list_users(_G, [], Res) ->
+  Res;
+
+build_list_users(G, [{User, _Server}|Users], Res) ->
+  build_list_users(G, Users, [{User, G, User}|Res]).
