mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Updated to trunk r2532, fix subscription managment (by owner)
SVN Revision: 2536
This commit is contained in:
parent
fcf66c2e75
commit
a1a6469ed0
@ -35,7 +35,7 @@
|
||||
%%% Functions concerning configuration should be rewritten.
|
||||
%%%
|
||||
%%% Support for subscription-options and multi-subscribe features was
|
||||
%%% added by Brian Cully <bjc@kublai.com>. Subscriptions and options are
|
||||
%%% added by Brian Cully (bjc AT kublai.com). Subscriptions and options are
|
||||
%%% stored in the pubsub_subscription table, with a link to them provided
|
||||
%%% by the subscriptions field of pubsub_state. For information on
|
||||
%%% subscription-options and mulit-subscribe see XEP-0060 sections 6.1.6,
|
||||
@ -272,9 +272,9 @@ terminate_plugins(Host, ServerHost, Plugins, TreePlugin) ->
|
||||
TreePlugin:terminate(Host, ServerHost),
|
||||
ok.
|
||||
|
||||
init_nodes(_Host, _ServerHost) ->
|
||||
%create_node(Host, ServerHost, ["home"], service_jid(Host), "hometree"),
|
||||
%create_node(Host, ServerHost, ["home", ServerHost], service_jid(Host), "hometree"),
|
||||
init_nodes(Host, ServerHost) ->
|
||||
create_node(Host, ServerHost, ["home"], service_jid(Host), "hometree"),
|
||||
create_node(Host, ServerHost, ["home", ServerHost], service_jid(Host), "hometree"),
|
||||
ok.
|
||||
|
||||
update_node_database(Host, ServerHost) ->
|
||||
@ -284,7 +284,7 @@ update_node_database(Host, ServerHost) ->
|
||||
[host_node, host_parent, info] ->
|
||||
?INFO_MSG("upgrade node pubsub tables",[]),
|
||||
F = fun() ->
|
||||
lists:foldl(
|
||||
{Result, LastIdx} = lists:foldl(
|
||||
fun({pubsub_node, NodeId, ParentId, {nodeinfo, Items, Options, Entities}}, {RecList, NodeIdx}) ->
|
||||
ItemsList =
|
||||
lists:foldl(
|
||||
@ -328,7 +328,9 @@ update_node_database(Host, ServerHost) ->
|
||||
RecList], NodeIdx + 1}
|
||||
end, {[], 1},
|
||||
mnesia:match_object(
|
||||
{pubsub_node, {Host, '_'}, '_', '_'}))
|
||||
{pubsub_node, {Host, '_'}, '_', '_'})),
|
||||
mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []}),
|
||||
Result
|
||||
end,
|
||||
{atomic, NewRecords} = mnesia:transaction(F),
|
||||
{atomic, ok} = mnesia:delete_table(pubsub_node),
|
||||
@ -341,11 +343,9 @@ update_node_database(Host, ServerHost) ->
|
||||
end,
|
||||
case mnesia:transaction(FNew) of
|
||||
{atomic, Result} ->
|
||||
?INFO_MSG("Pubsub node tables updated correctly: ~p",
|
||||
[Result]);
|
||||
?INFO_MSG("Pubsub node tables updated correctly: ~p", [Result]);
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Problem updating Pubsub node tables:~n~p",
|
||||
[Reason])
|
||||
?ERROR_MSG("Problem updating Pubsub node tables:~n~p", [Reason])
|
||||
end;
|
||||
[nodeid, parentid, type, owners, options] ->
|
||||
F = fun({pubsub_node, NodeId, {_, Parent}, Type, Owners, Options}) ->
|
||||
@ -359,7 +359,7 @@ update_node_database(Host, ServerHost) ->
|
||||
end,
|
||||
mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]),
|
||||
FNew = fun() ->
|
||||
lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) ->
|
||||
LastIdx = lists:foldl(fun(#pubsub_node{nodeid = NodeId} = PubsubNode, NodeIdx) ->
|
||||
mnesia:write(PubsubNode#pubsub_node{id = NodeIdx}),
|
||||
lists:foreach(fun(#pubsub_state{stateid = StateId} = State) ->
|
||||
{JID, _} = StateId,
|
||||
@ -379,15 +379,15 @@ update_node_database(Host, ServerHost) ->
|
||||
end, 1, mnesia:match_object(
|
||||
{pubsub_node, {Host, '_'}, '_', '_', '_', '_', '_'})
|
||||
++ mnesia:match_object(
|
||||
{pubsub_node, {{'_', ServerHost, '_'}, '_'}, '_', '_', '_', '_', '_'}))
|
||||
{pubsub_node, {{'_', ServerHost, '_'}, '_'}, '_', '_', '_', '_', '_'})),
|
||||
mnesia:write(#pubsub_index{index = node, last = LastIdx, free = []})
|
||||
end,
|
||||
case mnesia:transaction(FNew) of
|
||||
{atomic, Result} ->
|
||||
?INFO_MSG("Pubsub node tables updated correctly: ~p",
|
||||
[Result]);
|
||||
rename_default_nodeplugin(),
|
||||
?INFO_MSG("Pubsub node tables updated correctly: ~p", [Result]);
|
||||
{aborted, Reason} ->
|
||||
?ERROR_MSG("Problem updating Pubsub node tables:~n~p",
|
||||
[Reason])
|
||||
?ERROR_MSG("Problem updating Pubsub node tables:~n~p", [Reason])
|
||||
end;
|
||||
[nodeid, id, parent, type, owners, options] ->
|
||||
F = fun({pubsub_node, NodeId, Id, Parent, Type, Owners, Options}) ->
|
||||
@ -399,10 +399,16 @@ update_node_database(Host, ServerHost) ->
|
||||
owners = Owners,
|
||||
options = Options}
|
||||
end,
|
||||
mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]);
|
||||
mnesia:transform_table(pubsub_node, F, [nodeid, id, parents, type, owners, options]),
|
||||
rename_default_nodeplugin();
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
rename_default_nodeplugin() ->
|
||||
lists:foreach(fun(Node) ->
|
||||
mnesia:dirty_write(Node#pubsub_node{type = "hometree"})
|
||||
end, mnesia:dirty_match_object(#pubsub_node{type = "default", _ = '_'})).
|
||||
|
||||
update_state_database(_Host, _ServerHost) ->
|
||||
case catch mnesia:table_info(pubsub_state, attributes) of
|
||||
@ -467,7 +473,7 @@ send_loop(State) ->
|
||||
lists:foreach(fun(PType) ->
|
||||
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, JID]),
|
||||
lists:foreach(
|
||||
fun({Node, subscribed, SubJID}) ->
|
||||
fun({Node, subscribed, _, SubJID}) ->
|
||||
if (SubJID == LJID) or (SubJID == BJID) ->
|
||||
#pubsub_node{options = Options, type = Type, id = NodeId} = Node,
|
||||
case get_option(Options, send_last_published_item) of
|
||||
@ -637,13 +643,12 @@ disco_sm_features(Acc, From, To, Node, _Lang) ->
|
||||
end.
|
||||
|
||||
disco_sm_items(Acc, From, To, <<>>, _Lang) ->
|
||||
%% TODO, use iq_disco_items(Host, [], From)
|
||||
Host = exmpp_jid:prep_domain_as_list(To),
|
||||
LJID = jlib:short_prepd_bare_jid(To),
|
||||
case tree_action(Host, get_subnodes, [Host, [], From]) of
|
||||
[] ->
|
||||
Acc;
|
||||
Nodes ->
|
||||
SBJID = jlib:short_prepd_bare_jid(To),
|
||||
Items = case Acc of
|
||||
{result, I} -> I;
|
||||
_ -> []
|
||||
@ -651,8 +656,7 @@ disco_sm_items(Acc, From, To, <<>>, _Lang) ->
|
||||
NodeItems = lists:map(
|
||||
fun(#pubsub_node{nodeid = {_, Node}}) ->
|
||||
#xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs =
|
||||
[?XMLATTR('jid', exmpp_jid:to_binary(LJID)),
|
||||
?XMLATTR('node', node_to_string(Node))]}
|
||||
[?XMLATTR('jid', exmpp_jid:to_binary(SBJID)) | nodeAttr(Node)]}
|
||||
end, Nodes),
|
||||
{result, NodeItems ++ Items}
|
||||
end;
|
||||
@ -675,7 +679,7 @@ disco_sm_items(Acc, From, To, NodeB, _Lang) ->
|
||||
fun(#pubsub_item{itemid = {Id, _}}) ->
|
||||
%% "jid" is required by XEP-0030, and
|
||||
%% "node" is forbidden by XEP-0060.
|
||||
{result, Name} = node_action(Host, Node, get_item_name, [NodeId, Id]),
|
||||
{result, Name} = node_call(Type, get_item_name, [Host, Node, Id]),
|
||||
#xmlel{ns = ?NS_DISCO_ITEMS, name = 'item', attrs =
|
||||
[?XMLATTR('jid', exmpp_jid:to_binary(LJID)),
|
||||
?XMLATTR('name', Name)]}
|
||||
@ -695,16 +699,16 @@ disco_sm_items(Acc, From, To, NodeB, _Lang) ->
|
||||
%%
|
||||
|
||||
presence_probe(JID, JID, Pid) ->
|
||||
{U, S, R} = jlib:short_prepd_jid(JID),
|
||||
{User, Server, Resource} = jlib:short_prepd_jid(JID),
|
||||
Host = exmpp_jid:prep_domain_as_list(JID),
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:cast(Proc, {presence, JID, Pid}),
|
||||
gen_server:cast(Proc, {presence, U, S, [R], JID});
|
||||
gen_server:cast(Proc, {presence, User, Server, [Resource], JID});
|
||||
presence_probe(Peer, JID, _Pid) ->
|
||||
{U, S, R} = jlib:short_prepd_jid(Peer),
|
||||
{User, Server, Resource} = jlib:short_prepd_jid(Peer),
|
||||
Host = exmpp_jid:prep_domain_as_list(JID),
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:cast(Proc, {presence, U, S, [R], JID}).
|
||||
gen_server:cast(Proc, {presence, User, Server, [Resource], JID}).
|
||||
|
||||
%% -------
|
||||
%% subscription hooks handling functions
|
||||
@ -782,7 +786,7 @@ handle_cast({remove_user, LUser, LServer}, State) ->
|
||||
lists:foreach(fun(PType) ->
|
||||
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Owner]),
|
||||
lists:foreach(fun
|
||||
({#pubsub_node{nodeid = {H, N}}, subscribed, JID}) ->
|
||||
({#pubsub_node{nodeid = {H, N}}, subscribed, _, JID}) ->
|
||||
unsubscribe_node(H, N, Owner, JID, all);
|
||||
(_) ->
|
||||
ok
|
||||
@ -803,7 +807,7 @@ handle_cast({unsubscribe, Subscriber, Owner}, State) ->
|
||||
lists:foreach(fun(PType) ->
|
||||
{result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Subscriber]),
|
||||
lists:foreach(fun
|
||||
({Node, subscribed, JID}) ->
|
||||
({Node, subscribed, _, JID}) ->
|
||||
#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId} = Node,
|
||||
case get_option(Options, access_model) of
|
||||
presence ->
|
||||
@ -909,8 +913,7 @@ do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
|
||||
#iq{type = get, ns = ?NS_DISCO_INFO,
|
||||
payload = SubEl, lang = Lang} ->
|
||||
QAttrs = SubEl#xmlel.attrs,
|
||||
Node = exmpp_xml:get_attribute_from_list_as_list(QAttrs,
|
||||
'node', ""),
|
||||
Node = exmpp_xml:get_attribute_from_list_as_list(QAttrs, 'node', ""),
|
||||
ServerHostB = list_to_binary(ServerHost),
|
||||
Info = ejabberd_hooks:run_fold(
|
||||
disco_info, ServerHostB, [],
|
||||
@ -1524,15 +1527,20 @@ find_authorization_response(Packet) ->
|
||||
%% Host = mod_pubsub:host()
|
||||
%% JID = jlib:jid()
|
||||
%% SNode = string()
|
||||
%% Subscription = atom()
|
||||
%% Subscription = atom() | {atom(), mod_pubsub:subid)}
|
||||
%% Plugins = [Plugin::string()]
|
||||
%% @doc Send a message to JID with the supplied Subscription
|
||||
send_authorization_approval(Host, JID, SNode, Subscription) ->
|
||||
SubAttrs = case Subscription of
|
||||
{S, SID} -> [?XMLATTR('subscription', subscription_to_string(S)),
|
||||
?XMLATTR('subid', SID)];
|
||||
S -> [?XMLATTR('subscription', subscription_to_string(S))]
|
||||
end,
|
||||
Stanza = event_stanza(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'subscription', attrs =
|
||||
[?XMLATTR('node', SNode),
|
||||
?XMLATTR('jid', exmpp_jid:to_binary(JID)),
|
||||
?XMLATTR('subscription', subscription_to_string(Subscription))]}]),
|
||||
?XMLATTR('jid', exmpp_jid:to_binary(JID))] ++ SubAttrs
|
||||
}]),
|
||||
ejabberd_router ! {route, service_jid(Host), JID, Stanza}.
|
||||
|
||||
handle_authorization_response(Host, From, To, Packet, XFields) ->
|
||||
@ -1551,14 +1559,14 @@ handle_authorization_response(Host, From, To, Packet, XFields) ->
|
||||
"true" -> true;
|
||||
_ -> false
|
||||
end,
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId, owners = Owners}) ->
|
||||
Action = fun(#pubsub_node{type = Type, owners = Owners, id = NodeId}) ->
|
||||
IsApprover = lists:member(jlib:short_prepd_bare_jid(From), Owners),
|
||||
{result, Subscriptions} = node_call(Type, get_subscriptions, [NodeId, Subscriber]),
|
||||
if
|
||||
not IsApprover ->
|
||||
{error, 'forbidden'};
|
||||
true ->
|
||||
update_auth(Host, SNode, Type, NodeId,
|
||||
update_auth(Host, SNode, Type, NodeId,
|
||||
Subscriber, Allow,
|
||||
Subscriptions)
|
||||
end
|
||||
@ -1760,8 +1768,8 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
|
||||
{result, Result};
|
||||
Error ->
|
||||
%% in case we change transaction to sync_dirty...
|
||||
%% node_call(Type, delete_node, [NodeId]),
|
||||
%% tree_call(Host, delete_node, [NodeId]),
|
||||
%% node_call(Type, delete_node, [Host, Node]),
|
||||
%% tree_call(Host, delete_node, [Host, Node]),
|
||||
Error
|
||||
end;
|
||||
Error ->
|
||||
@ -1786,10 +1794,14 @@ delete_node(_Host, [], _Owner) ->
|
||||
{error, 'not-allowed'};
|
||||
delete_node(Host, Node, Owner) ->
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
|
||||
SubsByDepth = get_collection_subscriptions(Host, Node),
|
||||
case node_call(Type, get_affiliation, [NodeId, Owner]) of
|
||||
{result, owner} ->
|
||||
Removed = tree_call(Host, delete_node, [NodeId]),
|
||||
node_call(Type, delete_node, [Removed]);
|
||||
Removed = tree_call(Host, delete_node, [Host, Node]),
|
||||
case node_call(Type, delete_node, [Removed]) of
|
||||
{result, Res} -> {result, {SubsByDepth, Res}};
|
||||
Error -> Error
|
||||
end;
|
||||
_ ->
|
||||
%% Entity is not an owner
|
||||
{error, 'forbidden'}
|
||||
@ -1797,27 +1809,27 @@ delete_node(Host, Node, Owner) ->
|
||||
end,
|
||||
Reply = [],
|
||||
case transaction(Host, Node, Action, transaction) of
|
||||
{result, {_, {Result, broadcast, Removed}}} ->
|
||||
lists:foreach(fun({RNode, RSubscriptions}) ->
|
||||
{result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} ->
|
||||
lists:foreach(fun({RNode, _RSubscriptions}) ->
|
||||
{RH, RN} = RNode#pubsub_node.nodeid,
|
||||
NodeId = RNode#pubsub_node.id,
|
||||
Type = RNode#pubsub_node.type,
|
||||
Options = RNode#pubsub_node.options,
|
||||
broadcast_removed_node(RH, RN, NodeId, Type, Options, RSubscriptions),
|
||||
broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth),
|
||||
unset_cached_item(RH, NodeId)
|
||||
end, Removed),
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, {_, {Result, _Removed}}} ->
|
||||
{result, {_, {_, {Result, _Removed}}}} ->
|
||||
case Result of
|
||||
default -> {result, Reply};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, {_, default}} ->
|
||||
{result, {_, {_, default}}} ->
|
||||
{result, Reply};
|
||||
{result, {_, Result}} ->
|
||||
{result, {_, {_, Result}}} ->
|
||||
{result, Result};
|
||||
Error ->
|
||||
Error
|
||||
@ -1858,7 +1870,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
Features = features(Type),
|
||||
SubscribeFeature = lists:member("subscribe", Features),
|
||||
OptionsFeature = lists:member("subscription-options", Features),
|
||||
HasOptions = not (SubOpts == []),
|
||||
HasOptions = not (SubOpts == []),
|
||||
SubscribeConfig = get_option(Options, subscribe),
|
||||
AccessModel = get_option(Options, access_model),
|
||||
SendLast = get_option(Options, send_last_published_item),
|
||||
@ -1898,35 +1910,37 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
|
||||
end,
|
||||
Reply = fun(Subscription) ->
|
||||
%% TODO, this is subscription-notification, should depends on node features
|
||||
SubAttrs = case Subscription of
|
||||
{subscribed, SubId} ->
|
||||
[{"subscription", subscription_to_string(subscribed)},
|
||||
{"subid", SubId}];
|
||||
Other ->
|
||||
[{"subscription", subscription_to_string(Other)}]
|
||||
end,
|
||||
Fields =
|
||||
[?XMLATTR('node', node_to_string(Node)),
|
||||
?XMLATTR('jid', exmpp_jid:to_binary(Subscriber)),
|
||||
?XMLATTR('subscription', subscription_to_string(Subscription))],
|
||||
[ ?XMLATTR('jid', exmpp_jid:to_binary(Subscriber)) | SubAttrs],
|
||||
#xmlel{ns = ?NS_PUBSUB, name = 'pubsub', children =
|
||||
[#xmlel{ns = ?NS_PUBSUB, name = 'subscription', attrs =
|
||||
case Subscription of
|
||||
subscribed -> [?XMLATTR('subid', SubId)|Fields];
|
||||
_ -> Fields
|
||||
end}]}
|
||||
[#xmlel{ns = ?NS_PUBSUB, name = 'subscription', attrs = Fields}]}
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {TNode, {Result, subscribed, send_last}}} ->
|
||||
{result, {TNode, {Result, subscribed, SubId, send_last}}} ->
|
||||
NodeId = TNode#pubsub_node.id,
|
||||
Type = TNode#pubsub_node.type,
|
||||
send_items(Host, Node, NodeId, Type, Subscriber, last),
|
||||
case Result of
|
||||
default -> {result, Reply(subscribed)};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, {TNode, {Result, Subscription}}} ->
|
||||
case Subscription of
|
||||
pending -> send_authorization_request(TNode, Subscriber);
|
||||
_ -> ok
|
||||
end,
|
||||
case Result of
|
||||
default -> {result, Reply(Subscription)};
|
||||
default -> {result, Reply({subscribed, SubId})};
|
||||
_ -> {result, Result}
|
||||
end;
|
||||
{result, {_TNode, {default, subscribed, SubId}}} ->
|
||||
{result, Reply({subscribed, SubId})};
|
||||
{result, {_TNode, {Result, subscribed, _SubId}}} ->
|
||||
{result, Result};
|
||||
{result, {TNode, {default, pending, _SubId}}} ->
|
||||
send_authorization_request(TNode, Subscriber),
|
||||
{result, Reply(pending)};
|
||||
{result, {TNode, {Result, pending}}} ->
|
||||
send_authorization_request(TNode, Subscriber),
|
||||
{result, Result};
|
||||
{result, {_, Result}} ->
|
||||
%% this case should never occure anyway
|
||||
{result, Result};
|
||||
@ -1992,7 +2006,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
|
||||
Features = features(Type),
|
||||
PublishFeature = lists:member("publish", Features),
|
||||
PublishModel = get_option(Options, publish_model),
|
||||
MaxItems = max_items(Options),
|
||||
MaxItems = max_items(Host, Options),
|
||||
DeliverPayloads = get_option(Options, deliver_payloads),
|
||||
PersistItems = get_option(Options, persist_items),
|
||||
PayloadCount = payload_xmlelements(Payload),
|
||||
@ -2276,7 +2290,7 @@ get_item(Host, Node, ItemId) ->
|
||||
end.
|
||||
|
||||
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
|
||||
%% Host = host()
|
||||
%% Host = pubsubHost()
|
||||
%% Node = pubsubNode()
|
||||
%% NodeId = pubsubNodeId()
|
||||
%% Type = pubsubNodeType()
|
||||
@ -2651,6 +2665,7 @@ get_subscriptions(Host, Node, JID) ->
|
||||
{result, {_, Subscriptions}} ->
|
||||
Entities = lists:flatmap(
|
||||
fun({_, none}) -> [];
|
||||
({_, pending, _}) -> [];
|
||||
({{AU, AS, AR}, Subscription}) ->
|
||||
[#xmlel{ns = ?NS_PUBSUB_OWNER, name = 'subscription', attrs =
|
||||
[?XMLATTR('jid', exmpp_jid:to_binary(AU, AS, AR)),
|
||||
@ -2681,19 +2696,20 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
|
||||
#xmlel{name = 'subscription', attrs = Attrs} ->
|
||||
JID = try
|
||||
exmpp_jid:parse(
|
||||
exmpp_xml:get_attribute_from_list_as_list(Attrs, 'jid', ""))
|
||||
exmpp_xml:get_attribute_from_list(Attrs, 'jid', ""))
|
||||
catch
|
||||
_:_ ->
|
||||
error
|
||||
end,
|
||||
Subscription = string_to_subscription(
|
||||
exmpp_xml:get_attribute_from_list_as_list(Attrs, 'subscription', false)),
|
||||
SubId = exmpp_xml:get_attribute_from_list_as_list(Attrs, "subid", false),
|
||||
if
|
||||
(JID == error) or
|
||||
(Subscription == false) ->
|
||||
error;
|
||||
true ->
|
||||
[{jlib:short_prepd_jid(JID), Subscription} | Acc]
|
||||
[{JID, Subscription, SubId} | Acc]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -2702,18 +2718,38 @@ set_subscriptions(Host, Node, From, EntitiesEls) ->
|
||||
error ->
|
||||
{error, 'bad-request'};
|
||||
_ ->
|
||||
Action = fun(#pubsub_node{type = Type, id = NodeId, owners = Owners}) ->
|
||||
case lists:member(Owner, Owners) of
|
||||
true ->
|
||||
lists:foreach(
|
||||
fun({JID, Subscription}) ->
|
||||
node_call(Type, set_subscription, [NodeId, JID, Subscription])
|
||||
end, Entities),
|
||||
{result, []};
|
||||
_ ->
|
||||
{error, 'forbidden'}
|
||||
end
|
||||
end,
|
||||
Notify = fun(JID, Sub, _SubId) ->
|
||||
Stanza = #xmlel{ns = ?NS_JABBER_CLIENT,
|
||||
name = 'message',
|
||||
children =
|
||||
[#xmlel{ns = ?NS_PUBSUB,
|
||||
name = 'pubsub',
|
||||
children =
|
||||
[#xmlel{ns = ?NS_PUBSUB,
|
||||
name = 'subscription',
|
||||
attrs = [?XMLATTR('jid', exmpp_jid:to_binary(JID)),
|
||||
?XMLATTR('subsription', subscription_to_string(Sub)) | nodeAttr(Node)]}]}]},
|
||||
|
||||
ejabberd_router ! {route, service_jid(Host), JID, Stanza}
|
||||
end,
|
||||
Action = fun(#pubsub_node{owners = Owners, type = Type, id = NodeId}) ->
|
||||
case lists:member(Owner, Owners) of
|
||||
true ->
|
||||
Result = lists:foldl(fun({JID, Subscription, SubId}, Acc) ->
|
||||
|
||||
case node_call(Type, set_subscriptions, [NodeId, JID, Subscription, SubId]) of
|
||||
{error, Err} -> [{error, Err} | Acc];
|
||||
_ -> Notify(JID, Subscription, SubId), Acc
|
||||
end
|
||||
end, [], Entities),
|
||||
case Result of
|
||||
[] -> {result, []};
|
||||
_ -> {error, 'not-acceptable'}
|
||||
end;
|
||||
_ ->
|
||||
{error, 'forbidden'}
|
||||
end
|
||||
end,
|
||||
case transaction(Host, Node, Action, sync_dirty) of
|
||||
{result, {_, Result}} -> {result, Result};
|
||||
Other -> Other
|
||||
@ -2799,7 +2835,7 @@ service_jid(Host) ->
|
||||
_ -> exmpp_jid:make(Host)
|
||||
end.
|
||||
|
||||
%% @spec (LJID, PresenceDelivery) -> boolean()
|
||||
%% @spec (LJID, NotifyType, Depth, NodeOptions, SubOptions) -> boolean()
|
||||
%% LJID = jid()
|
||||
%% NotifyType = items | nodes
|
||||
%% Depth = integer()
|
||||
@ -2889,13 +2925,13 @@ broadcast_publish_item(Host, Node, NodeId, Type, Options, Removed, ItemId, _From
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds) ->
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, false).
|
||||
broadcast_retract_items(_Host, _Node, _NodeId, _Type, _Options, [], _ForceNotify) ->
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds) ->
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, false).
|
||||
broadcast_retract_items(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _ForceNotify) ->
|
||||
{result, false};
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, ForceNotify) ->
|
||||
%broadcast(Host, Node, NodeId, Options, notify_retract, ForceNotify, 'retract', RetractEls)
|
||||
case (get_option(Options, notify_retract) or ForceNotify) of
|
||||
broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNotify) ->
|
||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_retract, ForceNotify, 'retract', RetractEls)
|
||||
case (get_option(NodeOptions, notify_retract) or ForceNotify) of
|
||||
true ->
|
||||
case get_collection_subscriptions(Host, Node) of
|
||||
[] ->
|
||||
@ -2905,7 +2941,7 @@ broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, ForceNotify)
|
||||
Stanza = event_stanza(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node), children =
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'retract', attrs = itemAttr(ItemId)} || ItemId <- ItemIds]}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, Options, SubsByDepth, items, Stanza),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, items, Stanza),
|
||||
{result, true};
|
||||
_ ->
|
||||
{result, false}
|
||||
@ -2914,9 +2950,9 @@ broadcast_retract_items(Host, Node, NodeId, Type, Options, ItemIds, ForceNotify)
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_purge_node(Host, Node, NodeId, Type, Options) ->
|
||||
%broadcast(Host, Node, NodeId, Options, notify_retract, false, 'purge', [])
|
||||
case get_option(Options, notify_retract) of
|
||||
broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
|
||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_retract, false, 'purge', [])
|
||||
case get_option(NodeOptions, notify_retract) of
|
||||
true ->
|
||||
case get_collection_subscriptions(Host, Node) of
|
||||
[] ->
|
||||
@ -2924,7 +2960,7 @@ broadcast_purge_node(Host, Node, NodeId, Type, Options) ->
|
||||
SubsByDepth when is_list(SubsByDepth) ->
|
||||
Stanza = event_stanza(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'purge', attrs = nodeAttr(Node)}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, Options, SubsByDepth, nodes, Stanza),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza),
|
||||
{result, true};
|
||||
_ ->
|
||||
{result, false}
|
||||
@ -2933,43 +2969,43 @@ broadcast_purge_node(Host, Node, NodeId, Type, Options) ->
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_removed_node(Host, Node, NodeId, Type, Options, Subs) ->
|
||||
%broadcast(Host, Node, NodeId, Options, notify_delete, false, 'delete', [])
|
||||
case get_option(Options, notify_delete) of
|
||||
broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
|
||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_delete, false, 'delete', [])
|
||||
case get_option(NodeOptions, notify_delete) of
|
||||
true ->
|
||||
case Subs of
|
||||
case SubsByDepth of
|
||||
[] ->
|
||||
{result, false};
|
||||
_ ->
|
||||
Stanza = event_stanza(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'delete', attrs = nodeAttr(Node)}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, Options, Subs, nodes, Stanza),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza),
|
||||
{result, true}
|
||||
end;
|
||||
_ ->
|
||||
{result, false}
|
||||
end.
|
||||
|
||||
broadcast_config_notification(Host, Node, NodeId, Type, Options, Lang) ->
|
||||
%broadcast(Host, Node, NodeId, Options, notify_config, false, 'items', ConfigEls)
|
||||
case get_option(Options, notify_config) of
|
||||
broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
|
||||
%broadcast(Host, Node, NodeId, NodeOptions, notify_config, false, 'items', ConfigEls)
|
||||
case get_option(NodeOptions, notify_config) of
|
||||
true ->
|
||||
case get_collection_subscriptions(Host, Node) of
|
||||
[] ->
|
||||
{result, false};
|
||||
SubsByDepth when is_list(SubsByDepth) ->
|
||||
Content = case get_option(Options, deliver_payloads) of
|
||||
Content = case get_option(NodeOptions, deliver_payloads) of
|
||||
true ->
|
||||
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x', attrs = [?XMLATTR('type', <<"form">>)], children =
|
||||
get_configure_xfields(Type, Options, Lang, [])}];
|
||||
false ->
|
||||
[]
|
||||
get_configure_xfields(Type, NodeOptions, Lang, [])}];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Stanza = event_stanza(
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'items', attrs = nodeAttr(Node), children =
|
||||
[#xmlel{ns = ?NS_PUBSUB_EVENT, name = 'item', attrs = [?XMLATTR('id', <<"configuration">>)], children =
|
||||
Content}]}]),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, Options, SubsByDepth, nodes, Stanza),
|
||||
broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza),
|
||||
{result, true};
|
||||
_ ->
|
||||
{result, false}
|
||||
@ -2995,22 +3031,24 @@ get_node_subs(#pubsub_node{type = Type,
|
||||
|
||||
get_options_for_subs(_Host, Node, NodeID, Subs) ->
|
||||
lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
|
||||
{result, #pubsub_subscription{options = Options}} = pubsub_subscription:get_subscription(JID, NodeID, SubID),
|
||||
[{JID, Node, Options} | Acc];
|
||||
case pubsub_subscription:get_subscription(JID, NodeID, SubID) of
|
||||
{result, #pubsub_subscription{options = Options}} -> [{JID, Node, Options} | Acc];
|
||||
_ -> Acc
|
||||
end;
|
||||
(_, Acc) ->
|
||||
Acc
|
||||
end, [], Subs).
|
||||
|
||||
% TODO: merge broadcast code that way
|
||||
%broadcast(Host, Node, NodeId, Type, Options, Feature, Force, ElName, SubEls) ->
|
||||
% case (get_option(Options, Feature) or Force) of
|
||||
%broadcast(Host, Node, NodeId, Type, NodeOptions, Feature, Force, ElName, SubEls) ->
|
||||
% case (get_option(NodeOptions, Feature) or Force) of
|
||||
% true ->
|
||||
% case node_action(Host, Type, get_node_subscriptions, [NodeId]) of
|
||||
% {result, []} ->
|
||||
% {result, false};
|
||||
% {result, Subs} ->
|
||||
% Stanza = event_stanza([{xmlelement, ElName, [{"node", node_to_string(Node)}], SubEls}]),
|
||||
% broadcast_stanza(Host, Node, Type, Options, Subs, Stanza),
|
||||
% broadcast_stanza(Host, Node, Type, NodeOptions, SubOpts, Stanza),
|
||||
% {result, true};
|
||||
% _ ->
|
||||
% {result, false}
|
||||
@ -3116,7 +3154,7 @@ collate_subs_by_jid(SubsByDepth) ->
|
||||
end,
|
||||
lists:keystore(JID, 1, Acc, {JID, [Node | OldNodes]})
|
||||
end, [], SubsByDepth).
|
||||
|
||||
|
||||
%% If we don't know the resource, just pick first if any
|
||||
%% If no resource available, check if caps anyway (remote online)
|
||||
user_resources(User, Server) ->
|
||||
@ -3198,7 +3236,8 @@ node_options(Type) ->
|
||||
Result
|
||||
end.
|
||||
|
||||
%% @spec (Options) -> MaxItems
|
||||
%% @spec (Host, Options) -> MaxItems
|
||||
%% Host = host()
|
||||
%% Options = [Option]
|
||||
%% Option = {Key::atom(), Value::term()}
|
||||
%% MaxItems = integer() | unlimited
|
||||
@ -3208,7 +3247,7 @@ node_options(Type) ->
|
||||
%% @todo In practice, the current data structure means that we cannot manage
|
||||
%% millions of items on a given node. This should be addressed in a new
|
||||
%% version.
|
||||
max_items(Options) ->
|
||||
max_items(Host, Options) ->
|
||||
case get_option(Options, persist_items) of
|
||||
true ->
|
||||
case get_option(Options, max_items) of
|
||||
@ -3218,8 +3257,13 @@ max_items(Options) ->
|
||||
end;
|
||||
false ->
|
||||
case get_option(Options, send_last_published_item) of
|
||||
never -> 0;
|
||||
_ -> 1
|
||||
never ->
|
||||
0;
|
||||
_ ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> 0;
|
||||
false -> 1
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
@ -3306,8 +3350,10 @@ set_configure(Host, Node, From, Els, Lang) ->
|
||||
end,
|
||||
case set_xoption(XData, OldOpts) of
|
||||
NewOpts when is_list(NewOpts) ->
|
||||
tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]),
|
||||
{result, ok};
|
||||
case tree_call(Host, set_node, [N#pubsub_node{options = NewOpts}]) of
|
||||
ok -> {result, ok};
|
||||
Err -> Err
|
||||
end;
|
||||
Err ->
|
||||
Err
|
||||
end
|
||||
@ -3412,35 +3458,45 @@ set_xoption([{"pubsub#type", Value} | Opts], NewOpts) ->
|
||||
?SET_STRING_XOPT(type, Value);
|
||||
set_xoption([{"pubsub#body_xslt", Value} | Opts], NewOpts) ->
|
||||
?SET_STRING_XOPT(body_xslt, Value);
|
||||
set_xoption([{"pubsub#collection", Value} | Opts], NewOpts) ->
|
||||
NewValue = [string_to_node(V) || V <- Value],
|
||||
?SET_LIST_XOPT(collection, NewValue);
|
||||
set_xoption([{"pubsub#node", [Value]} | Opts], NewOpts) ->
|
||||
NewValue = string_to_node(Value),
|
||||
?SET_LIST_XOPT(node, NewValue);
|
||||
set_xoption([_ | Opts], NewOpts) ->
|
||||
% skip unknown field
|
||||
set_xoption(Opts, NewOpts).
|
||||
|
||||
%%%% last item cache handling
|
||||
|
||||
is_last_item_cache_enabled({_, ServerHost, _}) ->
|
||||
is_last_item_cache_enabled(ServerHost);
|
||||
is_last_item_cache_enabled(Host) ->
|
||||
case ets:lookup(gen_mod:get_module_proc(Host, config), last_item_cache) of
|
||||
[{last_item_cache, true}] -> true;
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
set_cached_item({_, ServerHost, _}, NodeId, ItemId, Payload) ->
|
||||
set_cached_item(ServerHost, NodeId, ItemId, Payload);
|
||||
set_cached_item(Host, NodeId, ItemId, Payload) ->
|
||||
case ets:lookup(gen_mod:get_module_proc(Host, config), last_item_cache) of
|
||||
[{last_item_cache, true}] ->
|
||||
ets:insert(gen_mod:get_module_proc(Host, last_items), {NodeId, {ItemId, Payload}});
|
||||
_ ->
|
||||
ok
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:insert(gen_mod:get_module_proc(Host, last_items), {NodeId, {ItemId, Payload}});
|
||||
_ -> ok
|
||||
end.
|
||||
unset_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
unset_cached_item(ServerHost, NodeId);
|
||||
unset_cached_item(Host, NodeId) ->
|
||||
case ets:lookup(gen_mod:get_module_proc(Host, config), last_item_cache) of
|
||||
[{last_item_cache, true}] ->
|
||||
ets:delete(gen_mod:get_module_proc(Host, last_items), NodeId);
|
||||
_ ->
|
||||
ok
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true -> ets:delete(gen_mod:get_module_proc(Host, last_items), NodeId);
|
||||
_ -> ok
|
||||
end.
|
||||
get_cached_item({_, ServerHost, _}, NodeId) ->
|
||||
get_cached_item(ServerHost, NodeId);
|
||||
get_cached_item(Host, NodeId) ->
|
||||
case ets:lookup(gen_mod:get_module_proc(Host, config), last_item_cache) of
|
||||
[{last_item_cache, true}] ->
|
||||
case is_last_item_cache_enabled(Host) of
|
||||
true ->
|
||||
case ets:lookup(gen_mod:get_module_proc(Host, last_items), NodeId) of
|
||||
[{NodeId, {ItemId, Payload}}] ->
|
||||
#pubsub_item{itemid = {ItemId, NodeId}, payload = Payload};
|
||||
@ -3477,12 +3533,12 @@ select_type(ServerHost, Host, Node, Type) ->
|
||||
true -> SelectedType;
|
||||
false -> hd(ConfiguredTypes)
|
||||
end.
|
||||
select_type(ServerHost, Host, Node) ->
|
||||
select_type(ServerHost, Host, Node) ->
|
||||
select_type(ServerHost, Host, Node, hd(plugins(ServerHost))).
|
||||
|
||||
features() ->
|
||||
[
|
||||
%TODO "access-authorize", % OPTIONAL
|
||||
% see plugin "access-authorize", % OPTIONAL
|
||||
"access-open", % OPTIONAL this relates to access_model option in node_hometree
|
||||
"access-presence", % OPTIONAL this relates to access_model option in node_pep
|
||||
%TODO "access-roster", % OPTIONAL
|
||||
@ -3496,7 +3552,7 @@ features() ->
|
||||
% see plugin "delete-items", % RECOMMENDED
|
||||
% see plugin "delete-nodes", % RECOMMENDED
|
||||
% see plugin "filtered-notifications", % RECOMMENDED
|
||||
%TODO "get-pending", % OPTIONAL
|
||||
% see plugin "get-pending", % OPTIONAL
|
||||
% see plugin "instant-nodes", % RECOMMENDED
|
||||
"item-ids", % RECOMMENDED
|
||||
"last-published", % RECOMMENDED
|
||||
@ -3506,8 +3562,8 @@ features() ->
|
||||
"member-affiliation", % RECOMMENDED
|
||||
%TODO "meta-data", % RECOMMENDED
|
||||
% see plugin "modify-affiliations", % OPTIONAL
|
||||
%TODO "multi-collection", % OPTIONAL
|
||||
%TODO "multi-subscribe", % OPTIONAL
|
||||
% see plugin "multi-collection", % OPTIONAL
|
||||
% see plugin "multi-subscribe", % OPTIONAL
|
||||
% see plugin "outcast-affiliation", % RECOMMENDED
|
||||
% see plugin "persistent-items", % RECOMMENDED
|
||||
"presence-notifications", % OPTIONAL
|
||||
@ -3521,8 +3577,9 @@ features() ->
|
||||
"retrieve-default" % RECOMMENDED
|
||||
% see plugin "retrieve-items", % RECOMMENDED
|
||||
% see plugin "retrieve-subscriptions", % RECOMMENDED
|
||||
%TODO "shim", % OPTIONAL
|
||||
% see plugin "subscribe", % REQUIRED
|
||||
%TODO "subscription-options", % OPTIONAL
|
||||
% see plugin "subscription-options", % OPTIONAL
|
||||
% see plugin "subscription-notifications" % OPTIONAL
|
||||
].
|
||||
features(Type) ->
|
||||
|
@ -536,7 +536,7 @@ remove_extra_items(NodeId, MaxItems, ItemIds) ->
|
||||
%% ItemId = string()
|
||||
%% @doc <p>Triggers item deletion.</p>
|
||||
%% <p>Default plugin: The user performing the deletion must be the node owner
|
||||
%% or a publisher.</p>
|
||||
%% or a publisher, or PublishModel being open.</p>
|
||||
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
|
||||
GenKey = jlib:short_prepd_bare_jid(Publisher),
|
||||
GenState = get_state(NodeId, GenKey),
|
||||
@ -696,7 +696,10 @@ set_subscriptions(NodeId, Owner, Subscription, SubId) ->
|
||||
SubState = get_state(NodeId, SubKey),
|
||||
case {SubId, SubState#pubsub_state.subscriptions} of
|
||||
{_, []} ->
|
||||
{error, 'item-not-found'};
|
||||
case Subscription of
|
||||
none -> ok;
|
||||
_ -> new_subscription(NodeId, Owner, Subscription, SubState)
|
||||
end;
|
||||
{"", [{_, SID}]} ->
|
||||
case Subscription of
|
||||
none -> unsub_with_subid(NodeId, SID, SubState);
|
||||
@ -721,8 +724,14 @@ replace_subscription(_, [], Acc) ->
|
||||
replace_subscription({Sub, SubId}, [{_, SubID} | T], Acc) ->
|
||||
replace_subscription({Sub, SubId}, T, [{Sub, SubID} | Acc]).
|
||||
|
||||
new_subscription(NodeId, Owner, Subscription, SubState) ->
|
||||
SubId = pubsub_subscription:add_subscription(Owner, NodeId, []),
|
||||
Subscriptions = SubState#pubsub_state.subscriptions,
|
||||
set_state(SubState#pubsub_state{subscriptions = [{Subscription, SubId} | Subscriptions]}),
|
||||
{Subscription, SubId}.
|
||||
|
||||
unsub_with_subid(NodeId, SubId, SubState) ->
|
||||
pubsub_subscription:unsubscribe_node(SubState#pubsub_state.stateid,
|
||||
pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid,
|
||||
NodeId, SubId),
|
||||
NewSubs = lists:filter(fun ({_, SID}) -> SubId =/= SID end,
|
||||
SubState#pubsub_state.subscriptions),
|
||||
|
@ -107,7 +107,7 @@
|
||||
-record(pubsub_state, {stateid,
|
||||
items = [],
|
||||
affiliation = none,
|
||||
subscriptions = none
|
||||
subscriptions = []
|
||||
}).
|
||||
|
||||
%%% @type pubsubItem() = #pubsub_item{
|
||||
|
Loading…
Reference in New Issue
Block a user