diff --git a/src/mod_register.erl b/src/mod_register.erl
index fc5dbc1..a1f9c0e 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -192,6 +192,8 @@ try_register(User, Server, Password) ->
 		allow ->
 		    case ejabberd_auth:try_register(User, Server, Password) of
 			{atomic, ok} ->
+			    ejabberd_hooks:run(user_registered, Server,
+					       [User, Server]),
 			    send_welcome_message(JID),
 			    send_registration_notifications(JID),
 			    ok;
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index 89c4777..2034df2 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -37,13 +37,16 @@
 	 process_item/2,
 	 in_subscription/6,
 	 out_subscription/4,
+	 user_registered/2,
+	 user_available/1,
+	 unset_presence/4,
 	 list_groups/1,
 	 create_group/2,
 	 create_group/3,
 	 delete_group/2,
 	 get_group_opts/2,
 	 set_group_opts/3,
-	 get_group_users/2,
+	 get_group_users/3,
 	 get_group_explicit_users/2,
 	 add_user_to_group/3,
 	 remove_user_from_group/3]).
@@ -81,7 +84,13 @@ start(Host, _Opts) ->
     ejabberd_hooks:add(roster_get_jid_info, Host,
         	       ?MODULE, get_jid_info, 70),
     ejabberd_hooks:add(roster_process_item, Host,
-        	       ?MODULE, process_item, 50).
+        	       ?MODULE, process_item, 50),
+    ejabberd_hooks:add(user_registered, Host,
+         	       ?MODULE, user_registered, 50),
+    ejabberd_hooks:add(user_available_hook, Host,
+         	       ?MODULE, user_available, 50),
+    ejabberd_hooks:add(unset_presence_hook, Host,
+         	       ?MODULE, unset_presence, 50).
 %%ejabberd_hooks:add(remove_user, Host,
 %%    	       ?MODULE, remove_user, 50),
 
@@ -101,7 +110,13 @@ stop(Host) ->
     ejabberd_hooks:delete(roster_get_jid_info, Host,
         		  ?MODULE, get_jid_info, 70),
     ejabberd_hooks:delete(roster_process_item, Host,
-			  ?MODULE, process_item, 50).
+			  ?MODULE, process_item, 50),
+    ejabberd_hooks:delete(user_registered, Host,
+			  ?MODULE, user_registered, 50),
+    ejabberd_hooks:delete(user_available_hook, Host,
+			  ?MODULE, user_available, 50),
+    ejabberd_hooks:delete(unset_presence_hook, Host,
+			  ?MODULE, unset_presence, 50).
 %%ejabberd_hooks:delete(remove_user, Host,
 %%    		  ?MODULE, remove_user, 50),
 
@@ -120,7 +135,7 @@ get_user_roster(Items, US) ->
 						   get_group_name(S, Group),
 						   Acc2)
 			    end
-		    end, Acc1, get_group_users(S, Group))
+		    end, Acc1, get_group_users(U, S, Group))
 	  end, dict:new(), DisplayedGroups),
 
     %% If partially subscribed users are also in shared roster, show them as
@@ -181,7 +196,7 @@ get_subscription_lists({F, T}, User, Server) ->
 	lists:usort(
 	  lists:flatmap(
 	    fun(Group) ->
-		    get_group_users(LServer, Group)
+		    get_group_users(LUser, LServer, Group)
 	    end, DisplayedGroups)),
     SRJIDs = [{U1, S1, ""} || {U1, S1} <- SRUsers],
     {lists:usort(SRJIDs ++ F), lists:usort(SRJIDs ++ T)}.
@@ -200,7 +215,7 @@ get_jid_info({Subscription, Groups}, User, Server, JID) ->
 		    fun(User1, Acc2) ->
 			    dict:append(
 			      User1, get_group_name(LServer, Group), Acc2)
-		    end, Acc1, get_group_users(LServer, Group))
+		    end, Acc1, get_group_users(LUser, LServer, Group))
 	  end, dict:new(), DisplayedGroups),
     case dict:find(US1, SRUsers) of
 	{ok, GroupNames} ->
@@ -230,7 +245,7 @@ process_subscription(Direction, User, Server, JID, _Type, Acc) ->
 	lists:usort(
 	  lists:flatmap(
 	    fun(Group) ->
-		    get_group_users(LServer, Group)
+		    get_group_users(LUser, LServer, Group)
 	    end, DisplayedGroups)),
     case lists:member(US1, SRUsers) of
 	true ->
