diff --git a/ChangeLog b/ChangeLog index b23da4198..43948824d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2009-01-03 Christophe Romain + + * src/mod_pubsub/mod_pubsub.erl: deliver notification depending on + presence-based-delivery configuration (EJAB-827). notification code + rewrite. + + * src/mod_pubsub/mod_pubsub.erl: code cleanning, minor bugfixes + * src/mod_pubsub/node_default.erl: Likewise + * src/mod_pubsub/node_pep.erl: Likewise + * src/mod_pubsub/pubsub.hrl: Likewise + + * src/mod_pubsub/mod_pubsub.erl: prevent subscribing with full jid, + waiting for full jid support (EJAB-701) + 2008-12-29 Alexey Shchepin * src/ejabberd_c2s.erl: Bugfix in "from" attribute checking diff --git a/src/mod_pubsub/mod_pubsub.erl b/src/mod_pubsub/mod_pubsub.erl index bd9451b66..42ac390d7 100644 --- a/src/mod_pubsub/mod_pubsub.erl +++ b/src/mod_pubsub/mod_pubsub.erl @@ -30,7 +30,7 @@ %%% %%% @reference See XEP-0060: Pubsub for %%% the latest version of the PubSub specification. -%%% This module uses version 1.11 of the specification as a base. +%%% This module uses version 1.12 of the specification as a base. %%% Most of the specification is implemented. %%% Functions concerning configuration should be rewritten. %%% Code is derivated from the original pubsub v1.7, by Alexey Shchepin @@ -41,7 +41,7 @@ -module(mod_pubsub). -author('christophe.romain@process-one.net'). --version('1.11-01'). +-version('1.12-01'). -behaviour(gen_server). -behaviour(gen_mod). @@ -786,7 +786,7 @@ iq_disco_items(Host, Item, _From) -> Node = string_to_node(SNode), %% Note: Multiple Node Discovery not supported (mask on pubsub#type) %% TODO this code is also back-compatible with pubsub v1.8 (for client issue) - %% TODO make it pubsub v1.10 compliant (this breaks client compatibility) + %% TODO make it pubsub v1.12 compliant (this breaks client compatibility) %% TODO That is, remove name attribute Action = fun(#pubsub_node{type = Type}) -> @@ -1252,7 +1252,6 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) -> %% [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "result"}], %% [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI), %% ?XFIELD("jid-single", "Node Creator", "creator", jlib:jid_to_string(OwnerKey))]}]), - %% todo publish_item(Host, ServerHost, ["pubsub", "nodes"], node_to_string(Node)), case Result of default -> {result, Reply}; _ -> {result, Result} @@ -1298,14 +1297,17 @@ delete_node(Host, Node, Owner) -> {error, Error} -> {error, Error}; {result, {Result, broadcast, Removed}} -> - broadcast_removed_node(Host, Removed), - %%broadcast_retract_item(Host, ["pubsub", "nodes"], node_to_string(Node)), + lists:foreach(fun(RNode) -> + broadcast_removed_node(Host, RNode) + end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, {Result, Removed}} -> - broadcast_removed_node(Host, Removed), + lists:foreach(fun(RNode) -> + broadcast_removed_node(Host, RNode) + end, Removed), case Result of default -> {result, Reply}; _ -> {result, Result} @@ -1514,23 +1516,17 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) -> {error, Reason} -> {error, Reason}; {result, {Result, broadcast, Removed}} -> - lists:foreach(fun(OldItem) -> - broadcast_retract_item(Host, Node, OldItem) - end, Removed), + broadcast_retract_items(Host, Node, Removed), broadcast_publish_item(Host, Node, ItemId, jlib:jid_tolower(Publisher), Payload), case Result of default -> {result, Reply}; _ -> {result, Result} end; {result, default, Removed} -> - lists:foreach(fun(OldItem) -> - broadcast_retract_item(Host, Node, OldItem) - end, Removed), + broadcast_retract_items(Host, Node, Removed), {result, Reply}; {result, Result, Removed} -> - lists:foreach(fun(OldItem) -> - broadcast_retract_item(Host, Node, OldItem) - end, Removed), + broadcast_retract_items(Host, Node, Removed), {result, Result}; {result, default} -> {result, Reply}; @@ -1581,7 +1577,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) -> {error, Reason} -> {error, Reason}; {result, {Result, broadcast}} -> - broadcast_retract_item(Host, Node, ItemId, ForceNotify), + broadcast_retract_items(Host, Node, [ItemId], ForceNotify), case Result of default -> {result, Reply}; _ -> {result, Result} @@ -1749,12 +1745,10 @@ send_items(Host, Node, LJID, Number) -> [First|Tail] = Items, [lists:foldl( fun(CurItem, LastItem) -> - {_, {LMS, LS, LmS}} = LastItem#pubsub_item.creation, - {_, {CMS, CS, CmS}} = CurItem#pubsub_item.creation, - LTimestamp = LMS * 1000000 + LS * 1000 + LmS, - CTimestamp = CMS * 1000000 + CS * 1000 + CmS, + {_, LTimeStamp} = LastItem#pubsub_item.creation, + {_, CTimeStamp} = CurItem#pubsub_item.creation, if - CTimestamp > LTimestamp -> CurItem; + CTimeStamp > LTimeStamp -> CurItem; true -> LastItem end end, First, Tail)]; @@ -1773,10 +1767,9 @@ send_items(Host, Node, LJID, Number) -> end, {xmlelement, "item", ItemAttrs, Payload} end, ToSend), - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", [{"node", node_to_string(Node)}], - ItemsEls}]}]}, + Stanza = event_stanza( + [{xmlelement, "items", [{"node", node_to_string(Node)}], + ItemsEls}]), ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza}. %% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response} @@ -2106,10 +2099,10 @@ service_jid(Host) -> %% Subscription = atom() %% PresenceDelivery = boolean() %% @doc

Check if a notification must be delivered or not -is_to_delivered(_, none, _) -> false; -is_to_delivered(_, pending, _) -> false; -is_to_delivered(_, _, false) -> true; -is_to_delivered({User, Server, _}, _, true) -> +is_to_deliver(_, none, _) -> false; +is_to_deliver(_, pending, _) -> false; +is_to_deliver(_, _, false) -> true; +is_to_deliver({User, Server, _}, _, true) -> case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of [] -> false; Ss -> @@ -2118,224 +2111,172 @@ is_to_delivered({User, Server, _}, _, true) -> end, false, Ss) end. +%% @spec (Els) -> stanza() +%% Els = [xmlelement()] +%% @doc

