diff --git a/ChangeLog b/ChangeLog index b03b74aee..429f7151a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-01-08 Christophe Romain + + * src/mod_pubsub/mod_pubsub.erl: completely support subscription using + full JID (EJAB-701) + * src/mod_pubsub/node_default.erl: Likewise + + * src/mod_pubsub/node_default.erl: any entity can retrieve item when + node access model is "open" (thanks to Myers Carpenter)(EJAB-836) + 2009-01-08 Pablo Polvorin * src/mod_vcard_ldap.erl, src/mod_muc/mod_muc.erl, src/mod_roster.hrl, src/mod_offline_odbc.erl, src/ejabberd_s2s_in.erl, src/adhoc.erl, diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index 4b388d72c..65e483d34 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -465,12 +465,12 @@ handle_cast({presence, JID, Pid}, State) -> lists:foreach(fun(Type) -> {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, JID]), lists:foreach( - fun({Node, subscribed}) -> + fun({Node, subscribed, SubJID}) -> case tree_action(Host, get_node, [Host, Node, JID]) of #pubsub_node{options = Options} -> case get_option(Options, send_last_published_item) of on_sub_and_presence -> - send_last_item(Host, Node, LJID); + send_last_item(Host, Node, SubJID); _ -> ok end; @@ -522,13 +522,11 @@ handle_cast({presence, JID, Pid}, State) -> handle_cast({remove_user, LUser, LServer}, State) -> Host = State#state.host, Owner = exmpp_jid:make_bare_jid(LUser, LServer), - OwnerKey = jlib:short_prepd_bare_jid(Owner), %% remove user's subscriptions lists:foreach(fun(Type) -> {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, Owner]), lists:foreach(fun - ({Node, subscribed}) -> - JID = exmpp_jid:jid_to_list(LUser, LServer), + ({Node, subscribed, JID}) -> unsubscribe_node(Host, Node, Owner, JID, all); (_) -> ok @@ -537,7 +535,7 @@ handle_cast({remove_user, LUser, LServer}, State) -> %% remove user's PEP nodes lists:foreach(fun(#pubsub_node{nodeid={NodeKey, NodeName}}) -> delete_node(NodeKey, NodeName, Owner) - end, tree_action(Host, get_nodes, [OwnerKey])), + end, tree_action(Host, get_nodes, [jlib:short_prepd_bare_jid(Owner)])), %% remove user's nodes delete_node(Host, ["home", LServer, LUser], Owner), {noreply, State}; @@ -1446,13 +1444,14 @@ subscribe_node(Host, Node, From, JID) -> %%
  • The node does not exist.
  • %%
  • The request specifies a subscription ID that is not valid or current.
  • %% -unsubscribe_node(Host, Node, From, JID, SubId) -> - Subscriber = try - jlib:short_prepd_jid(exmpp_jid:list_to_jid(JID)) +unsubscribe_node(Host, Node, From, JID, SubId) when is_list(JID) -> + Subscriber = try jlib:short_prepd_jid(exmpp_jid:list_to_jid(JID)) catch _:_ -> {undefined, undefined, undefined} end, + unsubscribe_node(Host, Node, From, Subscriber, SubId); +unsubscribe_node(Host, Node, From, Subscriber, SubId) -> case node_action(Host, Node, unsubscribe_node, [Host, Node, From, Subscriber, SubId]) of {error, Error} -> @@ -1930,7 +1929,8 @@ get_subscriptions(Host, JID, Plugins) when is_list(Plugins) -> %% Service does not support retreive subscriptions {{error, extended_error('feature-not-implemented', unsupported, "retrieve-subscriptions")}, Acc}; true -> - {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, JID]), + Subscriber = jlib:jid_remove_resource(JID), + {result, Subscriptions} = node_action(Type, get_entity_subscriptions, [Host, Subscriber]), {Status, [Subscriptions|Acc]} end end, {ok, []}, Plugins), @@ -2296,7 +2296,7 @@ broadcast_config_notification(Host, Node, Lang) -> broadcast_stanza(Host, NodeOpts, States, Stanza) -> PresenceDelivery = get_option(NodeOpts, presence_based_delivery), - BroadcastAll = get_option(NodeOpts, broadcast_all_resources), + BroadcastAll = get_option(NodeOpts, broadcast_all_resources), %% XXX this is not standard From = service_jid(Host), lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) -> case is_to_deliver(LJID, Subs, PresenceDelivery) of diff --git a/src/mod_pubsub/node_default.erl b/src/mod_pubsub/node_default.erl index 2e3e0ad02..27a9fdd55 100644 --- a/src/mod_pubsub/node_default.erl +++ b/src/mod_pubsub/node_default.erl @@ -279,11 +279,16 @@ delete_node(Host, Removed) -> %%

    In the default plugin module, the record is unchanged.

    subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - SubscriberKey = jlib:short_prepd_bare_jid(Subscriber), - Authorized = (jlib:short_prepd_bare_jid(Sender) == SubscriberKey), - State = get_state(Host, Node, SubscriberKey), - #pubsub_state{affiliation = Affiliation, - subscription = Subscription} = State, + SubKey = jlib:short_prepd_jid(Owner), + GenKey = jlib:short_prepd_bare_jid(SubKey), + Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), + GenState = get_state(Host, Node, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(Host, Node, SubKey) + end, + Affiliation = GenState#pubsub_state.affiliation, + Subscription = SubState#pubsub_state.subscription, Whitelisted = lists:member(Affiliation, [member, publisher, owner]), if not Authorized -> @@ -323,7 +328,7 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel, true -> subscribed end, - set_state(State#pubsub_state{subscription = NewSubscription}), + set_state(SubState#pubsub_state{subscription = NewSubscription}), case NewSubscription of subscribed -> case SendLast of @@ -345,9 +350,16 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel, %% Reason = mod_pubsub:stanzaError() %% @doc

    Unsubscribe the Subscriber from the Node.

    unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> - SubscriberKey = jlib:short_prepd_bare_jid(Subscriber), - Authorized = (jlib:short_prepd_bare_jid(Sender) == SubscriberKey), - State = get_state(Host, Node, SubscriberKey), + SubKey = jlib:short_prepd_jid(Subscriber), + GenKey = jlib:short_prepd_bare_jid(SubKey), + Authorized = (jlib:short_prepd_bare_jid(Sender) == GenKey), + GenState = get_state(Host, Node, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(Host, Node, SubKey) + end, + Affiliation = GenState#pubsub_state.affiliation, + Subscription = SubState#pubsub_state.subscription, if %% Entity did not specify SubID %%SubID == "", ?? -> @@ -356,17 +368,17 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> %%InvalidSubID -> %% {error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")}; %% Requesting entity is not a subscriber - State#pubsub_state.subscription == none -> + Subscription == none -> {error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")}; %% Requesting entity is prohibited from unsubscribing entity - (not Authorized) and (State#pubsub_state.affiliation =/= owner) -> + (not Authorized) and (Affiliation =/= owner) -> {error, 'forbidden'}; %% Was just subscriber, remove the record - State#pubsub_state.affiliation == none -> - mnesia:delete({pubsub_state, State#pubsub_state.stateid}), + Affiliation == none -> + del_state(SubState#pubsub_state.stateid), {result, default}; true -> - set_state(State#pubsub_state{subscription = none}), + set_state(SubState#pubsub_state{subscription = none}), {result, default} end. @@ -410,10 +422,15 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) -> %%

    %%

    In the default plugin module, the record is unchanged.

    publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> - PublisherKey = jlib:short_prepd_bare_jid(Publisher), - State = get_state(Host, Node, PublisherKey), - #pubsub_state{affiliation = Affiliation, - subscription = Subscription} = State, + SubKey = jlib:short_prepd_jid(Publisher), + GenKey = jlib:short_prepd_bare_jid(SubKey), + GenState = get_state(Host, Node, GenKey), + SubState = case SubKey of + GenKey -> GenState; + _ -> get_state(Host, Node, SubKey) + end, + Affiliation = GenState#pubsub_state.affiliation, + Subscription = SubState#pubsub_state.subscription, if not ((PublishModel == open) or ((PublishModel == publishers) @@ -423,7 +440,7 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> %% Entity does not have sufficient privileges to publish to node {error, 'forbidden'}; true -> - PubId = {PublisherKey, now()}, %% TODO, uses {now(),PublisherKey} for sorting (EJAB-824) + PubId = {SubKey, now()}, %% TODO, uses {now(),PublisherKey} for sorting (EJAB-824) %% TODO: check creation, presence, roster (EJAB-663) Item = case get_item(Host, Node, ItemId) of {result, OldItem} -> @@ -431,17 +448,17 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> payload = Payload}; _ -> #pubsub_item{itemid = {ItemId, {Host, Node}}, - creation = PubId, + creation = {GenKey, now()}, modification = PubId, payload = Payload} end, - Items = [ItemId | State#pubsub_state.items--[ItemId]], + Items = [ItemId | GenState#pubsub_state.items--[ItemId]], {result, {NI, OI}} = remove_extra_items( Host, Node, MaxItems, Items), if MaxItems > 0 -> set_item(Item); true -> ok end, - set_state(State#pubsub_state{items = NI}), + set_state(GenState#pubsub_state{items = NI}), {result, {default, broadcast, OI}} end. @@ -482,12 +499,12 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) -> %%

    Default plugin: The user performing the deletion must be the node owner %% or a publisher.

    delete_item(Host, Node, Publisher, ItemId) -> - PublisherKey = jlib:short_prepd_bare_jid(Publisher), - State = get_state(Host, Node, PublisherKey), - #pubsub_state{affiliation = Affiliation, items = Items} = State, + GenKey = jlib:short_prepd_bare_jid(Publisher), + GenState = get_state(Host, Node, GenKey), + #pubsub_state{affiliation = Affiliation, items = Items} = GenState, Allowed = (Affiliation == publisher) orelse (Affiliation == owner) orelse case get_item(Host, Node, ItemId) of - {result, #pubsub_item{creation = {PublisherKey, _}}} -> true; + {result, #pubsub_item{creation = {GenKey, _}}} -> true; _ -> false end, if @@ -499,7 +516,7 @@ delete_item(Host, Node, Publisher, ItemId) -> {result, _} -> del_item(Host, Node, ItemId), NewItems = lists:delete(ItemId, Items), - set_state(State#pubsub_state{items = NewItems}), + set_state(GenState#pubsub_state{items = NewItems}), {result, {default, broadcast}}; _ -> %% Non-existent node or item @@ -514,8 +531,9 @@ delete_item(Host, Node, Publisher, ItemId) -> %% Node = mod_pubsub:pubsubNode() %% Owner = mod_pubsub:jid() purge_node(Host, Node, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - case get_state(Host, Node, OwnerKey) of + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(Host, Node, GenKey), + case GenState of #pubsub_state{items = Items, affiliation = owner} -> lists:foreach(fun(ItemId) -> mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}) @@ -537,9 +555,9 @@ purge_node(Host, Node, Owner) -> %% that will be added to the affiliation stored in the main %% pubsub_state table.

    get_entity_affiliations(Host, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), + GenKey = jlib:short_prepd_bare_jid(Owner), States = mnesia:match_object( - #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}), + #pubsub_state{stateid = {GenKey, {Host, '_'}}, _ = '_'}), Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) -> {N, A} end, @@ -554,14 +572,14 @@ get_node_affiliations(Host, Node) -> {result, lists:map(Tr, States)}. get_affiliation(Host, Node, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - State = get_state(Host, Node, OwnerKey), - {result, State#pubsub_state.affiliation}. + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(Host, Node, GenKey), + {result, GenState#pubsub_state.affiliation}. set_affiliation(Host, Node, Owner, Affiliation) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - State = get_state(Host, Node, OwnerKey), - set_state(State#pubsub_state{affiliation = Affiliation}), + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(Host, Node, GenKey), + set_state(GenState#pubsub_state{affiliation = Affiliation}), ok. %% @spec (Host, Owner) -> [{Node,Subscription}] @@ -576,11 +594,16 @@ set_affiliation(Host, Node, Owner, Affiliation) -> %% that will be added to the affiliation stored in the main %% pubsub_state table.

    get_entity_subscriptions(Host, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - States = mnesia:match_object( - #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}), - Tr = fun(#pubsub_state{stateid = {_, {_, N}}, subscription = S}) -> - {N, S} + States = case jlib:short_prepd_bare_jid(Owner) of + {U, D, ""} -> mnesia:match_object( + #pubsub_state{stateid = {{U, D, '_'}, {Host, '_'}}, _ = '_'}); + {U, D, R} -> mnesia:match_object( + #pubsub_state{stateid = {{U, D, ""}, {Host, '_'}}, _ = '_'}) + ++ mnesia:match_object( + #pubsub_state{stateid = {{U, D, R}, {Host, '_'}}, _ = '_'}) + end, + Tr = fun(#pubsub_state{stateid = {J, {_, N}}, subscription = S}) -> + {N, S, J} end, {result, lists:map(Tr, States)}. @@ -593,14 +616,14 @@ get_node_subscriptions(Host, Node) -> {result, lists:map(Tr, States)}. get_subscription(Host, Node, Owner) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - State = get_state(Host, Node, OwnerKey), - {result, State#pubsub_state.subscription}. + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(Host, Node, GenKey), + {result, GenState#pubsub_state.subscription}. set_subscription(Host, Node, Owner, Subscription) -> - OwnerKey = jlib:short_prepd_bare_jid(Owner), - State = get_state(Host, Node, OwnerKey), - set_state(State#pubsub_state{subscription = Subscription}), + GenKey = jlib:short_prepd_bare_jid(Owner), + GenState = get_state(Host, Node, GenKey), + set_state(GenState#pubsub_state{subscription = Subscription}), ok. %% @spec (Host, Node) -> [States] | [] @@ -668,10 +691,9 @@ get_items(Host, Node, _From) -> #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}), {result, Items}. get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - State = get_state(Host, Node, jlib:short_prepd_bare_jid(JID)), - #pubsub_state{affiliation = Affiliation, - subscription = Subscription} = State, - Subscribed = not ((Subscription == none) or (Subscription == pending)), + GenKey = jlib:short_prepd_bare_jid(JID), + GenState = get_state(Host, Node, GenKey), + Affiliation = GenState#pubsub_state.affiliation, Whitelisted = lists:member(Affiliation, [member, publisher, owner]), if %%SubID == "", ?? -> @@ -683,9 +705,6 @@ get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubI Affiliation == outcast -> %% Requesting entity is blocked {error, 'forbidden'}; - (AccessModel == open) and (not Subscribed) -> - %% Entity is not subscribed - {error, ?ERR_EXTENDED('not-authorized', "not-subscribed")}; (AccessModel == presence) and (not PresenceSubscription) -> %% Entity is not authorized to create a subscription (presence subscription required) {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")}; @@ -719,10 +738,9 @@ get_item(Host, Node, ItemId) -> {error, 'item-not-found'} end. get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - State = get_state(Host, Node, jlib:short_prepd_bare_jid(JID)), - #pubsub_state{affiliation = Affiliation, - subscription = Subscription} = State, - Subscribed = not ((Subscription == none) or (Subscription == pending)), + GenKey = jlib:short_prepd_bare_jid(JID), + GenState = get_state(Host, Node, GenKey), + Affiliation = GenState#pubsub_state.affiliation, Whitelisted = lists:member(Affiliation, [member, publisher, owner]), if %%SubID == "", ?? -> @@ -734,9 +752,6 @@ get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup Affiliation == outcast -> %% Requesting entity is blocked {error, 'forbidden'}; - (AccessModel == open) and (not Subscribed) -> - %% Entity is not subscribed - {error, ?ERR_EXTENDED('not-authorized', "not-subscribed")}; (AccessModel == presence) and (not PresenceSubscription) -> %% Entity is not authorized to create a subscription (presence subscription required) {error, ?ERR_EXTENDED('not-authorized', "presence-subscription-required")};