@@ -252,6 +267,13 @@ list_groups(Host) ->
 	[{'==', '$2', Host}],
 	['$1']}]).
 
+groups_with_opts(Host) ->
+    lists:map(fun([G,O]) -> {G, O} end,
+	mnesia:dirty_select(sr_group,
+	    [{#sr_group{group_host={'$1', Host}, opts='$2', _='_'},
+	    [],
+	    [['$1','$2']] }])).
+
 create_group(Host, Group) ->
     create_group(Host, Group, []).
 
@@ -297,7 +319,7 @@ get_user_groups(US) ->
 	    [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
 	_ ->
 	    []
-    end ++ get_all_users_groups(Host).
+    end ++ get_special_users_groups(Host).
 
 is_group_enabled(Host, Group) ->
     case catch mnesia:dirty_read(sr_group, {Group, Host}) of
@@ -320,13 +342,103 @@ get_group_opt(Host, Group, Opt, Default) ->
 	    false
     end.
 
-get_group_users(Host, Group) ->
+-record(last_activity, {us, timestamp, status}).
+-record(session, {sid, usr, us, priority, info}).
+
+get_recent_users(Host, Days) ->
+    LServer = jlib:nameprep(Host),
+    %% Convert older time
+    SecOlder = Days*24*60*60,
+
+    %% Get current time
+    {MegaSecs, Secs, _MicroSecs} = now(),
+    TimeStampOlder = MegaSecs * 1000000 + Secs - SecOlder,
+
+    %% Get the list of recently connected users
+    RecentUsers = mnesia:dirty_select(last_activity, 
+        [{#last_activity{us={'$1',LServer},timestamp='$2',_='_'},
+          [{'>','$2',TimeStampOlder}],
+          ['$1'] }]),
+    %% Get the list of connected users
+    OnlineUsers = mnesia:dirty_select(session, 
+        [{#session{us={'$1',LServer}, _='_'}, 
+         [], 
+         ['$1']}]),
+    
+    Users = lists:usort(RecentUsers++OnlineUsers),
+    lists:map(fun(User) -> {User, LServer} end, Users).
+
+get_online_users(Host) ->
+    lists:usort([{U, S} || {U, S, _} <- ejabberd_sm:get_vh_session_list(Host)]).
+
+get_nearby_users(User, Host) ->
+    Resources = ejabberd_sm:get_user_resources(User, Host),
+    Sessions = ejabberd_sm:get_vh_session_list(Host),
+    Nearby = lists:foldl(
+      fun(Resource, Acc1) ->
+        lists:foldl(
+          fun({U, S, R}, Acc2) ->
+            if
+              R == Resource -> {U, S} ++ Acc2;
+              true -> Acc2
+            end
+          end, [], Sessions) ++ Acc1
+      end, [], Resources),
+    lists:usort(Nearby).
+
+get_group_users(User, Host, Group) ->
     case get_group_opt(Host, Group, all_users, false) of
 	true ->
 	    ejabberd_auth:get_vh_registered_users(Host);
 	false ->
 	    []
-    end ++ get_group_explicit_users(Host, Group).
+    end ++
+    case get_group_opt(Host, Group, recent_users_days, 0) of
+	0 ->
+	    [];
+	Days when is_integer(Days) ->
+            get_recent_users(Host, Days)
+    end ++
+    case get_group_opt(Host, Group, online_users, false) of
+	true ->
+	    get_online_users(Host);
+	false ->
+	    []
+    end ++
+    case get_group_opt(Host, Group, nearby_users, false) of
+	true ->
+	    get_nearby_users(User, Host);
+	false ->
+	    []
+    end ++
+    get_group_explicit_users(Host, Group).
+    
+get_group_users(User, Host, Group, GroupOpts) ->
+    case proplists:get_value(all_users, GroupOpts, false) of
+	true ->
+	    ejabberd_auth:get_vh_registered_users(Host);
+	false ->
+	    []
+    end ++
+    case proplists:get_value(recent_users_days, GroupOpts, 0) of
+	0 ->
+	    [];
+	Days when is_integer(Days) ->
+            get_recent_users(Host, Days)
+    end ++
+    case proplists:get_value(online_users, GroupOpts, false) of
+	true ->
+	    get_online_users(Host);
+	false ->
+	    []
+    end ++
+    case proplists:get_value(nearby_users, GroupOpts, false) of
+	true ->
+	    get_nearby_users(User, Host);
+	false ->
+	    []
+    end ++
+    get_group_explicit_users(Host, Group).
 
 get_group_explicit_users(Host, Group) ->
     case catch mnesia:dirty_index_read(
@@ -340,11 +452,43 @@ get_group_explicit_users(Host, Group) ->
 get_group_name(Host, Group) ->
     get_group_opt(Host, Group, name, Group).
 
-get_all_users_groups(Host) ->
+get_special_users_groups(Host) ->
     lists:filter(
-      fun(Group) -> get_group_opt(Host, Group, all_users, false) end,
+      fun(Group) ->
+	get_group_opt(Host, Group, all_users, false) orelse
+	get_group_opt(Host, Group, recent_users_days, 0) > 0 orelse
+	get_group_opt(Host, Group, online_users, false) orelse
+	get_group_opt(Host, Group, nearby_users, false) end,
       list_groups(Host)).
 
+displayed_groups(GroupsOpts, SelectedGroupsOpts) ->
+    DisplayedGroups = lists:usort(
+	lists:flatmap(
+	    fun({_Group, Opts}) ->
+		[G || G <- proplists:get_value(displayed_groups, Opts, []), not lists:member(disabled, Opts)]
+	    end, SelectedGroupsOpts)),
+    [G || G <- DisplayedGroups, not lists:member(disabled, proplists:get_value(G, GroupsOpts, []))].
+
+get_special_displayed_groups(GroupsOpts) ->
+    Groups = lists:filter(
+	fun({_Group, Opts}) ->
+	    proplists:get_value(all_users, Opts, false) orelse
+	    proplists:get_value(recent_users_days, Opts, 0) > 0 orelse
+	    proplists:get_value(online_users, Opts, false) orelse
+	    proplists:get_value(nearby_users, Opts, false)
+	end, GroupsOpts),
+    displayed_groups(GroupsOpts, Groups).
+
+get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
+    Groups = case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of
+	Rs when is_list(Rs) ->
+	    [{Group, proplists:get_value(Group, GroupsOpts, [])} ||
+		    #sr_user{group_host = {Group, H}} <- Rs, H == LServer];
+	_ ->
+	    []
+    end,
+    displayed_groups(GroupsOpts, Groups).
+
 get_user_displayed_groups(US) ->
     Host = element(2, US),
     DisplayedGroups1 =
@@ -382,6 +526,191 @@ remove_user_from_group(Host, US, Group) ->
 	end,
     mnesia:transaction(F).
 
+user_registered(User, Server) ->
+    LUser = jlib:nodeprep(User),
+    LServer = jlib:nameprep(Server),
+    GroupsOpts = groups_with_opts(LServer),
+    SpecialGroups = get_special_displayed_groups(GroupsOpts),
+    UserGroups = get_user_displayed_groups(LUser, LServer, GroupsOpts),
+    lists:foreach(
+      fun(Group) ->
+	    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
+	    GroupName = proplists:get_value(name, GroupOpts, Group),
+	    lists:foreach(
+	    fun({U, S}) ->
+		    Item = #roster{usj = {U, S, {LUser, LServer, ""}},
+				    us = {U, S},
+				    jid = {LUser, LServer, ""},
+				    name = "",
+				    subscription = both,
+				    ask = none,
+				    groups = [GroupName]},
+		    push_item(U, S, jlib:make_jid("", S, ""), Item)
+	    end, get_group_users(LUser, LServer, Group, GroupOpts))
+      end, lists:usort(SpecialGroups++UserGroups)).
+
+%% get a roster item for a contact from a particular user's
+%% perspective, considering both normal and shared roster items
+%% FIXME: is there a more efficient way to do this?
+get_user_roster_item(FromUS, ToUS) ->
+    {FUser, FServer} = FromUS,
+    case catch ejabberd_hooks:run_fold(roster_get, FServer, [], [ToUS]) of
+      Items when is_list(Items) ->
+	case [I || I <- Items, I#roster.jid == {FUser, FServer, []}] of
+	  [Item | _ ] ->
+	    Item;
+	  [] ->
+	    false
+	end;
+      _ ->
+	error
+    end.
+
+user_available(New) ->
+    LUser = New#jid.luser,
+    LServer = New#jid.lserver,
+    Resources = ejabberd_sm:get_user_resources(LUser, LServer),
+    ?INFO_MSG("user_available for ~p @ ~p (~p resources)",
+        [LUser, LServer, length(Resources)]),
+    case length(Resources) of
+      %% first session for this user
+      1 ->
+	%% find all online users on the new user's server, but not the new user himself
+	OnlineUsers = lists:usort(
+	    [U || {U, _, _} <- ejabberd_sm:get_vh_session_list(LServer), U /= LUser]),
+	GroupsOpts = groups_with_opts(LServer),
+	SpecialGroups = get_special_displayed_groups(GroupsOpts),
+	%% for each of these people
+	lists:foreach(
+	  fun(OnlineUser) ->
+	    %% see if they are displaying any @online@ groups
+	    UserGroups = get_user_displayed_groups(OnlineUser, LServer, GroupsOpts),
+	    OnlineGroups = lists:filter(
+		fun(Group) ->
+		    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
+		    proplists:get_value(online_users, GroupOpts, false) orelse
+		    proplists:get_value(nearby_users, GroupOpts, false)
+		end, lists:usort(UserGroups++SpecialGroups)),
+	    case OnlineGroups of
+	    [] ->
+		ok;
+	    _ ->
+		%% if so, push them an item for the new user
+		Item = case get_user_roster_item({LUser, LServer}, {OnlineUser, LServer}) of
+		I when is_record(I, roster) -> I;
+		_ -> none
+		end,
+		push_item(OnlineUser, LServer, jlib:make_jid("", LServer, ""), Item)
+	    end
+	  end, OnlineUsers);
+      _ ->
+	ok
+    end.
+
+unset_presence(LUser, LServer, Resource, Status) ->
+    Resources = ejabberd_sm:get_user_resources(LUser, LServer),
+    ?INFO_MSG("unset_presence for ~p @ ~p / ~p -> ~p (~p resources)",
+        [LUser, LServer, Resource, Status, length(Resources)]),
+    %% if user has no resources left...
+    case length(Resources) of
+      0 ->
+	%% find all users on their server
+	OnlineUsers = lists:usort(
+	    [U || {U, _, _} <- ejabberd_sm:get_vh_session_list(LServer)]),
+	GroupsOpts = groups_with_opts(LServer),
+	SpecialGroups = get_special_displayed_groups(GroupsOpts),
+	%% for each of these people
+	lists:foreach(
+	  fun(OnlineUser) ->
+	    %% see if they are displaying any @online@ groups
+	    UserGroups = get_user_displayed_groups(OnlineUser, LServer, GroupsOpts),
+	    OnlineGroups = lists:filter(
+		fun(Group) ->
+		    GroupOpts = proplists:get_value(Group, GroupsOpts, []),
+		    proplists:get_value(online_users, GroupOpts, false) orelse
+		    proplists:get_value(nearby_users, GroupOpts, false)
+		end, lists:usort(UserGroups++SpecialGroups)),
+	    case OnlineGroups of
+	    [] ->
+		ok;
+	    _ ->
+		%% push them their item for the departed user, or remove it
+		Item = case get_user_roster_item({LUser, LServer}, {OnlineUser, LServer}) of
+		I when is_record(I, roster) -> I;
+		_ -> #roster{usj = {OnlineUser, LServer, {LUser, LServer, ""}},
+				us = {OnlineUser, LServer},
+				jid = {LUser, LServer, ""},
+				name = "",
+				subscription = remove,
+				ask = none,
+				groups = []}
+		end,
+		push_item(OnlineUser, LServer, jlib:make_jid("", LServer, ""), Item)
+	    end
+	  end, OnlineUsers);
+      _ ->
+	ok
+    end.
+
+push_item(_User, _Server, _From, none) -> 
+    ok;
+push_item(User, Server, From, Item) ->
+    % was ejabberd_sm:route(jlib:make_jid("", "", ""), jlib:make_jid(User, Server, ""), why ?
+    ejabberd_sm:route(From, jlib:make_jid(User, Server, ""),
+		      {xmlelement, "broadcast", [],
+		       [{item,
+			 Item#roster.jid,
+			 Item#roster.subscription}]}),
+    Stanza = jlib:iq_to_xml(
+	    #iq{type = set, xmlns = ?NS_ROSTER,
+		id = "push",
+		sub_el = [{xmlelement, "query",
+			   [{"xmlns", ?NS_ROSTER}],
+			   [item_to_xml(Item)]}]}),
+    lists:foreach(
+	fun(Resource) ->
+	    JID = jlib:make_jid(User, Server, Resource),
+	    ejabberd_router:route(JID, JID, Stanza)
+	end, ejabberd_sm:get_user_resources(User, Server)).
+
+item_to_xml(Item) ->
+    Attrs1 = [{"jid", jlib:jid_to_string(Item#roster.jid)}],
+    Attrs2 = case Item#roster.name of
+		 "" ->
+		     Attrs1;
+		 Name ->
+		     [{"name", Name} | Attrs1]
+	     end,
+    Attrs3 = case Item#roster.subscription of
+		 none ->
+		     [{"subscription", "none"} | Attrs2];
+		 from ->
+		     [{"subscription", "from"} | Attrs2];
+		 to ->
+		     [{"subscription", "to"} | Attrs2];
+		 both ->
+		     [{"subscription", "both"} | Attrs2];
+		 remove ->
+		     [{"subscription", "remove"} | Attrs2]
+	     end,
+    Attrs4 = case ask_to_pending(Item#roster.ask) of
+		 out ->
+		     [{"ask", "subscribe"} | Attrs3];
+		 both ->
+		     [{"ask", "subscribe"} | Attrs3];
+		 _ ->
+		     Attrs3
+	     end,
+    SubEls1 = lists:map(fun(G) ->
+				{xmlelement, "group", [], [{xmlcdata, G}]}
+			end, Item#roster.groups),
+    SubEls = SubEls1 ++ Item#roster.xs,
+    {xmlelement, "item", Attrs4, SubEls}.
+
+ask_to_pending(subscribe) -> out;
+ask_to_pending(unsubscribe) -> none;
+ask_to_pending(Ask) -> Ask.
+
 
 %%---------------------
 %% Web Admin
@@ -485,6 +814,9 @@ shared_roster_group(Host, Group, Query, Lang) ->
     Name = get_opt(GroupOpts, name, ""),
     Description = get_opt(GroupOpts, description, ""),
     AllUsers = get_opt(GroupOpts, all_users, false),
+    RecentUsersDays = get_opt(GroupOpts, recent_users_days, 0),
+    OnlineUsers = get_opt(GroupOpts, online_users, false),
+    NearbyUsers = get_opt(GroupOpts, nearby_users, false),
     %%Disabled = false,
     DisplayedGroups = get_opt(GroupOpts, displayed_groups, []),
     Members = mod_shared_roster:get_group_explicit_users(Host, Group),
@@ -494,7 +826,26 @@ shared_roster_group(Host, Group, Query, Lang) ->
 		"@all@\n";
 	    true ->
 		[]
-	end ++ [[us_to_list(Member), $\n] || Member <- Members],
+	end ++
+	if
+	    RecentUsersDays > 0 ->
+		"@recent@\n";
+	    true ->
+		[]
+	end ++
+	if
+	    OnlineUsers ->
+		"@online@\n";
+	    true ->
+		[]
+	end ++
+	if
+	    NearbyUsers ->
+		"@nearby@\n";
+	    true ->
+		[]
+	end ++
+	[[us_to_list(Member), $\n] || Member <- Members],
     FDisplayedGroups = [[DG, $\n] || DG <- DisplayedGroups],
     FGroup =
 	?XAE("table", [],
@@ -577,6 +928,12 @@ shared_roster_group_parse_query(Host, Group, Query) ->
 			  case SJID of
 			      "@all@" ->
 				  USs;
+			      "@recent@" ->
+				  USs;
+			      "@online@" ->
+				  USs;
+			      "@nearby@" ->
+				  USs;
 			      _ ->
 				  case jlib:string_to_jid(SJID) of
 				      JID when is_record(JID, jid) ->
@@ -591,10 +948,27 @@ shared_roster_group_parse_query(Host, Group, Query) ->
 		    true -> [{all_users, true}];
 		    false -> []
 		end,
+	    %% FIXME: this should be specified by the user
+	    RecentUsersOpt =
+		case lists:member("@recent@", SJIDs) of
+		    true -> [{recent_users_days, 7}];
+		    false -> []
+		end,
+	    OnlineUsersOpt =
+		case lists:member("@online@", SJIDs) of
+		    true -> [{online_users, true}];
+		    false -> []
+		end,
+	    NearbyUsersOpt =
+		case lists:member("@nearby@", SJIDs) of
+		    true -> [{nearby_users, true}];
+		    false -> []
+		end,
 
 	    mod_shared_roster:set_group_opts(
 	      Host, Group,
-	      NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt),
+	      NameOpt ++ DispGroupsOpt ++ DescriptionOpt ++ AllUsersOpt
+		++ RecentUsersOpt ++ OnlineUsersOpt ++ NearbyUsersOpt),
 
 	    if
 		NewMembers == error -> error;