Build pubsub event stanza +event_stanza(Els) -> + {xmlelement, "message", [], + [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], Els}]}. + %%%%%% broadcast functions broadcast_publish_item(Host, Node, ItemId, _From, Payload) -> Action = fun(#pubsub_node{options = Options, type = Type}) -> - case node_call(Type, get_states, [Host, Node]) of - {error, _} -> {result, false}; - {result, []} -> {result, false}; - {result, States} -> - PresenceDelivery = get_option(Options, presence_based_delivery), - BroadcastAll = get_option(Options, broadcast_all_resources), - Content = case get_option(Options, deliver_payloads) of - true -> Payload; - false -> [] - end, - ItemAttrs = case ItemId of - "" -> []; - _ -> [{"id", ItemId}] - end, - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", [{"node", node_to_string(Node)}], - [{xmlelement, "item", ItemAttrs, Content}]}]}]}, - lists:foreach( - fun(#pubsub_state{stateid = {LJID, _}, - subscription = Subscription}) -> - case is_to_delivered(LJID, Subscription, PresenceDelivery) of - true -> - DestJIDs = case BroadcastAll of - true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID)); - false -> [LJID] - end, - lists:foreach( - fun(DestJID) -> - ejabberd_router ! {route, service_jid(Host), jlib:make_jid(DestJID), Stanza} - end, DestJIDs); - false -> - ok - end - end, States), - broadcast_by_caps(Host, Node, Type, Stanza), - {result, true} - end + case node_call(Type, get_states, [Host, Node]) of + {result, []} -> + {result, false}; + {result, States} -> + Content = case get_option(Options, deliver_payloads) of + true -> Payload; + false -> [] + end, + ItemAttrs = case ItemId of + "" -> []; + _ -> [{"id", ItemId}] + end, + Stanza = event_stanza( + [{xmlelement, "items", [{"node", node_to_string(Node)}], + [{xmlelement, "item", ItemAttrs, Content}]}]), + broadcast_stanza(Host, Options, States, Stanza), + broadcast_by_caps(Host, Node, Type, Stanza), + {result, true}; + _ -> + {result, false} + end end, transaction(Host, Node, Action, sync_dirty). -broadcast_retract_item(Host, Node, ItemId) -> - broadcast_retract_item(Host, Node, ItemId, false). -broadcast_retract_item(Host, Node, ItemId, ForceNotify) -> +broadcast_retract_items(Host, Node, ItemIds) -> + broadcast_retract_items(Host, Node, ItemIds, false). +broadcast_retract_items(Host, Node, ItemIds, ForceNotify) -> Action = fun(#pubsub_node{options = Options, type = Type}) -> - case node_call(Type, get_states, [Host, Node]) of - {error, _} -> {result, false}; - {result, []} -> {result, false}; - {result, States} -> - Notify = case ForceNotify of - true -> true; - _ -> get_option(Options, notify_retract) - end, - ItemAttrs = case ItemId of - "" -> []; - _ -> [{"id", ItemId}] - end, - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", [{"node", node_to_string(Node)}], - [{xmlelement, "retract", ItemAttrs, []}]}]}]}, - case Notify of - true -> - lists:foreach( - fun(#pubsub_state{stateid = {JID, _}, - subscription = Subscription}) -> - if (Subscription /= none) and - (Subscription /= pending) -> - ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza}; - true -> - ok - end - end, States), - broadcast_by_caps(Host, Node, Type, Stanza), - {result, true}; - false -> - {result, false} - end - end + case (get_option(Options, notify_retract) or ForceNotify) of + true -> + case node_call(Type, get_states, [Host, Node]) of + {result, []} -> + {result, false}; + {result, States} -> + RetractEls = lists:map( + fun(ItemId) -> + ItemAttrs = case ItemId of + "" -> []; + _ -> [{"id", ItemId}] + end, + {xmlelement, "retract", ItemAttrs, []} + end, ItemIds), + Stanza = event_stanza( + [{xmlelement, "items", [{"node", node_to_string(Node)}], + RetractEls}]), + broadcast_stanza(Host, Options, States, Stanza), + broadcast_by_caps(Host, Node, Type, Stanza), + {result, true}; + _ -> + {result, false} + end; + _ -> + {result, false} + end end, transaction(Host, Node, Action, sync_dirty). broadcast_purge_node(Host, Node) -> Action = fun(#pubsub_node{options = Options, type = Type}) -> - case node_call(Type, get_states, [Host, Node]) of - {error, _} -> {result, false}; - {result, []} -> {result, false}; - {result, States} -> - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", - [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "purge", [{"node", node_to_string(Node)}], - []}]}]}, - case get_option(Options, notify_retract) of - true -> - lists:foreach( - fun(#pubsub_state{stateid = {JID,_}, - subscription = Subscription}) -> - if (Subscription /= none) and - (Subscription /= pending) -> - ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza}; - true -> - ok - end - end, States), - broadcast_by_caps(Host, Node, Type, Stanza), - {result, true}; - false -> - {result, false} - end - end + case get_option(Options, notify_retract) of + true -> + case node_call(Type, get_states, [Host, Node]) of + {result, []} -> + {result, false}; + {result, States} -> + Stanza = event_stanza( + [{xmlelement, "purge", [{"node", node_to_string(Node)}], []}]), + broadcast_stanza(Host, Options, States, Stanza), + broadcast_by_caps(Host, Node, Type, Stanza), + {result, true}; + _ -> + {result, false} + end; + _ -> + {result, false} + end end, transaction(Host, Node, Action, sync_dirty). -broadcast_removed_node(Host, Removed) -> - lists:foreach( - fun(Node) -> - Action = - fun(#pubsub_node{options = Options, type = Type}) -> - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "delete", [{"node", node_to_string(Node)}], - []}]}]}, - case get_option(Options, notify_delete) of - true -> - case node_call(Type, get_states, [Host, Node]) of - {result, States} -> - lists:foreach( - fun(#pubsub_state{stateid = {JID, _}, - subscription = Subscription}) -> - if (Subscription /= none) and - (Subscription /= pending) -> - ejabberd_router ! {route, service_jid(Host), jlib:make_jid(JID), Stanza}; - true -> - ok - end - end, States), - broadcast_by_caps(Host, Node, Type, Stanza), - {result, true}; - _ -> - {result, false} - end; - _ -> - {result, false} - end - end, - transaction(Host, Node, Action, sync_dirty) - end, Removed). +broadcast_removed_node(Host, Node) -> + Action = + fun(#pubsub_node{options = Options, type = Type}) -> + case get_option(Options, notify_delete) of + true -> + case node_call(Type, get_states, [Host, Node]) of + {result, []} -> + {result, false}; + {result, States} -> + Stanza = event_stanza( + [{xmlelement, "delete", [{"node", node_to_string(Node)}], []}]), + broadcast_stanza(Host, Options, States, Stanza), + broadcast_by_caps(Host, Node, Type, Stanza), + {result, true}; + _ -> + {result, false} + end; + _ -> + {result, false} + end + end, + transaction(Host, Node, Action, sync_dirty). broadcast_config_notification(Host, Node, Lang) -> Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type}) -> - case node_call(Type, get_states, [Host, Node]) of - {error, _} -> {result, false}; - {result, []} -> {result, false}; - {result, States} -> - case get_option(Options, notify_config) of - true -> - PresenceDelivery = get_option(Options, presence_based_delivery), - Content = case get_option(Options, deliver_payloads) of - true -> - [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], - get_configure_xfields(Type, Options, Lang, Owners)}]; - false -> - [] - end, - Stanza = {xmlelement, "message", [], - [{xmlelement, "event", [{"xmlns", ?NS_PUBSUB_EVENT}], - [{xmlelement, "items", [{"node", node_to_string(Node)}], - [{xmlelement, "item", [{"id", "configuration"}], - Content}]}]}]}, - lists:foreach( - fun(#pubsub_state{stateid = {LJID, _}, - subscription = Subscription}) -> - case is_to_delivered(LJID, Subscription, PresenceDelivery) of - true -> - ejabberd_router ! {route, service_jid(Host), jlib:make_jid(LJID), Stanza}; - false -> - ok - end - end, States), - broadcast_by_caps(Host, Node, Type, Stanza), - {result, true}; - _ -> - {result, false} - end - end + case get_option(Options, notify_config) of + true -> + case node_call(Type, get_states, [Host, Node]) of + {result, []} -> + {result, false}; + {result, States} -> + Content = case get_option(Options, deliver_payloads) of + true -> + [{xmlelement, "x", [{"xmlns", ?NS_XDATA}, {"type", "form"}], + get_configure_xfields(Type, Options, Lang, Owners)}]; + false -> + [] + end, + Stanza = event_stanza( + [{xmlelement, "items", [{"node", node_to_string(Node)}], + [{xmlelement, "item", [{"id", "configuration"}], + Content}]}]), + broadcast_stanza(Host, Options, States, Stanza), + broadcast_by_caps(Host, Node, Type, Stanza), + {result, true}; + _ -> + {result, false} + end; + _ -> + {result, false} + end end, transaction(Host, Node, Action, sync_dirty). -%TODO: simplify broadcast_* using a generic function like that: -%broadcast(Host, Node, Fun) -> -% transaction(fun() -> -% case tree_call(Host, get_node, [Host, Node]) of -% #pubsub_node{options = Options, owners = Owners, type = Type} -> -% case node_call(Type, get_states, [Host, Node]) of -% {error, _} -> {result, false}; -% {result, []} -> {result, false}; -% {result, States} -> -% lists:foreach(fun(#pubsub_state{stateid = {JID,_}, subscription = Subscription}) -> -% Fun(Host, Node, Options, Owners, JID, Subscription) -% end, States), -% {result, true} -% end; -% Other -> -% Other -% end -% end, sync_dirty). - +broadcast_stanza(Host, NodeOpts, States, Stanza) -> + PresenceDelivery = get_option(NodeOpts, presence_based_delivery), + BroadcastAll = get_option(NodeOpts, broadcast_all_resources), + From = service_jid(Host), + lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) -> + case is_to_deliver(LJID, Subs, PresenceDelivery) of + true -> + JIDs = case BroadcastAll of + true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID)); + false -> [LJID] + end, + lists:foreach(fun(JID) -> + ejabberd_router ! {route, From, jlib:make_jid(JID), Stanza} + end, JIDs); + false -> + ok + end + end, States). %% broadcast Stanza to all contacts of the user that are advertising %% interest in this kind of Node. @@ -2676,18 +2617,18 @@ plugins(Host) -> features() -> [ - %"access-authorize", % OPTIONAL + %TODO "access-authorize", % OPTIONAL "access-open", % OPTIONAL this relates to access_model option in node_default "access-presence", % OPTIONAL this relates to access_model option in node_pep - %"access-roster", % OPTIONAL - %"access-whitelist", % OPTIONAL + %TODO "access-roster", % OPTIONAL + %TODO "access-whitelist", % OPTIONAL % see plugin "auto-create", % OPTIONAL % see plugin "auto-subscribe", % RECOMMENDED "collections", % RECOMMENDED "config-node", % RECOMMENDED "create-and-configure", % RECOMMENDED % see plugin "create-nodes", % RECOMMENDED - %TODO "delete-any", % OPTIONAL + % see plugin "delete-any", % RECOMMENDED % see plugin "delete-nodes", % RECOMMENDED % see plugin "filtered-notifications", % RECOMMENDED %TODO "get-pending", % OPTIONAL diff --git a/src/mod_pubsub/node_default.erl b/src/mod_pubsub/node_default.erl index ba8e2edd7..ee10eeb74 100644 --- a/src/mod_pubsub/node_default.erl +++ b/src/mod_pubsub/node_default.erl @@ -157,6 +157,7 @@ features() -> ["create-nodes", "auto-create", "delete-nodes", + "delete-any", "instant-nodes", "manage-subscriptions", "modify-affiliations", @@ -217,8 +218,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) -> %% @doc

create_node(Host, Node, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - mnesia:write(#pubsub_state{stateid = {OwnerKey, {Host, Node}}, - affiliation = owner, subscription = none}), + set_state(#pubsub_state{stateid = {OwnerKey, {Host, Node}}, affiliation = owner}), {result, {default, broadcast}}. @@ -231,12 +231,8 @@ delete_node(Host, Removed) -> fun(Node) -> lists:foreach( fun(#pubsub_state{stateid = StateId, items = Items}) -> - lists:foreach( - fun(ItemId) -> - mnesia:delete( - {pubsub_item, {ItemId, {Host, Node}}}) - end, Items), - mnesia:delete({pubsub_state, StateId}) + del_items(Host, Node, Items), + del_state(StateId) end, mnesia:match_object( #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'})) @@ -278,14 +274,9 @@ delete_node(Host, Removed) -> %%

In the default plugin module, the record is unchanged.

subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) -> - SenderKey = jlib:jid_tolower(Sender), - Authorized = (jlib:jid_remove_resource(SenderKey) == jlib:jid_remove_resource(Subscriber)), - % TODO add some acl check for Authorized ? - State = case get_state(Host, Node, Subscriber) of - {error, ?ERR_ITEM_NOT_FOUND} -> - #pubsub_state{stateid = {Subscriber, {Host, Node}}}; % TODO: bug on Key ? - {result, S} -> S - end, + SubscriberKey = jlib:jid_tolower(jlib:jid_remove_resource(Subscriber)), + Authorized = (jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == SubscriberKey), + State = get_state(Host, Node, SubscriberKey), #pubsub_state{affiliation = Affiliation, subscription = Subscription} = State, if @@ -321,7 +312,6 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel, if AccessModel == authorize -> pending; - %%TODO Affiliation == none -> ? %%NeedConfiguration -> %% unconfigured true -> @@ -349,43 +339,29 @@ 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) -> - SenderKey = jlib:jid_tolower(Sender), - Match = jlib:jid_remove_resource(SenderKey) == jlib:jid_remove_resource(Subscriber), - Authorized = case Match of - true -> - true; - false -> - case get_state(Host, Node, SenderKey) of % TODO: bug on Key ? - {result, #pubsub_state{affiliation=owner}} -> true; - _ -> false - end - end, - case get_state(Host, Node, Subscriber) of - {error, ?ERR_ITEM_NOT_FOUND} -> - %% Requesting entity is not a subscriber + SubscriberKey = jlib:jid_tolower(jlib:jid_remove_resource(Subscriber)), + Authorized = (jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == SubscriberKey), + State = get_state(Host, Node, SubscriberKey), + if + %% Entity did not specify SubID + %%SubID == "", ?? -> + %% {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; + %% Invalid subscription identifier + %%InvalidSubID -> + %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; + %% Requesting entity is not a subscriber + State#pubsub_state.subscription == none -> {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}; - {result, State} -> - if - %% Entity did not specify SubID - %%SubID == "", ?? -> - %% {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")}; - %% Invalid subscription identifier - %%InvalidSubID -> - %% {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")}; - %% Requesting entity is not a subscriber - State#pubsub_state.subscription == none -> - {error, ?ERR_EXTENDED(?ERR_UNEXPECTED_REQUEST, "not-subscribed")}; - %% Requesting entity is prohibited from unsubscribing entity - not Authorized -> - {error, ?ERR_FORBIDDEN}; - %% Was just subscriber, remove the record - State#pubsub_state.affiliation == none -> - mnesia:delete({pubsub_state, State#pubsub_state.stateid}), - {result, default}; - true -> - set_state(State#pubsub_state{subscription = none}), - {result, default} - end + %% Requesting entity is prohibited from unsubscribing entity + (not Authorized) and (State#pubsub_state.affiliation =/= owner) -> + {error, ?ERR_FORBIDDEN}; + %% Was just subscriber, remove the record + State#pubsub_state.affiliation == none -> + del_state(State#pubsub_state.stateid), + {result, default}; + true -> + set_state(State#pubsub_state{subscription = none}), + {result, default} end. %% @spec (Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> @@ -429,10 +405,7 @@ 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:jid_tolower(jlib:jid_remove_resource(Publisher)), - State = case get_state(Host, Node, PublisherKey) of - {error, ?ERR_ITEM_NOT_FOUND} -> #pubsub_state{stateid={PublisherKey, {Host, Node}}}; - {result, S} -> S - end, + State = get_state(Host, Node, PublisherKey), #pubsub_state{affiliation = Affiliation, subscription = Subscription} = State, if @@ -445,16 +418,15 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) -> {error, ?ERR_FORBIDDEN}; true -> PubId = {PublisherKey, now()}, - %% TODO: check creation, presence, roster (EJAB-663) Item = case get_item(Host, Node, ItemId) of - {error, ?ERR_ITEM_NOT_FOUND} -> + {result, OldItem} -> + OldItem#pubsub_item{modification = PubId, + payload = Payload}; + _ -> #pubsub_item{itemid = {ItemId, {Host, Node}}, creation = PubId, modification = PubId, - payload = Payload}; - {result, OldItem} -> - OldItem#pubsub_item{modification = PubId, - payload = Payload} + payload = Payload} end, Items = [ItemId | State#pubsub_state.items--[ItemId]], {result, {NI, OI}} = remove_extra_items( @@ -488,9 +460,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) -> NewItems = lists:sublist(ItemIds, MaxItems), OldItems = lists:nthtail(length(NewItems), ItemIds), %% Remove extra items: - lists:foreach(fun(ItemId) -> - mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}) - end, OldItems), + del_items(Host, Node, OldItems), %% Return the new items list: {result, {NewItems, OldItems}}. @@ -506,12 +476,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) -> %% or a publisher.

delete_item(Host, Node, Publisher, ItemId) -> PublisherKey = jlib:jid_tolower(jlib:jid_remove_resource(Publisher)), - State = case get_state(Host, Node, PublisherKey) of - {error, ?ERR_ITEM_NOT_FOUND} -> - #pubsub_state{stateid = {PublisherKey, {Host, Node}}}; - {result, S} -> - S - end, + State = get_state(Host, Node, PublisherKey), #pubsub_state{affiliation = Affiliation, items = Items} = State, Allowed = (Affiliation == publisher) orelse (Affiliation == owner) orelse case get_item(Host, Node, ItemId) of @@ -525,7 +490,7 @@ delete_item(Host, Node, Publisher, ItemId) -> true -> case get_item(Host, Node, ItemId) of {result, _} -> - mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}), + del_item(Host, Node, ItemId), NewItems = lists:delete(ItemId, Items), set_state(State#pubsub_state{items = NewItems}), {result, {default, broadcast}}; @@ -546,16 +511,12 @@ delete_item(Host, Node, Publisher, ItemId) -> purge_node(Host, Node, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), case get_state(Host, Node, OwnerKey) of - {result, #pubsub_state{items = Items, affiliation = owner}} -> - lists:foreach(fun(ItemId) -> - mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}) - end, Items), + #pubsub_state{items = Items, affiliation = owner} -> + del_items(Host, Node, Items), {result, {default, broadcast}}; - {result, _} -> - %% Entity is not owner - {error, ?ERR_FORBIDDEN}; _ -> - {error, ?ERR_ITEM_NOT_FOUND} + %% Entity is not owner + {error, ?ERR_FORBIDDEN} end. %% @spec (Host, JID) -> [{Node,Affiliation}] @@ -571,8 +532,7 @@ purge_node(Host, Node, Owner) -> get_entity_affiliations(Host, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), States = mnesia:match_object( - #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, - _ = '_'}), + #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}), Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) -> {N, A} end, @@ -580,8 +540,7 @@ get_entity_affiliations(Host, Owner) -> get_node_affiliations(Host, Node) -> States = mnesia:match_object( - #pubsub_state{stateid = {'_', {Host, Node}}, - _ = '_'}), + #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}), Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) -> {J, A} end, @@ -589,22 +548,13 @@ get_node_affiliations(Host, Node) -> get_affiliation(Host, Node, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Affiliation = case get_state(Host, Node, OwnerKey) of - {result, #pubsub_state{affiliation = A}} -> A; - _ -> none - end, - {result, Affiliation}. + State = get_state(Host, Node, OwnerKey), + {result, State#pubsub_state.affiliation}. set_affiliation(Host, Node, Owner, Affiliation) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Record = case get_state(Host, Node, OwnerKey) of - {error, ?ERR_ITEM_NOT_FOUND} -> - #pubsub_state{stateid = {OwnerKey, {Host, Node}}, - affiliation = Affiliation}; - {result, State} -> - State#pubsub_state{affiliation = Affiliation} - end, - set_state(Record), + State = get_state(Host, Node, OwnerKey), + set_state(State#pubsub_state{affiliation = Affiliation}), ok. %% @spec (Host) -> [{Node,Subscription}] @@ -620,8 +570,7 @@ set_affiliation(Host, Node, Owner, Affiliation) -> get_entity_subscriptions(Host, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), States = mnesia:match_object( - #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, - _ = '_'}), + #pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}), Tr = fun(#pubsub_state{stateid = {_, {_, N}}, subscription = S}) -> {N, S} end, @@ -629,8 +578,7 @@ get_entity_subscriptions(Host, Owner) -> get_node_subscriptions(Host, Node) -> States = mnesia:match_object( - #pubsub_state{stateid = {'_', {Host, Node}}, - _ = '_'}), + #pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}), Tr = fun(#pubsub_state{stateid = {J, {_, _}}, subscription = S}) -> {J, S} end, @@ -638,22 +586,13 @@ get_node_subscriptions(Host, Node) -> get_subscription(Host, Node, Owner) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Subscription = case get_state(Host, Node, OwnerKey) of - {result, #pubsub_state{subscription = S}} -> S; - _ -> none - end, - {result, Subscription}. + State = get_state(Host, Node, OwnerKey), + {result, State#pubsub_state.subscription}. set_subscription(Host, Node, Owner, Subscription) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Record = case get_state(Host, Node, OwnerKey) of - {error, ?ERR_ITEM_NOT_FOUND} -> - #pubsub_state{stateid = {OwnerKey, {Host, Node}}, - subscription = Subscription}; - {result, State} -> - State#pubsub_state{subscription = Subscription} - end, - set_state(Record), + State = get_state(Host, Node, OwnerKey), + set_state(State#pubsub_state{subscription = Subscription}), ok. %% @spec (Host, Node) -> [States] | [] @@ -682,11 +621,10 @@ get_states(Host, Node) -> %% State = mod_pubsub:pubsubItems() %% @doc

Returns a state (one state list), given its reference.

get_state(Host, Node, JID) -> - case mnesia:read({pubsub_state, {JID, {Host, Node}}}) of - [State] when is_record(State, pubsub_state) -> - {result, State}; - _ -> - {error, ?ERR_ITEM_NOT_FOUND} + StateId = {JID, {Host, Node}}, + case mnesia:read({pubsub_state, StateId}) of + [State] when is_record(State, pubsub_state) -> State; + _ -> #pubsub_state{stateid=StateId} end. %% @spec (State) -> ok | {error, ?ERR_INTERNAL_SERVER_ERROR} @@ -697,6 +635,12 @@ set_state(State) when is_record(State, pubsub_state) -> set_state(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}. +%% @spec (StateId) -> ok | {error, Reason::stanzaError()} +%% StateId = mod_pubsub:pubsubStateId() +%% @doc

Delete a state from database.

+del_state(StateId) -> + mnesia:delete({pubsub_state, StateId}). + %% @spec (Host, Node) -> [Items] | [] %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() @@ -716,11 +660,9 @@ get_items(Host, Node) -> #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}), {result, Items}. get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - {Affiliation, Subscription} = - case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of - {result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; - _ -> {none, none} - end, + State = get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))), + #pubsub_state{affiliation = Affiliation, + subscription = Subscription} = State, Subscribed = not ((Subscription == none) or (Subscription == pending)), if %%SubID == "", ?? -> @@ -768,11 +710,9 @@ get_item(Host, Node, ItemId) -> {error, ?ERR_ITEM_NOT_FOUND} end. get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - {Affiliation, Subscription} = - case get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))) of - {result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; - _ -> {none, none} - end, + State = get_state(Host, Node, jlib:jid_tolower(jlib:jid_remove_resource(JID))), + #pubsub_state{affiliation = Affiliation, + subscription = Subscription} = State, Subscribed = not ((Subscription == none) or (Subscription == pending)), if %%SubID == "", ?? -> @@ -813,3 +753,15 @@ set_item(Item) when is_record(Item, pubsub_item) -> mnesia:write(Item); set_item(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}. + +%% @spec (ItemId) -> ok | {error, Reason::stanzaError()} +%% Host = mod_pubsub:host() +%% Node = mod_pubsub:pubsubNode() +%% ItemId = string() +%% @doc

Delete an item from database.

+del_item(Host, Node, ItemId) -> + mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}). +del_items(Host, Node, ItemIds) -> + lists:foreach(fun(ItemId) -> + del_item(Host, Node, ItemId) + end, ItemIds). diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index a9fdaf36e..7bba44aaf 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -178,15 +178,9 @@ get_affiliation(_Host, Node, Owner) -> set_affiliation(_Host, Node, Owner, Affiliation) -> OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), - Record = case get_state(OwnerKey, Node, OwnerKey) of - {error, ?ERR_ITEM_NOT_FOUND} -> - #pubsub_state{stateid = {OwnerKey, {OwnerKey, Node}}, - affiliation = Affiliation}; - {result, State} -> - State#pubsub_state{affiliation = Affiliation} - end, - set_state(Record), - ok. + State = get_state(OwnerKey, Node, OwnerKey), + set_state(State#pubsub_state{affiliation = Affiliation}), + ok. get_entity_subscriptions(_Host, _Owner) -> {result, []}. diff --git a/src/mod_pubsub/pubsub.hrl b/src/mod_pubsub/pubsub.hrl index 959124e4c..3f64b7b23 100644 --- a/src/mod_pubsub/pubsub.hrl +++ b/src/mod_pubsub/pubsub.hrl @@ -72,7 +72,7 @@ %%% lserver = string(), %%% lresource = string()}. -%%% @type usr() = {User::string(), Server::string(), Resource::string()}. +%%% @type ljid() = {User::string(), Server::string(), Resource::string()}. %%% @type affiliation() = none | owner | publisher | outcast. %%% @type subscription() = none | pending | unconfigured | subscribed. @@ -81,7 +81,7 @@ %%% nodeid = {Host::host(), Node::pubsubNode()}, %%% parentid = {Host::host(), Node::pubsubNode()}, %%% type = nodeType(), -%%% owners = [usr()], +%%% owners = [ljid()], %%% options = [nodeOption()]}. %%%

This is the format of the nodes table. The type of the table %%% is: set,ram/disc.

@@ -94,7 +94,7 @@ }). %%% @type pubsubState() = #pubsub_state{ -%%% stateid = {jid(), {Host::host(), Node::pubsubNode()}}, +%%% stateid = {ljid(), {Host::host(), Node::pubsubNode()}}, %%% items = [ItemId::string()], %%% affiliation = affiliation(), %%% subscription = subscription()}. @@ -108,8 +108,8 @@ %% @type pubsubItem() = #pubsub_item{ %% itemid = {ItemId::string(), {Host::host(),Node::pubsubNode()}}, -%% creation = {JID::jid(), now()}, -%% modification = {JID::jid(), now()}, +%% creation = {ljid(), now()}, +%% modification = {ljid(), now()}, %% payload = XMLContent::string()}. %%%

This is the format of the published items table. The type of the %%% table is: set,disc,fragmented.