From e032a8c54f0b28af78c05bb3ae22106cec40bc83 Mon Sep 17 00:00:00 2001
From: Christophe Romain Check if a notification must be delivered or not. Build pubsub event stanza
+event_stanza(Els) ->
+ #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children = 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
- "" -> [];
- _ -> [#xmlattr{name = 'id', value = ItemId}]
- end,
- Stanza = make_stanza(Node, 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,
- route_stanza(Host, DestJIDs, Stanza);
- 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
+ "" -> [];
+ _ -> [#xmlattr{name = 'id', value = ItemId}]
+ end,
+ Stanza = event_stanza(
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = 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).
-%% ItemAttrs is a list of tuples:
-%% For example: [{"id", ItemId}]
-make_stanza(Node, ItemAttrs, Payload) ->
- #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Payload}]}]}]}.
-
-%% DestJIDs = [{LUser, LServer, LResource}]
-route_stanza(Host, DestJIDs, Stanza) ->
- lists:foreach(
- fun({DU, DS, DR}) ->
- ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(DU, DS, DR), Stanza}
- end, DestJIDs).
-
-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
- "" -> [];
- _ -> [#xmlattr{name = 'id', value = ItemId}]
- end,
- Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = ItemAttrs}]}]}]},
- case Notify of
- true ->
- lists:foreach(
- fun(#pubsub_state{stateid = {{U, S, R}, _},
- subscription = Subscription}) ->
- if (Subscription /= none) and
- (Subscription /= pending) ->
- ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), 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
+ "" -> [];
+ _ -> [#xmlattr{name = 'id', value = ItemId}]
+ end,
+ #xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = ItemAttrs}
+ end, ItemIds),
+ Stanza = event_stanza(
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}],
+ children = 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 = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}]}]}]},
- case get_option(Options, notify_retract) of
- true ->
- lists:foreach(
- fun(#pubsub_state{stateid = {{U, S, R},_},
- subscription = Subscription}) ->
- if (Subscription /= none) and
- (Subscription /= pending) ->
- ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), 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(
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [#xmlattr{name = 'node', value = 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 = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [#xmlattr{name = 'node', value = 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 = {{U, S, R}, _},
- subscription = Subscription}) ->
- if (Subscription /= none) and
- (Subscription /= pending) ->
- ejabberd_router ! {route, service_jid(Host), jlib:make_jid(U, S, R), 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(
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [#xmlattr{name = 'node', value = 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 ->
- [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [#xmlattr{name = 'type', value = "form"}], children =
- get_configure_xfields(Type, Options, Lang, Owners)}];
- false ->
- []
- end,
- Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
- [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [#xmlattr{name = 'id', value = "configuration"}], children =
- Content}]}]}]},
- lists:foreach(
- fun(#pubsub_state{stateid = {{U, S, R} = LJID, _},
- subscription = Subscription}) ->
- case is_to_delivered(LJID, Subscription, PresenceDelivery) of
- true ->
- ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), 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 ->
+ [#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [#xmlattr{name = 'type', value = "form"}], children =
+ get_configure_xfields(Type, Options, Lang, Owners)}];
+ false ->
+ []
+ end,
+ Stanza = event_stanza(
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
+ [#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [#xmlattr{name = 'id', value = "configuration"}], children =
+ 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({U, S, R}) ->
+ ejabberd_router ! {route, From, exmpp_jlib:make_jid(U, S, R), 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.
@@ -2724,18 +2658,18 @@ select_type(ServerHost, Host, Node) ->
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 8757ac4a4..2deb66907 100644
--- a/src/mod_pubsub/node_default.erl
+++ b/src/mod_pubsub/node_default.erl
@@ -160,6 +160,7 @@ features() ->
["create-nodes",
"auto-create",
"delete-nodes",
+ "delete-any",
"instant-nodes",
"manage-subscriptions",
"modify-affiliations",
@@ -222,8 +223,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
%% @doc In the default plugin module, the record is unchanged. Unsubscribe the Subscriber from the Node. In the default plugin module, the record is unchanged.
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, '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, Reason::stanzaError()} @@ -700,6 +642,12 @@ set_state(State) when is_record(State, pubsub_state) -> set_state(_) -> {error, 'internal-server-error'}. +%% @spec (StateId) -> ok | {error, Reason::stanzaError()} +%% StateId = mod_pubsub:pubsubStateId() +%% @docDelete a state from database.
+del_state(StateId) -> + mnesia:delete({pubsub_state, StateId}). + %% @spec (Host, Node) -> [Items] | [] %% Host = mod_pubsub:host() %% Node = mod_pubsub:pubsubNode() @@ -719,11 +667,9 @@ get_items(Host, Node, _From) -> #pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}), {result, Items}. get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - {Affiliation, Subscription} = - case get_state(Host, Node, jlib:short_prepd_bare_jid(JID)) of - {result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; - _ -> {none, none} - end, + 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)), if %%SubID == "", ?? -> @@ -771,11 +717,9 @@ get_item(Host, Node, ItemId) -> {error, 'item-not-found'} end. get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) -> - {Affiliation, Subscription} = - case get_state(Host, Node, jlib:short_prepd_bare_jid(JID)) of - {result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S}; - _ -> {none, none} - end, + 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)), if %%SubID == "", ?? -> @@ -817,6 +761,18 @@ set_item(Item) when is_record(Item, pubsub_item) -> set_item(_) -> {error, 'internal-server-error'}. +%% @spec (ItemId) -> ok | {error, Reason::stanzaError()} +%% Host = mod_pubsub:host() +%% Node = mod_pubsub:pubsubNode() +%% ItemId = string() +%% @docDelete 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). + %% @docReturn the name of the node if known: Default is to return %% node id.
get_item_name(_Host, _Node, Id) -> diff --git a/src/mod_pubsub/node_pep.erl b/src/mod_pubsub/node_pep.erl index ec617abda..cbbc16ce6 100644 --- a/src/mod_pubsub/node_pep.erl +++ b/src/mod_pubsub/node_pep.erl @@ -182,15 +182,9 @@ get_affiliation(_Host, Node, Owner) -> set_affiliation(_Host, Node, Owner, Affiliation) -> OwnerKey = jlib:short_prepd_bare_jid(Owner), - Record = case get_state(OwnerKey, Node, OwnerKey) of - {error, '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 b4ceed4fa..61ed88dbe 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.