mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
PubSub cleanup, EJAB-827 fix, EJAB-701 partial fix
SVN Revision: 1767
This commit is contained in:
parent
214ef31053
commit
e032a8c54f
18
ChangeLog
18
ChangeLog
@ -1,3 +1,21 @@
|
|||||||
|
2009-01-03 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
* src/mod_pubsub/mod_pubsub.erl: use of delete-any feature instead of
|
||||||
|
delete-nodes for delete item use case (fix from erroneous definition
|
||||||
|
in XEP-0060)
|
||||||
|
|
||||||
2008-12-23 Christophe Romain <christophe.romain@process-one.net>
|
2008-12-23 Christophe Romain <christophe.romain@process-one.net>
|
||||||
|
|
||||||
* src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
|
* src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
%%%
|
%%%
|
||||||
%%% @reference See <a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060: Pubsub</a> for
|
%%% @reference See <a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060: Pubsub</a> for
|
||||||
%%% the latest version of the PubSub specification.
|
%%% 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.
|
%%% Most of the specification is implemented.
|
||||||
%%% Functions concerning configuration should be rewritten.
|
%%% Functions concerning configuration should be rewritten.
|
||||||
%%% Code is derivated from the original pubsub v1.7, by Alexey Shchepin <alexey@process-one.net>
|
%%% Code is derivated from the original pubsub v1.7, by Alexey Shchepin <alexey@process-one.net>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
-module(mod_pubsub).
|
-module(mod_pubsub).
|
||||||
-author('christophe.romain@process-one.net').
|
-author('christophe.romain@process-one.net').
|
||||||
-version('1.11-01').
|
-version('1.12-01').
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
@ -92,9 +92,7 @@
|
|||||||
string_to_subscription/1,
|
string_to_subscription/1,
|
||||||
string_to_affiliation/1,
|
string_to_affiliation/1,
|
||||||
extended_error/2,
|
extended_error/2,
|
||||||
extended_error/3,
|
extended_error/3
|
||||||
make_stanza/3,
|
|
||||||
route_stanza/3
|
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% API and gen_server callbacks
|
%% API and gen_server callbacks
|
||||||
@ -1076,12 +1074,11 @@ find_authorization_response(Packet) ->
|
|||||||
%% Plugins = [Plugin::string()]
|
%% Plugins = [Plugin::string()]
|
||||||
%% @doc Send a message to JID with the supplied Subscription
|
%% @doc Send a message to JID with the supplied Subscription
|
||||||
send_authorization_approval(Host, JID, Node, Subscription) ->
|
send_authorization_approval(Host, JID, Node, Subscription) ->
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
Stanza = event_stanza(
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
|
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'subscription', attrs =
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'subscription', attrs =
|
||||||
[#xmlattr{name = 'node', value = Node},
|
[#xmlattr{name = 'node', value = Node},
|
||||||
#xmlattr{name = 'jid', value = exmpp_jid:jid_to_list(JID)},
|
#xmlattr{name = 'jid', value = exmpp_jid:jid_to_list(JID)},
|
||||||
#xmlattr{name = 'subscription', value = subscription_to_string(Subscription)}]}]}]},
|
#xmlattr{name = 'subscription', value = subscription_to_string(Subscription)}]}]),
|
||||||
ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
|
ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
|
||||||
|
|
||||||
handle_authorization_response(Host, From, To, Packet, XFields) ->
|
handle_authorization_response(Host, From, To, Packet, XFields) ->
|
||||||
@ -1271,7 +1268,6 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
|||||||
%% [{xmlelement, "x", [{"xmlns", ?NS_DATA_FORMS}, {"type", "result"}],
|
%% [{xmlelement, "x", [{"xmlns", ?NS_DATA_FORMS}, {"type", "result"}],
|
||||||
%% [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI),
|
%% [?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NMI),
|
||||||
%% ?XFIELD("jid-single", "Node Creator", "creator", jlib:jid_to_string(OwnerKey))]}]),
|
%% ?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
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
@ -1317,14 +1313,17 @@ delete_node(Host, Node, Owner) ->
|
|||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
{error, Error};
|
{error, Error};
|
||||||
{result, {Result, broadcast, Removed}} ->
|
{result, {Result, broadcast, Removed}} ->
|
||||||
broadcast_removed_node(Host, Removed),
|
lists:foreach(fun(RNode) ->
|
||||||
%%broadcast_retract_item(Host, ["pubsub", "nodes"], node_to_string(Node)),
|
broadcast_removed_node(Host, RNode)
|
||||||
|
end, Removed),
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
end;
|
end;
|
||||||
{result, {Result, Removed}} ->
|
{result, {Result, Removed}} ->
|
||||||
broadcast_removed_node(Host, Removed),
|
lists:foreach(fun(RNode) ->
|
||||||
|
broadcast_removed_node(Host, RNode)
|
||||||
|
end, Removed),
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
@ -1536,23 +1535,17 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
|||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{error, Reason};
|
{error, Reason};
|
||||||
{result, {Result, broadcast, Removed}} ->
|
{result, {Result, broadcast, Removed}} ->
|
||||||
lists:foreach(fun(OldItem) ->
|
broadcast_retract_items(Host, Node, Removed),
|
||||||
broadcast_retract_item(Host, Node, OldItem)
|
|
||||||
end, Removed),
|
|
||||||
broadcast_publish_item(Host, Node, ItemId, jlib:short_prepd_jid(Publisher), Payload),
|
broadcast_publish_item(Host, Node, ItemId, jlib:short_prepd_jid(Publisher), Payload),
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
end;
|
end;
|
||||||
{result, default, Removed} ->
|
{result, default, Removed} ->
|
||||||
lists:foreach(fun(OldItem) ->
|
broadcast_retract_items(Host, Node, Removed),
|
||||||
broadcast_retract_item(Host, Node, OldItem)
|
|
||||||
end, Removed),
|
|
||||||
{result, Reply};
|
{result, Reply};
|
||||||
{result, Result, Removed} ->
|
{result, Result, Removed} ->
|
||||||
lists:foreach(fun(OldItem) ->
|
broadcast_retract_items(Host, Node, Removed),
|
||||||
broadcast_retract_item(Host, Node, OldItem)
|
|
||||||
end, Removed),
|
|
||||||
{result, Result};
|
{result, Result};
|
||||||
{result, default} ->
|
{result, default} ->
|
||||||
{result, Reply};
|
{result, Reply};
|
||||||
@ -1583,7 +1576,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
|
|||||||
Action = fun(#pubsub_node{type = Type}) ->
|
Action = fun(#pubsub_node{type = Type}) ->
|
||||||
Features = features(Type),
|
Features = features(Type),
|
||||||
PersistentFeature = lists:member("persistent-items", Features),
|
PersistentFeature = lists:member("persistent-items", Features),
|
||||||
DeleteFeature = lists:member("delete-nodes", Features),
|
DeleteFeature = lists:member("delete-any", Features),
|
||||||
if
|
if
|
||||||
%%-> iq_pubsub just does that matchs
|
%%-> iq_pubsub just does that matchs
|
||||||
%% %% Request does not specify an item
|
%% %% Request does not specify an item
|
||||||
@ -1593,7 +1586,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
|
|||||||
{error, extended_error('feature-not-implemented', unsupported, "persistent-items")};
|
{error, extended_error('feature-not-implemented', unsupported, "persistent-items")};
|
||||||
not DeleteFeature ->
|
not DeleteFeature ->
|
||||||
%% Service does not support item deletion
|
%% Service does not support item deletion
|
||||||
{error, extended_error('feature-not-implemented', unsupported, "delete-nodes")};
|
{error, extended_error('feature-not-implemented', unsupported, "delete-any")};
|
||||||
true ->
|
true ->
|
||||||
node_call(Type, delete_item, [Host, Node, Publisher, ItemId])
|
node_call(Type, delete_item, [Host, Node, Publisher, ItemId])
|
||||||
end
|
end
|
||||||
@ -1603,7 +1596,7 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
|
|||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
{error, Reason};
|
{error, Reason};
|
||||||
{result, {Result, broadcast}} ->
|
{result, {Result, broadcast}} ->
|
||||||
broadcast_retract_item(Host, Node, ItemId, ForceNotify),
|
broadcast_retract_items(Host, Node, [ItemId], ForceNotify),
|
||||||
case Result of
|
case Result of
|
||||||
default -> {result, Reply};
|
default -> {result, Reply};
|
||||||
_ -> {result, Result}
|
_ -> {result, Result}
|
||||||
@ -1771,12 +1764,10 @@ send_items(Host, Node, {LU, LS, LR} = LJID, Number) ->
|
|||||||
[First|Tail] = Items,
|
[First|Tail] = Items,
|
||||||
[lists:foldl(
|
[lists:foldl(
|
||||||
fun(CurItem, LastItem) ->
|
fun(CurItem, LastItem) ->
|
||||||
{_, {LMS, LS, LmS}} = LastItem#pubsub_item.creation,
|
{_, LTimeStamp} = LastItem#pubsub_item.creation,
|
||||||
{_, {CMS, CS, CmS}} = CurItem#pubsub_item.creation,
|
{_, CTimeStamp} = CurItem#pubsub_item.creation,
|
||||||
LTimestamp = LMS * 1000000 + LS * 1000 + LmS,
|
|
||||||
CTimestamp = CMS * 1000000 + CS * 1000 + CmS,
|
|
||||||
if
|
if
|
||||||
CTimestamp > LTimestamp -> CurItem;
|
CTimeStamp > LTimeStamp -> CurItem;
|
||||||
true -> LastItem
|
true -> LastItem
|
||||||
end
|
end
|
||||||
end, First, Tail)];
|
end, First, Tail)];
|
||||||
@ -1795,10 +1786,9 @@ send_items(Host, Node, {LU, LS, LR} = LJID, Number) ->
|
|||||||
end,
|
end,
|
||||||
#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Payload}
|
#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Payload}
|
||||||
end, ToSend),
|
end, ToSend),
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
Stanza = event_stanza(
|
||||||
[#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 = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
|
||||||
ItemsEls}]}]},
|
ItemsEls}]),
|
||||||
ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(LU, LS, LR), Stanza}.
|
ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(LU, LS, LR), Stanza}.
|
||||||
|
|
||||||
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
|
%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
|
||||||
@ -2132,10 +2122,10 @@ service_jid(Host) ->
|
|||||||
%% Subscription = atom()
|
%% Subscription = atom()
|
||||||
%% PresenceDelivery = boolean()
|
%% PresenceDelivery = boolean()
|
||||||
%% @doc <p>Check if a notification must be delivered or not.</p>
|
%% @doc <p>Check if a notification must be delivered or not.</p>
|
||||||
is_to_delivered(_, none, _) -> false;
|
is_to_deliver(_, none, _) -> false;
|
||||||
is_to_delivered(_, pending, _) -> false;
|
is_to_deliver(_, pending, _) -> false;
|
||||||
is_to_delivered(_, _, false) -> true;
|
is_to_deliver(_, _, false) -> true;
|
||||||
is_to_delivered({User, Server, _}, _, true) ->
|
is_to_deliver({User, Server, _}, _, true) ->
|
||||||
case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
|
case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
|
||||||
[] -> false;
|
[] -> false;
|
||||||
Ss ->
|
Ss ->
|
||||||
@ -2153,228 +2143,172 @@ payload_xmlelements([], Count) -> Count;
|
|||||||
payload_xmlelements([#xmlel{}|Tail], Count) -> payload_xmlelements(Tail, Count+1);
|
payload_xmlelements([#xmlel{}|Tail], Count) -> payload_xmlelements(Tail, Count+1);
|
||||||
payload_xmlelements([_|Tail], Count) -> payload_xmlelements(Tail, Count).
|
payload_xmlelements([_|Tail], Count) -> payload_xmlelements(Tail, Count).
|
||||||
|
|
||||||
|
%% @spec (Els) -> stanza()
|
||||||
|
%% Els = [xmlelement()]
|
||||||
|
%% @doc <p>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 functions
|
||||||
|
|
||||||
broadcast_publish_item(Host, Node, ItemId, _From, Payload) ->
|
broadcast_publish_item(Host, Node, ItemId, _From, Payload) ->
|
||||||
Action =
|
Action =
|
||||||
fun(#pubsub_node{options = Options, type = Type}) ->
|
fun(#pubsub_node{options = Options, type = Type}) ->
|
||||||
case node_call(Type, get_states, [Host, Node]) of
|
case node_call(Type, get_states, [Host, Node]) of
|
||||||
{error, _} -> {result, false};
|
{result, []} ->
|
||||||
{result, []} -> {result, false};
|
{result, false};
|
||||||
{result, States} ->
|
{result, States} ->
|
||||||
PresenceDelivery = get_option(Options, presence_based_delivery),
|
Content = case get_option(Options, deliver_payloads) of
|
||||||
BroadcastAll = get_option(Options, broadcast_all_resources),
|
true -> Payload;
|
||||||
Content = case get_option(Options, deliver_payloads) of
|
false -> []
|
||||||
true -> Payload;
|
end,
|
||||||
false -> []
|
ItemAttrs = case ItemId of
|
||||||
end,
|
"" -> [];
|
||||||
ItemAttrs = case ItemId of
|
_ -> [#xmlattr{name = 'id', value = ItemId}]
|
||||||
"" -> [];
|
end,
|
||||||
_ -> [#xmlattr{name = 'id', value = ItemId}]
|
Stanza = event_stanza(
|
||||||
end,
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
|
||||||
Stanza = make_stanza(Node, ItemAttrs, Content),
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = ItemAttrs, children = Content}]}]),
|
||||||
lists:foreach(
|
broadcast_stanza(Host, Options, States, Stanza),
|
||||||
fun(#pubsub_state{stateid = {LJID, _},
|
broadcast_by_caps(Host, Node, Type, Stanza),
|
||||||
subscription = Subscription}) ->
|
{result, true};
|
||||||
case is_to_delivered(LJID, Subscription, PresenceDelivery) of
|
_ ->
|
||||||
true ->
|
{result, false}
|
||||||
DestJIDs = case BroadcastAll of
|
end
|
||||||
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
|
|
||||||
end,
|
end,
|
||||||
transaction(Host, Node, Action, sync_dirty).
|
transaction(Host, Node, Action, sync_dirty).
|
||||||
|
|
||||||
%% ItemAttrs is a list of tuples:
|
broadcast_retract_items(Host, Node, ItemIds) ->
|
||||||
%% For example: [{"id", ItemId}]
|
broadcast_retract_items(Host, Node, ItemIds, false).
|
||||||
make_stanza(Node, ItemAttrs, Payload) ->
|
broadcast_retract_items(Host, Node, ItemIds, ForceNotify) ->
|
||||||
#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) ->
|
|
||||||
Action =
|
Action =
|
||||||
fun(#pubsub_node{options = Options, type = Type}) ->
|
fun(#pubsub_node{options = Options, type = Type}) ->
|
||||||
case node_call(Type, get_states, [Host, Node]) of
|
case (get_option(Options, notify_retract) or ForceNotify) of
|
||||||
{error, _} -> {result, false};
|
true ->
|
||||||
{result, []} -> {result, false};
|
case node_call(Type, get_states, [Host, Node]) of
|
||||||
{result, States} ->
|
{result, []} ->
|
||||||
Notify = case ForceNotify of
|
{result, false};
|
||||||
true -> true;
|
{result, States} ->
|
||||||
_ -> get_option(Options, notify_retract)
|
RetractEls = lists:map(
|
||||||
end,
|
fun(ItemId) ->
|
||||||
ItemAttrs = case ItemId of
|
ItemAttrs = case ItemId of
|
||||||
"" -> [];
|
"" -> [];
|
||||||
_ -> [#xmlattr{name = 'id', value = ItemId}]
|
_ -> [#xmlattr{name = 'id', value = ItemId}]
|
||||||
end,
|
end,
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = ItemAttrs}
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
|
end, ItemIds),
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
|
Stanza = event_stanza(
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = ItemAttrs}]}]}]},
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}],
|
||||||
case Notify of
|
children = RetractEls}]),
|
||||||
true ->
|
broadcast_stanza(Host, Options, States, Stanza),
|
||||||
lists:foreach(
|
broadcast_by_caps(Host, Node, Type, Stanza),
|
||||||
fun(#pubsub_state{stateid = {{U, S, R}, _},
|
{result, true};
|
||||||
subscription = Subscription}) ->
|
_ ->
|
||||||
if (Subscription /= none) and
|
{result, false}
|
||||||
(Subscription /= pending) ->
|
end;
|
||||||
ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), Stanza};
|
_ ->
|
||||||
true ->
|
{result, false}
|
||||||
ok
|
end
|
||||||
end
|
|
||||||
end, States),
|
|
||||||
broadcast_by_caps(Host, Node, Type, Stanza),
|
|
||||||
{result, true};
|
|
||||||
false ->
|
|
||||||
{result, false}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
transaction(Host, Node, Action, sync_dirty).
|
transaction(Host, Node, Action, sync_dirty).
|
||||||
|
|
||||||
broadcast_purge_node(Host, Node) ->
|
broadcast_purge_node(Host, Node) ->
|
||||||
Action =
|
Action =
|
||||||
fun(#pubsub_node{options = Options, type = Type}) ->
|
fun(#pubsub_node{options = Options, type = Type}) ->
|
||||||
case node_call(Type, get_states, [Host, Node]) of
|
case get_option(Options, notify_retract) of
|
||||||
{error, _} -> {result, false};
|
true ->
|
||||||
{result, []} -> {result, false};
|
case node_call(Type, get_states, [Host, Node]) of
|
||||||
{result, States} ->
|
{result, []} ->
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
{result, false};
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
|
{result, States} ->
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}]}]}]},
|
Stanza = event_stanza(
|
||||||
case get_option(Options, notify_retract) of
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}]}]),
|
||||||
true ->
|
broadcast_stanza(Host, Options, States, Stanza),
|
||||||
lists:foreach(
|
broadcast_by_caps(Host, Node, Type, Stanza),
|
||||||
fun(#pubsub_state{stateid = {{U, S, R},_},
|
{result, true};
|
||||||
subscription = Subscription}) ->
|
_ ->
|
||||||
if (Subscription /= none) and
|
{result, false}
|
||||||
(Subscription /= pending) ->
|
end;
|
||||||
ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), Stanza};
|
_ ->
|
||||||
true ->
|
{result, false}
|
||||||
ok
|
end
|
||||||
end
|
|
||||||
end, States),
|
|
||||||
broadcast_by_caps(Host, Node, Type, Stanza),
|
|
||||||
{result, true};
|
|
||||||
false ->
|
|
||||||
{result, false}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
transaction(Host, Node, Action, sync_dirty).
|
transaction(Host, Node, Action, sync_dirty).
|
||||||
|
|
||||||
broadcast_removed_node(Host, Removed) ->
|
broadcast_removed_node(Host, Node) ->
|
||||||
lists:foreach(
|
Action =
|
||||||
fun(Node) ->
|
fun(#pubsub_node{options = Options, type = Type}) ->
|
||||||
Action =
|
case get_option(Options, notify_delete) of
|
||||||
fun(#pubsub_node{options = Options, type = Type}) ->
|
true ->
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
case node_call(Type, get_states, [Host, Node]) of
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
|
{result, []} ->
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}]}]}]},
|
{result, false};
|
||||||
case get_option(Options, notify_delete) of
|
{result, States} ->
|
||||||
true ->
|
Stanza = event_stanza(
|
||||||
case node_call(Type, get_states, [Host, Node]) of
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}]}]),
|
||||||
{result, States} ->
|
broadcast_stanza(Host, Options, States, Stanza),
|
||||||
lists:foreach(
|
broadcast_by_caps(Host, Node, Type, Stanza),
|
||||||
fun(#pubsub_state{stateid = {{U, S, R}, _},
|
{result, true};
|
||||||
subscription = Subscription}) ->
|
_ ->
|
||||||
if (Subscription /= none) and
|
{result, false}
|
||||||
(Subscription /= pending) ->
|
end;
|
||||||
ejabberd_router ! {route, service_jid(Host), jlib:make_jid(U, S, R), Stanza};
|
_ ->
|
||||||
true ->
|
{result, false}
|
||||||
ok
|
end
|
||||||
end
|
end,
|
||||||
end, States),
|
transaction(Host, Node, Action, sync_dirty).
|
||||||
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_config_notification(Host, Node, Lang) ->
|
broadcast_config_notification(Host, Node, Lang) ->
|
||||||
Action =
|
Action =
|
||||||
fun(#pubsub_node{options = Options, owners = Owners, type = Type}) ->
|
fun(#pubsub_node{options = Options, owners = Owners, type = Type}) ->
|
||||||
case node_call(Type, get_states, [Host, Node]) of
|
case get_option(Options, notify_config) of
|
||||||
{error, _} -> {result, false};
|
true ->
|
||||||
{result, []} -> {result, false};
|
case node_call(Type, get_states, [Host, Node]) of
|
||||||
{result, States} ->
|
{result, []} ->
|
||||||
case get_option(Options, notify_config) of
|
{result, false};
|
||||||
true ->
|
{result, States} ->
|
||||||
PresenceDelivery = get_option(Options, presence_based_delivery),
|
Content = case get_option(Options, deliver_payloads) of
|
||||||
Content = case get_option(Options, deliver_payloads) of
|
true ->
|
||||||
true ->
|
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [#xmlattr{name = 'type', value = "form"}], children =
|
||||||
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [#xmlattr{name = 'type', value = "form"}], children =
|
get_configure_xfields(Type, Options, Lang, Owners)}];
|
||||||
get_configure_xfields(Type, Options, Lang, Owners)}];
|
false ->
|
||||||
false ->
|
[]
|
||||||
[]
|
end,
|
||||||
end,
|
Stanza = event_stanza(
|
||||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT, name = 'message', children =
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'event', children =
|
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [#xmlattr{name = 'id', value = "configuration"}], children =
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = [#xmlattr{name = 'node', value = node_to_string(Node)}], children =
|
Content}]}]),
|
||||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [#xmlattr{name = 'id', value = "configuration"}], children =
|
broadcast_stanza(Host, Options, States, Stanza),
|
||||||
Content}]}]}]},
|
broadcast_by_caps(Host, Node, Type, Stanza),
|
||||||
lists:foreach(
|
{result, true};
|
||||||
fun(#pubsub_state{stateid = {{U, S, R} = LJID, _},
|
_ ->
|
||||||
subscription = Subscription}) ->
|
{result, false}
|
||||||
case is_to_delivered(LJID, Subscription, PresenceDelivery) of
|
end;
|
||||||
true ->
|
_ ->
|
||||||
ejabberd_router ! {route, service_jid(Host), exmpp_jid:make_jid(U, S, R), Stanza};
|
{result, false}
|
||||||
false ->
|
end
|
||||||
ok
|
|
||||||
end
|
|
||||||
end, States),
|
|
||||||
broadcast_by_caps(Host, Node, Type, Stanza),
|
|
||||||
{result, true};
|
|
||||||
_ ->
|
|
||||||
{result, false}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
transaction(Host, Node, Action, sync_dirty).
|
transaction(Host, Node, Action, sync_dirty).
|
||||||
|
|
||||||
%TODO: simplify broadcast_* using a generic function like that:
|
broadcast_stanza(Host, NodeOpts, States, Stanza) ->
|
||||||
%broadcast(Host, Node, Fun) ->
|
PresenceDelivery = get_option(NodeOpts, presence_based_delivery),
|
||||||
% transaction(fun() ->
|
BroadcastAll = get_option(NodeOpts, broadcast_all_resources),
|
||||||
% case tree_call(Host, get_node, [Host, Node]) of
|
From = service_jid(Host),
|
||||||
% #pubsub_node{options = Options, owners = Owners, type = Type} ->
|
lists:foreach(fun(#pubsub_state{stateid = {LJID, _}, subscription = Subs}) ->
|
||||||
% case node_call(Type, get_states, [Host, Node]) of
|
case is_to_deliver(LJID, Subs, PresenceDelivery) of
|
||||||
% {error, _} -> {result, false};
|
true ->
|
||||||
% {result, []} -> {result, false};
|
JIDs = case BroadcastAll of
|
||||||
% {result, States} ->
|
true -> ejabberd_sm:get_user_resources(element(1, LJID), element(2, LJID));
|
||||||
% lists:foreach(fun(#pubsub_state{stateid = {JID,_}, subscription = Subscription}) ->
|
false -> [LJID]
|
||||||
% Fun(Host, Node, Options, Owners, JID, Subscription)
|
end,
|
||||||
% end, States),
|
lists:foreach(fun({U, S, R}) ->
|
||||||
% {result, true}
|
ejabberd_router ! {route, From, exmpp_jlib:make_jid(U, S, R), Stanza}
|
||||||
% end;
|
end, JIDs);
|
||||||
% Other ->
|
false ->
|
||||||
% Other
|
ok
|
||||||
% end
|
end
|
||||||
% end, sync_dirty).
|
end, States).
|
||||||
|
|
||||||
|
|
||||||
%% broadcast Stanza to all contacts of the user that are advertising
|
%% broadcast Stanza to all contacts of the user that are advertising
|
||||||
%% interest in this kind of Node.
|
%% interest in this kind of Node.
|
||||||
@ -2724,18 +2658,18 @@ select_type(ServerHost, Host, Node) ->
|
|||||||
|
|
||||||
features() ->
|
features() ->
|
||||||
[
|
[
|
||||||
%"access-authorize", % OPTIONAL
|
%TODO "access-authorize", % OPTIONAL
|
||||||
"access-open", % OPTIONAL this relates to access_model option in node_default
|
"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-presence", % OPTIONAL this relates to access_model option in node_pep
|
||||||
%"access-roster", % OPTIONAL
|
%TODO "access-roster", % OPTIONAL
|
||||||
%"access-whitelist", % OPTIONAL
|
%TODO "access-whitelist", % OPTIONAL
|
||||||
% see plugin "auto-create", % OPTIONAL
|
% see plugin "auto-create", % OPTIONAL
|
||||||
% see plugin "auto-subscribe", % RECOMMENDED
|
% see plugin "auto-subscribe", % RECOMMENDED
|
||||||
"collections", % RECOMMENDED
|
"collections", % RECOMMENDED
|
||||||
"config-node", % RECOMMENDED
|
"config-node", % RECOMMENDED
|
||||||
"create-and-configure", % RECOMMENDED
|
"create-and-configure", % RECOMMENDED
|
||||||
% see plugin "create-nodes", % RECOMMENDED
|
% see plugin "create-nodes", % RECOMMENDED
|
||||||
%TODO "delete-any", % OPTIONAL
|
% see plugin "delete-any", % RECOMMENDED
|
||||||
% see plugin "delete-nodes", % RECOMMENDED
|
% see plugin "delete-nodes", % RECOMMENDED
|
||||||
% see plugin "filtered-notifications", % RECOMMENDED
|
% see plugin "filtered-notifications", % RECOMMENDED
|
||||||
%TODO "get-pending", % OPTIONAL
|
%TODO "get-pending", % OPTIONAL
|
||||||
|
@ -160,6 +160,7 @@ features() ->
|
|||||||
["create-nodes",
|
["create-nodes",
|
||||||
"auto-create",
|
"auto-create",
|
||||||
"delete-nodes",
|
"delete-nodes",
|
||||||
|
"delete-any",
|
||||||
"instant-nodes",
|
"instant-nodes",
|
||||||
"manage-subscriptions",
|
"manage-subscriptions",
|
||||||
"modify-affiliations",
|
"modify-affiliations",
|
||||||
@ -222,8 +223,7 @@ create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
|
|||||||
%% @doc <p></p>
|
%% @doc <p></p>
|
||||||
create_node(Host, Node, Owner) ->
|
create_node(Host, Node, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
mnesia:write(#pubsub_state{stateid = {OwnerKey, {Host, Node}},
|
set_state(#pubsub_state{stateid = {OwnerKey, {Host, Node}}, affiliation = owner}),
|
||||||
affiliation = owner, subscription = none}),
|
|
||||||
{result, {default, broadcast}}.
|
{result, {default, broadcast}}.
|
||||||
|
|
||||||
|
|
||||||
@ -236,12 +236,8 @@ delete_node(Host, Removed) ->
|
|||||||
fun(Node) ->
|
fun(Node) ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(#pubsub_state{stateid = StateId, items = Items}) ->
|
fun(#pubsub_state{stateid = StateId, items = Items}) ->
|
||||||
lists:foreach(
|
del_items(Host, Node, Items),
|
||||||
fun(ItemId) ->
|
del_state(StateId)
|
||||||
mnesia:delete(
|
|
||||||
{pubsub_item, {ItemId, {Host, Node}}})
|
|
||||||
end, Items),
|
|
||||||
mnesia:delete({pubsub_state, StateId})
|
|
||||||
end,
|
end,
|
||||||
mnesia:match_object(
|
mnesia:match_object(
|
||||||
#pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}))
|
#pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}))
|
||||||
@ -283,13 +279,9 @@ delete_node(Host, Removed) ->
|
|||||||
%% <p>In the default plugin module, the record is unchanged.</p>
|
%% <p>In the default plugin module, the record is unchanged.</p>
|
||||||
subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
|
subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
|
||||||
SendLast, PresenceSubscription, RosterGroup) ->
|
SendLast, PresenceSubscription, RosterGroup) ->
|
||||||
Authorized = (jlib:short_prepd_bare_jid(Sender) == jlib:short_bare_jid(Subscriber)),
|
SubscriberKey = jlib:short_prepd_bare_jid(Subscriber),
|
||||||
% TODO add some acl check for Authorized ?
|
Authorized = (jlib:short_prepd_bare_jid(Sender) == SubscriberKey),
|
||||||
State = case get_state(Host, Node, Subscriber) of
|
State = get_state(Host, Node, SubscriberKey),
|
||||||
{error, 'item-not-found'} ->
|
|
||||||
#pubsub_state{stateid = {Subscriber, {Host, Node}}}; % TODO: bug on Key ?
|
|
||||||
{result, S} -> S
|
|
||||||
end,
|
|
||||||
#pubsub_state{affiliation = Affiliation,
|
#pubsub_state{affiliation = Affiliation,
|
||||||
subscription = Subscription} = State,
|
subscription = Subscription} = State,
|
||||||
if
|
if
|
||||||
@ -325,7 +317,6 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
|
|||||||
if
|
if
|
||||||
AccessModel == authorize ->
|
AccessModel == authorize ->
|
||||||
pending;
|
pending;
|
||||||
%%TODO Affiliation == none -> ?
|
|
||||||
%%NeedConfiguration ->
|
%%NeedConfiguration ->
|
||||||
%% unconfigured
|
%% unconfigured
|
||||||
true ->
|
true ->
|
||||||
@ -353,43 +344,29 @@ subscribe_node(Host, Node, Sender, Subscriber, AccessModel,
|
|||||||
%% Reason = mod_pubsub:stanzaError()
|
%% Reason = mod_pubsub:stanzaError()
|
||||||
%% @doc <p>Unsubscribe the <tt>Subscriber</tt> from the <tt>Node</tt>.</p>
|
%% @doc <p>Unsubscribe the <tt>Subscriber</tt> from the <tt>Node</tt>.</p>
|
||||||
unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) ->
|
unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) ->
|
||||||
SenderKey = jlib:short_prepd_jid(Sender),
|
SubscriberKey = jlib:short_prepd_bare_jid(Subscriber),
|
||||||
Match = jlib:short_prepd_bare_jid(Sender) == jlib:short_bare_jid(Subscriber),
|
Authorized = (jlib:short_prepd_bare_jid(Sender) == SubscriberKey),
|
||||||
Authorized = case Match of
|
State = get_state(Host, Node, SubscriberKey),
|
||||||
true ->
|
if
|
||||||
true;
|
%% Entity did not specify SubID
|
||||||
false ->
|
%%SubID == "", ?? ->
|
||||||
case get_state(Host, Node, SenderKey) of % TODO: bug on Key ?
|
%% {error, ?ERR_EXTENDED('bad-request', "subid-required")};
|
||||||
{result, #pubsub_state{affiliation=owner}} -> true;
|
%% Invalid subscription identifier
|
||||||
_ -> false
|
%%InvalidSubID ->
|
||||||
end
|
%% {error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")};
|
||||||
end,
|
%% Requesting entity is not a subscriber
|
||||||
case get_state(Host, Node, Subscriber) of
|
State#pubsub_state.subscription == none ->
|
||||||
{error, 'item-not-found'} ->
|
|
||||||
%% Requesting entity is not a subscriber
|
|
||||||
{error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")};
|
{error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")};
|
||||||
{result, State} ->
|
%% Requesting entity is prohibited from unsubscribing entity
|
||||||
if
|
(not Authorized) and (State#pubsub_state.affiliation =/= owner) ->
|
||||||
%% Entity did not specify SubID
|
{error, 'forbidden'};
|
||||||
%%SubID == "", ?? ->
|
%% Was just subscriber, remove the record
|
||||||
%% {error, ?ERR_EXTENDED('bad-request', "subid-required")};
|
State#pubsub_state.affiliation == none ->
|
||||||
%% Invalid subscription identifier
|
mnesia:delete({pubsub_state, State#pubsub_state.stateid}),
|
||||||
%%InvalidSubID ->
|
{result, default};
|
||||||
%% {error, ?ERR_EXTENDED('not-acceptable', "invalid-subid")};
|
true ->
|
||||||
%% Requesting entity is not a subscriber
|
set_state(State#pubsub_state{subscription = none}),
|
||||||
State#pubsub_state.subscription == none ->
|
{result, default}
|
||||||
{error, ?ERR_EXTENDED('unexpected-request', "not-subscribed")};
|
|
||||||
%% Requesting entity is prohibited from unsubscribing entity
|
|
||||||
not Authorized ->
|
|
||||||
{error, '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
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
%% @spec (Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
||||||
@ -433,10 +410,7 @@ unsubscribe_node(Host, Node, Sender, Subscriber, _SubId) ->
|
|||||||
%% <p>In the default plugin module, the record is unchanged.</p>
|
%% <p>In the default plugin module, the record is unchanged.</p>
|
||||||
publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
||||||
PublisherKey = jlib:short_prepd_bare_jid(Publisher),
|
PublisherKey = jlib:short_prepd_bare_jid(Publisher),
|
||||||
State = case get_state(Host, Node, PublisherKey) of
|
State = get_state(Host, Node, PublisherKey),
|
||||||
{error, 'item-not-found'} -> #pubsub_state{stateid={PublisherKey, {Host, Node}}};
|
|
||||||
{result, S} -> S
|
|
||||||
end,
|
|
||||||
#pubsub_state{affiliation = Affiliation,
|
#pubsub_state{affiliation = Affiliation,
|
||||||
subscription = Subscription} = State,
|
subscription = Subscription} = State,
|
||||||
if
|
if
|
||||||
@ -448,17 +422,17 @@ publish_item(Host, Node, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
|
|||||||
%% Entity does not have sufficient privileges to publish to node
|
%% Entity does not have sufficient privileges to publish to node
|
||||||
{error, 'forbidden'};
|
{error, 'forbidden'};
|
||||||
true ->
|
true ->
|
||||||
PubId = {PublisherKey, now()},
|
PubId = {PublisherKey, now()}, %% TODO, uses {now(),PublisherKey} for sorting (EJAB-824)
|
||||||
%% TODO: check creation, presence, roster (EJAB-663)
|
%% TODO: check creation, presence, roster (EJAB-663)
|
||||||
Item = case get_item(Host, Node, ItemId) of
|
Item = case get_item(Host, Node, ItemId) of
|
||||||
{error, 'item-not-found'} ->
|
{result, OldItem} ->
|
||||||
|
OldItem#pubsub_item{modification = PubId,
|
||||||
|
payload = Payload};
|
||||||
|
_ ->
|
||||||
#pubsub_item{itemid = {ItemId, {Host, Node}},
|
#pubsub_item{itemid = {ItemId, {Host, Node}},
|
||||||
creation = PubId,
|
creation = PubId,
|
||||||
modification = PubId,
|
modification = PubId,
|
||||||
payload = Payload};
|
payload = Payload}
|
||||||
{result, OldItem} ->
|
|
||||||
OldItem#pubsub_item{modification = PubId,
|
|
||||||
payload = Payload}
|
|
||||||
end,
|
end,
|
||||||
Items = [ItemId | State#pubsub_state.items--[ItemId]],
|
Items = [ItemId | State#pubsub_state.items--[ItemId]],
|
||||||
{result, {NI, OI}} = remove_extra_items(
|
{result, {NI, OI}} = remove_extra_items(
|
||||||
@ -492,9 +466,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) ->
|
|||||||
NewItems = lists:sublist(ItemIds, MaxItems),
|
NewItems = lists:sublist(ItemIds, MaxItems),
|
||||||
OldItems = lists:nthtail(length(NewItems), ItemIds),
|
OldItems = lists:nthtail(length(NewItems), ItemIds),
|
||||||
%% Remove extra items:
|
%% Remove extra items:
|
||||||
lists:foreach(fun(ItemId) ->
|
del_items(Host, Node, OldItems),
|
||||||
mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})
|
|
||||||
end, OldItems),
|
|
||||||
%% Return the new items list:
|
%% Return the new items list:
|
||||||
{result, {NewItems, OldItems}}.
|
{result, {NewItems, OldItems}}.
|
||||||
|
|
||||||
@ -510,12 +482,7 @@ remove_extra_items(Host, Node, MaxItems, ItemIds) ->
|
|||||||
%% or a publisher.</p>
|
%% or a publisher.</p>
|
||||||
delete_item(Host, Node, Publisher, ItemId) ->
|
delete_item(Host, Node, Publisher, ItemId) ->
|
||||||
PublisherKey = jlib:short_prepd_bare_jid(Publisher),
|
PublisherKey = jlib:short_prepd_bare_jid(Publisher),
|
||||||
State = case get_state(Host, Node, PublisherKey) of
|
State = get_state(Host, Node, PublisherKey),
|
||||||
{error, 'item-not-found'} ->
|
|
||||||
#pubsub_state{stateid = {PublisherKey, {Host, Node}}};
|
|
||||||
{result, S} ->
|
|
||||||
S
|
|
||||||
end,
|
|
||||||
#pubsub_state{affiliation = Affiliation, items = Items} = State,
|
#pubsub_state{affiliation = Affiliation, items = Items} = State,
|
||||||
Allowed = (Affiliation == publisher) orelse (Affiliation == owner)
|
Allowed = (Affiliation == publisher) orelse (Affiliation == owner)
|
||||||
orelse case get_item(Host, Node, ItemId) of
|
orelse case get_item(Host, Node, ItemId) of
|
||||||
@ -529,7 +496,7 @@ delete_item(Host, Node, Publisher, ItemId) ->
|
|||||||
true ->
|
true ->
|
||||||
case get_item(Host, Node, ItemId) of
|
case get_item(Host, Node, ItemId) of
|
||||||
{result, _} ->
|
{result, _} ->
|
||||||
mnesia:delete({pubsub_item, {ItemId, {Host, Node}}}),
|
del_item(Host, Node, ItemId),
|
||||||
NewItems = lists:delete(ItemId, Items),
|
NewItems = lists:delete(ItemId, Items),
|
||||||
set_state(State#pubsub_state{items = NewItems}),
|
set_state(State#pubsub_state{items = NewItems}),
|
||||||
{result, {default, broadcast}};
|
{result, {default, broadcast}};
|
||||||
@ -548,16 +515,14 @@ delete_item(Host, Node, Publisher, ItemId) ->
|
|||||||
purge_node(Host, Node, Owner) ->
|
purge_node(Host, Node, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
case get_state(Host, Node, OwnerKey) of
|
case get_state(Host, Node, OwnerKey) of
|
||||||
{result, #pubsub_state{items = Items, affiliation = owner}} ->
|
#pubsub_state{items = Items, affiliation = owner} ->
|
||||||
lists:foreach(fun(ItemId) ->
|
lists:foreach(fun(ItemId) ->
|
||||||
mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})
|
mnesia:delete({pubsub_item, {ItemId, {Host, Node}}})
|
||||||
end, Items),
|
end, Items),
|
||||||
{result, {default, broadcast}};
|
{result, {default, broadcast}};
|
||||||
{result, _} ->
|
|
||||||
%% Entity is not owner
|
|
||||||
{error, 'forbidden'};
|
|
||||||
_ ->
|
_ ->
|
||||||
{error, 'item-not-found'}
|
%% Entity is not owner
|
||||||
|
{error, 'forbidden'}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (Host, JID) -> [{Node,Affiliation}]
|
%% @spec (Host, JID) -> [{Node,Affiliation}]
|
||||||
@ -573,8 +538,7 @@ purge_node(Host, Node, Owner) ->
|
|||||||
get_entity_affiliations(Host, Owner) ->
|
get_entity_affiliations(Host, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
States = mnesia:match_object(
|
States = mnesia:match_object(
|
||||||
#pubsub_state{stateid = {OwnerKey, {Host, '_'}},
|
#pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}),
|
||||||
_ = '_'}),
|
|
||||||
Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) ->
|
Tr = fun(#pubsub_state{stateid = {_, {_, N}}, affiliation = A}) ->
|
||||||
{N, A}
|
{N, A}
|
||||||
end,
|
end,
|
||||||
@ -582,8 +546,7 @@ get_entity_affiliations(Host, Owner) ->
|
|||||||
|
|
||||||
get_node_affiliations(Host, Node) ->
|
get_node_affiliations(Host, Node) ->
|
||||||
States = mnesia:match_object(
|
States = mnesia:match_object(
|
||||||
#pubsub_state{stateid = {'_', {Host, Node}},
|
#pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}),
|
||||||
_ = '_'}),
|
|
||||||
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) ->
|
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, affiliation = A}) ->
|
||||||
{J, A}
|
{J, A}
|
||||||
end,
|
end,
|
||||||
@ -591,22 +554,13 @@ get_node_affiliations(Host, Node) ->
|
|||||||
|
|
||||||
get_affiliation(Host, Node, Owner) ->
|
get_affiliation(Host, Node, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
Affiliation = case get_state(Host, Node, OwnerKey) of
|
State = get_state(Host, Node, OwnerKey),
|
||||||
{result, #pubsub_state{affiliation = A}} -> A;
|
{result, State#pubsub_state.affiliation}.
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
{result, Affiliation}.
|
|
||||||
|
|
||||||
set_affiliation(Host, Node, Owner, Affiliation) ->
|
set_affiliation(Host, Node, Owner, Affiliation) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
Record = case get_state(Host, Node, OwnerKey) of
|
State = get_state(Host, Node, OwnerKey),
|
||||||
{error, 'item-not-found'} ->
|
set_state(State#pubsub_state{affiliation = Affiliation}),
|
||||||
#pubsub_state{stateid = {OwnerKey, {Host, Node}},
|
|
||||||
affiliation = Affiliation};
|
|
||||||
{result, State} ->
|
|
||||||
State#pubsub_state{affiliation = Affiliation}
|
|
||||||
end,
|
|
||||||
set_state(Record),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% @spec (Host, Owner) -> [{Node,Subscription}]
|
%% @spec (Host, Owner) -> [{Node,Subscription}]
|
||||||
@ -623,8 +577,7 @@ set_affiliation(Host, Node, Owner, Affiliation) ->
|
|||||||
get_entity_subscriptions(Host, Owner) ->
|
get_entity_subscriptions(Host, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
States = mnesia:match_object(
|
States = mnesia:match_object(
|
||||||
#pubsub_state{stateid = {OwnerKey, {Host, '_'}},
|
#pubsub_state{stateid = {OwnerKey, {Host, '_'}}, _ = '_'}),
|
||||||
_ = '_'}),
|
|
||||||
Tr = fun(#pubsub_state{stateid = {_, {_, N}}, subscription = S}) ->
|
Tr = fun(#pubsub_state{stateid = {_, {_, N}}, subscription = S}) ->
|
||||||
{N, S}
|
{N, S}
|
||||||
end,
|
end,
|
||||||
@ -632,8 +585,7 @@ get_entity_subscriptions(Host, Owner) ->
|
|||||||
|
|
||||||
get_node_subscriptions(Host, Node) ->
|
get_node_subscriptions(Host, Node) ->
|
||||||
States = mnesia:match_object(
|
States = mnesia:match_object(
|
||||||
#pubsub_state{stateid = {'_', {Host, Node}},
|
#pubsub_state{stateid = {'_', {Host, Node}}, _ = '_'}),
|
||||||
_ = '_'}),
|
|
||||||
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, subscription = S}) ->
|
Tr = fun(#pubsub_state{stateid = {J, {_, _}}, subscription = S}) ->
|
||||||
{J, S}
|
{J, S}
|
||||||
end,
|
end,
|
||||||
@ -641,22 +593,13 @@ get_node_subscriptions(Host, Node) ->
|
|||||||
|
|
||||||
get_subscription(Host, Node, Owner) ->
|
get_subscription(Host, Node, Owner) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
Subscription = case get_state(Host, Node, OwnerKey) of
|
State = get_state(Host, Node, OwnerKey),
|
||||||
{result, #pubsub_state{subscription = S}} -> S;
|
{result, State#pubsub_state.subscription}.
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
{result, Subscription}.
|
|
||||||
|
|
||||||
set_subscription(Host, Node, Owner, Subscription) ->
|
set_subscription(Host, Node, Owner, Subscription) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
Record = case get_state(Host, Node, OwnerKey) of
|
State = get_state(Host, Node, OwnerKey),
|
||||||
{error, 'item-not-found'} ->
|
set_state(State#pubsub_state{subscription = Subscription}),
|
||||||
#pubsub_state{stateid = {OwnerKey, {Host, Node}},
|
|
||||||
subscription = Subscription};
|
|
||||||
{result, State} ->
|
|
||||||
State#pubsub_state{subscription = Subscription}
|
|
||||||
end,
|
|
||||||
set_state(Record),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% @spec (Host, Node) -> [States] | []
|
%% @spec (Host, Node) -> [States] | []
|
||||||
@ -685,11 +628,10 @@ get_states(Host, Node) ->
|
|||||||
%% State = mod_pubsub:pubsubItems()
|
%% State = mod_pubsub:pubsubItems()
|
||||||
%% @doc <p>Returns a state (one state list), given its reference.</p>
|
%% @doc <p>Returns a state (one state list), given its reference.</p>
|
||||||
get_state(Host, Node, JID) ->
|
get_state(Host, Node, JID) ->
|
||||||
case mnesia:read({pubsub_state, {JID, {Host, Node}}}) of
|
StateId = {JID, {Host, Node}},
|
||||||
[State] when is_record(State, pubsub_state) ->
|
case mnesia:read({pubsub_state, StateId}) of
|
||||||
{result, State};
|
[State] when is_record(State, pubsub_state) -> State
|
||||||
_ ->
|
_ -> #pubsub_state{stateid=StateId}
|
||||||
{error, 'item-not-found'}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (State) -> ok | {error, Reason::stanzaError()}
|
%% @spec (State) -> ok | {error, Reason::stanzaError()}
|
||||||
@ -700,6 +642,12 @@ set_state(State) when is_record(State, pubsub_state) ->
|
|||||||
set_state(_) ->
|
set_state(_) ->
|
||||||
{error, 'internal-server-error'}.
|
{error, 'internal-server-error'}.
|
||||||
|
|
||||||
|
%% @spec (StateId) -> ok | {error, Reason::stanzaError()}
|
||||||
|
%% StateId = mod_pubsub:pubsubStateId()
|
||||||
|
%% @doc <p>Delete a state from database.</p>
|
||||||
|
del_state(StateId) ->
|
||||||
|
mnesia:delete({pubsub_state, StateId}).
|
||||||
|
|
||||||
%% @spec (Host, Node) -> [Items] | []
|
%% @spec (Host, Node) -> [Items] | []
|
||||||
%% Host = mod_pubsub:host()
|
%% Host = mod_pubsub:host()
|
||||||
%% Node = mod_pubsub:pubsubNode()
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
@ -719,11 +667,9 @@ get_items(Host, Node, _From) ->
|
|||||||
#pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}),
|
#pubsub_item{itemid = {'_', {Host, Node}}, _ = '_'}),
|
||||||
{result, Items}.
|
{result, Items}.
|
||||||
get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
get_items(Host, Node, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
||||||
{Affiliation, Subscription} =
|
State = get_state(Host, Node, jlib:short_prepd_bare_jid(JID)),
|
||||||
case get_state(Host, Node, jlib:short_prepd_bare_jid(JID)) of
|
#pubsub_state{affiliation = Affiliation,
|
||||||
{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S};
|
subscription = Subscription} = State,
|
||||||
_ -> {none, none}
|
|
||||||
end,
|
|
||||||
Subscribed = not ((Subscription == none) or (Subscription == pending)),
|
Subscribed = not ((Subscription == none) or (Subscription == pending)),
|
||||||
if
|
if
|
||||||
%%SubID == "", ?? ->
|
%%SubID == "", ?? ->
|
||||||
@ -771,11 +717,9 @@ get_item(Host, Node, ItemId) ->
|
|||||||
{error, 'item-not-found'}
|
{error, 'item-not-found'}
|
||||||
end.
|
end.
|
||||||
get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
get_item(Host, Node, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
|
||||||
{Affiliation, Subscription} =
|
State = get_state(Host, Node, jlib:short_prepd_bare_jid(JID)),
|
||||||
case get_state(Host, Node, jlib:short_prepd_bare_jid(JID)) of
|
#pubsub_state{affiliation = Affiliation,
|
||||||
{result, #pubsub_state{affiliation = A, subscription = S}} -> {A, S};
|
subscription = Subscription} = State,
|
||||||
_ -> {none, none}
|
|
||||||
end,
|
|
||||||
Subscribed = not ((Subscription == none) or (Subscription == pending)),
|
Subscribed = not ((Subscription == none) or (Subscription == pending)),
|
||||||
if
|
if
|
||||||
%%SubID == "", ?? ->
|
%%SubID == "", ?? ->
|
||||||
@ -817,6 +761,18 @@ set_item(Item) when is_record(Item, pubsub_item) ->
|
|||||||
set_item(_) ->
|
set_item(_) ->
|
||||||
{error, 'internal-server-error'}.
|
{error, 'internal-server-error'}.
|
||||||
|
|
||||||
|
%% @spec (ItemId) -> ok | {error, Reason::stanzaError()}
|
||||||
|
%% Host = mod_pubsub:host()
|
||||||
|
%% Node = mod_pubsub:pubsubNode()
|
||||||
|
%% ItemId = string()
|
||||||
|
%% @doc <p>Delete an item from database.</p>
|
||||||
|
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).
|
||||||
|
|
||||||
%% @doc <p>Return the name of the node if known: Default is to return
|
%% @doc <p>Return the name of the node if known: Default is to return
|
||||||
%% node id.</p>
|
%% node id.</p>
|
||||||
get_item_name(_Host, _Node, Id) ->
|
get_item_name(_Host, _Node, Id) ->
|
||||||
|
@ -182,15 +182,9 @@ get_affiliation(_Host, Node, Owner) ->
|
|||||||
|
|
||||||
set_affiliation(_Host, Node, Owner, Affiliation) ->
|
set_affiliation(_Host, Node, Owner, Affiliation) ->
|
||||||
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
OwnerKey = jlib:short_prepd_bare_jid(Owner),
|
||||||
Record = case get_state(OwnerKey, Node, OwnerKey) of
|
State = get_state(OwnerKey, Node, OwnerKey),
|
||||||
{error, 'item-not-found'} ->
|
set_state(State#pubsub_state{affiliation = Affiliation}),
|
||||||
#pubsub_state{stateid = {OwnerKey, {OwnerKey, Node}},
|
ok.
|
||||||
affiliation = Affiliation};
|
|
||||||
{result, State} ->
|
|
||||||
State#pubsub_state{affiliation = Affiliation}
|
|
||||||
end,
|
|
||||||
set_state(Record),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
get_entity_subscriptions(_Host, _Owner) ->
|
get_entity_subscriptions(_Host, _Owner) ->
|
||||||
{result, []}.
|
{result, []}.
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
%%% lserver = string(),
|
%%% lserver = string(),
|
||||||
%%% lresource = 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 affiliation() = none | owner | publisher | outcast.
|
||||||
%%% @type subscription() = none | pending | unconfigured | subscribed.
|
%%% @type subscription() = none | pending | unconfigured | subscribed.
|
||||||
@ -81,7 +81,7 @@
|
|||||||
%%% nodeid = {Host::host(), Node::pubsubNode()},
|
%%% nodeid = {Host::host(), Node::pubsubNode()},
|
||||||
%%% parentid = {Host::host(), Node::pubsubNode()},
|
%%% parentid = {Host::host(), Node::pubsubNode()},
|
||||||
%%% type = nodeType(),
|
%%% type = nodeType(),
|
||||||
%%% owners = [usr()],
|
%%% owners = [ljid()],
|
||||||
%%% options = [nodeOption()]}.
|
%%% options = [nodeOption()]}.
|
||||||
%%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
|
%%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
|
||||||
%%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
%%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
|
||||||
@ -94,7 +94,7 @@
|
|||||||
}).
|
}).
|
||||||
|
|
||||||
%%% @type pubsubState() = #pubsub_state{
|
%%% @type pubsubState() = #pubsub_state{
|
||||||
%%% stateid = {jid(), {Host::host(), Node::pubsubNode()}},
|
%%% stateid = {ljid(), {Host::host(), Node::pubsubNode()}},
|
||||||
%%% items = [ItemId::string()],
|
%%% items = [ItemId::string()],
|
||||||
%%% affiliation = affiliation(),
|
%%% affiliation = affiliation(),
|
||||||
%%% subscription = subscription()}.
|
%%% subscription = subscription()}.
|
||||||
@ -108,8 +108,8 @@
|
|||||||
|
|
||||||
%% @type pubsubItem() = #pubsub_item{
|
%% @type pubsubItem() = #pubsub_item{
|
||||||
%% itemid = {ItemId::string(), {Host::host(),Node::pubsubNode()}},
|
%% itemid = {ItemId::string(), {Host::host(),Node::pubsubNode()}},
|
||||||
%% creation = {JID::jid(), now()},
|
%% creation = {ljid(), now()},
|
||||||
%% modification = {JID::jid(), now()},
|
%% modification = {ljid(), now()},
|
||||||
%% payload = XMLContent::string()}.
|
%% payload = XMLContent::string()}.
|
||||||
%%% <p>This is the format of the <tt>published items</tt> table. The type of the
|
%%% <p>This is the format of the <tt>published items</tt> table. The type of the
|
||||||
%%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
|
%%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
|
||||||
|
Loading…
Reference in New Issue
Block a user