]> granicus.if.org Git - ejabberd/commitdiff
Allow to get subscriptions on a given node (EJAB-712), and presence based fixes
authorChristophe Romain <christophe.romain@process-one.net>
Thu, 7 May 2009 23:35:59 +0000 (23:35 +0000)
committerChristophe Romain <christophe.romain@process-one.net>
Thu, 7 May 2009 23:35:59 +0000 (23:35 +0000)
SVN Revision: 2066

ChangeLog
src/mod_pubsub/mod_pubsub.erl

index 723c20281e4084bdffce6fc1e9c0e8ea7bec2aaa..d5565b2d50dbad71a16757a1216547aeb62c64c3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
 
        * src/ejabberd_app.erl: Better support for profiling.
index c90190859b139b70609c6d79aadc5d92b464c15e..5a4981da58bbfabb433b8d2c84dde456c5dc76b3 100644 (file)
@@ -54,6 +54,7 @@
 
 %% exports for hooks
 -export([presence_probe/3,
+        in_subscription/6,
         out_subscription/4,
         remove_user/2,
         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_items, ServerHost, ?MODULE, disco_sm_items, 75),
     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(remove_user, 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,
     Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
     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.
 
 %% -------
@@ -703,6 +711,33 @@ handle_cast({remove_user, LUser, LServer}, State) ->
     delete_node(Host, ["home", LServer, LUser], Owner),
     {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) ->
     {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_items, ServerHost, ?MODULE, disco_sm_items, 75),
     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(remove_user, 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)),
                    get_items(Host, Node, From, SubId, MaxItems, ItemIDs);
                {get, "subscriptions"} ->
-                   get_subscriptions(Host, From, Plugins);
+                   get_subscriptions(Host, Node, From, Plugins);
                {get, "affiliations"} ->
                    get_affiliations(Host, From, Plugins);
                {get, "options"} ->
@@ -1542,39 +1578,42 @@ subscribe_node(Host, Node, From, JID) ->
                     J -> jlib:jid_tolower(J)
                 end,
     SubId = uniqid(),
-    Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    SubscribeFeature = lists:member("subscribe", Features),
-                    SubscribeConfig = get_option(Options, subscribe),
-                    AccessModel = get_option(Options, access_model),
-                    SendLast = get_option(Options, send_last_published_item),
-                    AllowedGroups = get_option(Options, roster_groups_allowed, []),
-                    {PresenceSubscription, RosterGroup} =
-                        case Host of
-                            {OUser, OServer, _} ->
-                                get_roster_info(OUser, OServer,
-                                                Subscriber, AllowedGroups);
-                            _ ->
-                                case Subscriber of
-                                    {"", "", ""} -> {false, false};
-                                    {U, S, _} -> get_roster_info(U, S, Subscriber,
-                                                                 AllowedGroups)
-                                end
-                        end,
-                    if
-                        not SubscribeFeature ->
-                            %% Node does not support subscriptions
-                            {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
-                        not SubscribeConfig ->
-                            %% Node does not support subscriptions
-                            {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
-                        true ->
-                            node_call(Type, subscribe_node,
-                                      [NodeId, From, Subscriber,
+    Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) ->
+                   Features = features(Type),
+                   SubscribeFeature = lists:member("subscribe", Features),
+                   SubscribeConfig = get_option(Options, subscribe),
+                   AccessModel = get_option(Options, access_model),
+                   SendLast = get_option(Options, send_last_published_item),
+                   AllowedGroups = get_option(Options, roster_groups_allowed, []),
+                   {PresenceSubscription, RosterGroup} =
+                       case Host of
+                           {OUser, OServer, _} ->
+                               get_roster_info(OUser, OServer,
+                                               Subscriber, AllowedGroups);
+                           _ ->
+                               case Subscriber of
+                                   {"", "", ""} ->
+                                       {false, false};
+                                   _ ->
+                                       {OU, OS, _} = Owner,
+                                       get_roster_info(OU, OS,
+                                                       Subscriber, AllowedGroups)
+                               end
+                       end,
+                   if
+                       not SubscribeFeature ->
+                           %% Node does not support subscriptions
+                           {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
+                       not SubscribeConfig ->
+                           %% Node does not support subscriptions
+                           {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
+                       true ->
+                           node_call(Type, subscribe_node,
+                                       [NodeId, From, Subscriber,
                                        AccessModel, SendLast,
                                        PresenceSubscription, RosterGroup])
-                    end
-            end,
+                   end
+           end,
     Reply = fun(Subscription) ->
                    %% TODO, this is subscription-notification, should depends on node features
                    Fields =
@@ -1685,10 +1724,10 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
                        PayloadSize > PayloadMaxSize ->
                            %% Entity attempts to publish very large payload
                            {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
                            {error, extended_error(?ERR_BAD_REQUEST, "payload-required")};
-                       PayloadCount > 1 ->
+                       (PayloadCount > 1) or (PayloadCount == 0) ->
                            %% Entity attempts to publish item with multiple payload elements
                            {error, extended_error(?ERR_BAD_REQUEST, "invalid-payload")};
                        (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}) ->
                     Features = features(Type),
                     RetrieveFeature = lists:member("modify-affiliations", Features),
-                    Affiliation = node_call(Type, get_affiliation, [NodeId, JID]),
+                    {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
                     if
                         not RetrieveFeature ->
                             %% Service does not support modify affiliations
                             {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "modify-affiliations")};
-                        Affiliation /= {result, owner} ->
+                        Affiliation /= owner ->
                             %% Entity is not an owner
                             {error, ?ERR_FORBIDDEN};
                         true ->
@@ -2105,14 +2144,15 @@ set_affiliations(Host, Node, From, EntitiesEls) ->
     end.
 
 
-%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
+%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
 %%      Host = host()
+%%      Node = pubsubNode()
 %%      JID = jid()
 %%      Plugins = [Plugin::string()]
 %%      Reason = stanzaError()
 %%      Response = [pubsubIQResponse()]
 %% @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(
               fun(Type, {Status, Acc}) ->
                       Features = features(Type),
@@ -2130,36 +2170,57 @@ get_subscriptions(Host, JID, Plugins) when is_list(Plugins) ->
     case Result of
        {ok, Subscriptions} ->
            Entities = lists:flatmap(
-                        fun({_, none}) -> [];
-                           ({Node, Subscription}) ->
+                        fun({_, none}) ->
+                               [];
+                           ({SubsNode, Subscription}) ->
+                               case Node of
+                               [] ->
                                 [{xmlelement, "subscription",
-                                  [{"node", node_to_string(Node)},
+                                  [{"node", node_to_string(SubsNode)},
                                    {"subscription", subscription_to_string(Subscription)}],
                                   []}];
-                           ({_, none, _}) -> [];
-                           ({Node, Subscription, SubJID}) ->
+                               SubsNode ->
                                 [{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)},
                                    {"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))),
            {result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
                       [{xmlelement, "subscriptions", [],
                         Entities}]}]};
        {Error, _} ->
            Error
-    end;
+    end.
 get_subscriptions(Host, Node, JID) ->
     Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
                     Features = features(Type),
                     RetrieveFeature = lists:member("manage-subscriptions", Features),
-                    Affiliation = node_call(Type, get_affiliation, [NodeId, JID]),
+                    {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
                     if
                         not RetrieveFeature ->
                             %% Service does not support manage subscriptions
                             {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-subscriptions")};
-                        Affiliation /= {result, owner} ->
+                        Affiliation /= owner ->
                             %% Entity is not an owner
                             {error, ?ERR_FORBIDDEN};
                         true ->