25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-28 16:34:13 +01:00

Allow to get subscriptions on a given node (EJAB-712), and presence based fixes

SVN Revision: 2066
This commit is contained in:
Christophe Romain 2009-05-07 23:35:59 +00:00
parent 18af5f8675
commit 90263cf0e7
2 changed files with 118 additions and 49 deletions

View File

@ -1,3 +1,11 @@
2009-05-08 Christophe Romain <christophe.romain@process-one.net>
* src/mod_pubsub/mod_pubsub.erl: Allow to get subscriptions on a given
node (EJAB-712); Improve XML schema validation on publish item;
Remove subscriptions of users whose authorization has been removed on
nodes with presence access model; Do not allow unsubscribed user to
subscribe node with presence access model.
2009-05-08 Mickael Remond <mremond@process-one.net> 2009-05-08 Mickael Remond <mremond@process-one.net>
* src/ejabberd_app.erl: Better support for profiling. * src/ejabberd_app.erl: Better support for profiling.

View File

@ -54,6 +54,7 @@
%% exports for hooks %% exports for hooks
-export([presence_probe/3, -export([presence_probe/3,
in_subscription/6,
out_subscription/4, out_subscription/4,
remove_user/2, remove_user/2,
disco_local_identity/5, disco_local_identity/5,
@ -177,6 +178,7 @@ init([ServerHost, Opts]) ->
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50), ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
@ -632,7 +634,13 @@ out_subscription(User, Server, JID, subscribed) ->
end, end,
Proc = gen_mod:get_module_proc(Server, ?PROCNAME), Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
gen_server:cast(Proc, {presence, PUser, PServer, PResources, Owner}); gen_server:cast(Proc, {presence, PUser, PServer, PResources, Owner});
out_subscription(_, _, _, _) -> out_subscription(_,_,_,_) ->
ok.
in_subscription(_, User, Server, Subscriber, unsubscribed, _) ->
Owner = jlib:make_jid(User, Server, ""),
Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
gen_server:cast(Proc, {unsubscribe, Subscriber, Owner});
in_subscription(_, _, _, _, _, _) ->
ok. ok.
%% ------- %% -------
@ -703,6 +711,33 @@ handle_cast({remove_user, LUser, LServer}, State) ->
delete_node(Host, ["home", LServer, LUser], Owner), delete_node(Host, ["home", LServer, LUser], Owner),
{noreply, State}; {noreply, State};
handle_cast({unsubscribe, Subscriber, Owner}, State) ->
Host = State#state.host,
BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
lists:foreach(fun(PType) ->
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Subscriber]),
lists:foreach(fun
({Node, subscribed, JID}) ->
Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) ->
case get_option(Options, access_model) of
presence ->
case lists:member(BJID, Owners) of
true ->
node_call(Type, unsubscribe_node, [NodeId, Subscriber, JID, all]);
false ->
ok
end;
_ ->
ok
end
end,
transaction(Host, Node, Action, sync_dirty);
(_) ->
ok
end, Subscriptions)
end, State#state.plugins),
{noreply, State};
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
@ -753,6 +788,7 @@ terminate(_Reason, #state{host = Host,
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75), ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75), ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50), ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50), ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50), ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
@ -1121,7 +1157,7 @@ iq_pubsub(Host, ServerHost, From, IQType, SubEl, _Lang, Access, Plugins) ->
end, [], xml:remove_cdata(Els)), end, [], xml:remove_cdata(Els)),
get_items(Host, Node, From, SubId, MaxItems, ItemIDs); get_items(Host, Node, From, SubId, MaxItems, ItemIDs);
{get, "subscriptions"} -> {get, "subscriptions"} ->
get_subscriptions(Host, From, Plugins); get_subscriptions(Host, Node, From, Plugins);
{get, "affiliations"} -> {get, "affiliations"} ->
get_affiliations(Host, From, Plugins); get_affiliations(Host, From, Plugins);
{get, "options"} -> {get, "options"} ->
@ -1542,7 +1578,7 @@ subscribe_node(Host, Node, From, JID) ->
J -> jlib:jid_tolower(J) J -> jlib:jid_tolower(J)
end, end,
SubId = uniqid(), SubId = uniqid(),
Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) -> Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) ->
Features = features(Type), Features = features(Type),
SubscribeFeature = lists:member("subscribe", Features), SubscribeFeature = lists:member("subscribe", Features),
SubscribeConfig = get_option(Options, subscribe), SubscribeConfig = get_option(Options, subscribe),
@ -1556,9 +1592,12 @@ subscribe_node(Host, Node, From, JID) ->
Subscriber, AllowedGroups); Subscriber, AllowedGroups);
_ -> _ ->
case Subscriber of case Subscriber of
{"", "", ""} -> {false, false}; {"", "", ""} ->
{U, S, _} -> get_roster_info(U, S, Subscriber, {false, false};
AllowedGroups) _ ->
{OU, OS, _} = Owner,
get_roster_info(OU, OS,
Subscriber, AllowedGroups)
end end
end, end,
if if
@ -1685,10 +1724,10 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
PayloadSize > PayloadMaxSize -> PayloadSize > PayloadMaxSize ->
%% Entity attempts to publish very large payload %% Entity attempts to publish very large payload
{error, extended_error(?ERR_NOT_ACCEPTABLE, "payload-too-big")}; {error, extended_error(?ERR_NOT_ACCEPTABLE, "payload-too-big")};
PayloadCount == 0 -> (PayloadCount == 0) and (Payload == []) ->
%% Publisher attempts to publish to payload node with no payload %% Publisher attempts to publish to payload node with no payload
{error, extended_error(?ERR_BAD_REQUEST, "payload-required")}; {error, extended_error(?ERR_BAD_REQUEST, "payload-required")};
PayloadCount > 1 -> (PayloadCount > 1) or (PayloadCount == 0) ->
%% Entity attempts to publish item with multiple payload elements %% Entity attempts to publish item with multiple payload elements
{error, extended_error(?ERR_BAD_REQUEST, "invalid-payload")}; {error, extended_error(?ERR_BAD_REQUEST, "invalid-payload")};
(DeliverPayloads == 0) and (PersistItems == 0) and (PayloadSize > 0) -> (DeliverPayloads == 0) and (PersistItems == 0) and (PayloadSize > 0) ->
@ -2009,12 +2048,12 @@ get_affiliations(Host, Node, JID) ->
Action = fun(#pubsub_node{type = Type, id = NodeId}) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
Features = features(Type), Features = features(Type),
RetrieveFeature = lists:member("modify-affiliations", Features), RetrieveFeature = lists:member("modify-affiliations", Features),
Affiliation = node_call(Type, get_affiliation, [NodeId, JID]), {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
if if
not RetrieveFeature -> not RetrieveFeature ->
%% Service does not support modify affiliations %% Service does not support modify affiliations
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "modify-affiliations")}; {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "modify-affiliations")};
Affiliation /= {result, owner} -> Affiliation /= owner ->
%% Entity is not an owner %% Entity is not an owner
{error, ?ERR_FORBIDDEN}; {error, ?ERR_FORBIDDEN};
true -> true ->
@ -2105,14 +2144,15 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
end. end.
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response} %% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
%% Host = host() %% Host = host()
%% Node = pubsubNode()
%% JID = jid() %% JID = jid()
%% Plugins = [Plugin::string()] %% Plugins = [Plugin::string()]
%% Reason = stanzaError() %% Reason = stanzaError()
%% Response = [pubsubIQResponse()] %% Response = [pubsubIQResponse()]
%% @doc <p>Return the list of subscriptions as an XMPP response.</p> %% @doc <p>Return the list of subscriptions as an XMPP response.</p>
get_subscriptions(Host, JID, Plugins) when is_list(Plugins) -> get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
Result = lists:foldl( Result = lists:foldl(
fun(Type, {Status, Acc}) -> fun(Type, {Status, Acc}) ->
Features = features(Type), Features = features(Type),
@ -2130,36 +2170,57 @@ get_subscriptions(Host, JID, Plugins) when is_list(Plugins) ->
case Result of case Result of
{ok, Subscriptions} -> {ok, Subscriptions} ->
Entities = lists:flatmap( Entities = lists:flatmap(
fun({_, none}) -> []; fun({_, none}) ->
({Node, Subscription}) -> [];
({SubsNode, Subscription}) ->
case Node of
[] ->
[{xmlelement, "subscription", [{xmlelement, "subscription",
[{"node", node_to_string(Node)}, [{"node", node_to_string(SubsNode)},
{"subscription", subscription_to_string(Subscription)}], {"subscription", subscription_to_string(Subscription)}],
[]}]; []}];
({_, none, _}) -> []; SubsNode ->
({Node, Subscription, SubJID}) ->
[{xmlelement, "subscription", [{xmlelement, "subscription",
[{"node", node_to_string(Node)}, [{"subscription", subscription_to_string(Subscription)}],
[]}];
_ ->
[]
end;
({_, none, _}) ->
[];
({SubsNode, Subscription, SubJID}) ->
case Node of
[] ->
[{xmlelement, "subscription",
[{"node", node_to_string(SubsNode)},
{"jid", jlib:jid_to_string(SubJID)}, {"jid", jlib:jid_to_string(SubJID)},
{"subscription", subscription_to_string(Subscription)}], {"subscription", subscription_to_string(Subscription)}],
[]}] []}];
SubsNode ->
[{xmlelement, "subscription",
[{"jid", jlib:jid_to_string(SubJID)},
{"subscription", subscription_to_string(Subscription)}],
[]}];
_ ->
[]
end
end, lists:usort(lists:flatten(Subscriptions))), end, lists:usort(lists:flatten(Subscriptions))),
{result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}], {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "subscriptions", [], [{xmlelement, "subscriptions", [],
Entities}]}]}; Entities}]}]};
{Error, _} -> {Error, _} ->
Error Error
end; end.
get_subscriptions(Host, Node, JID) -> get_subscriptions(Host, Node, JID) ->
Action = fun(#pubsub_node{type = Type, id = NodeId}) -> Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
Features = features(Type), Features = features(Type),
RetrieveFeature = lists:member("manage-subscriptions", Features), RetrieveFeature = lists:member("manage-subscriptions", Features),
Affiliation = node_call(Type, get_affiliation, [NodeId, JID]), {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
if if
not RetrieveFeature -> not RetrieveFeature ->
%% Service does not support manage subscriptions %% Service does not support manage subscriptions
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-subscriptions")}; {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-subscriptions")};
Affiliation /= {result, owner} -> Affiliation /= owner ->
%% Entity is not an owner %% Entity is not an owner
{error, ?ERR_FORBIDDEN}; {error, ?ERR_FORBIDDEN};
true -> true ->