%% exports for hooks
-export([presence_probe/3,
+ in_subscription/6,
out_subscription/4,
remove_user/2,
disco_local_identity/5,
ejabberd_hooks:add(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
ejabberd_hooks:add(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
+ ejabberd_hooks:add(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
ejabberd_hooks:add(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:add(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
end,
Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
gen_server:cast(Proc, {presence, PUser, PServer, PResources, Owner});
-out_subscription(_, _, _, _) ->
+out_subscription(_,_,_,_) ->
+ ok.
+in_subscription(_, User, Server, Subscriber, unsubscribed, _) ->
+ Owner = jlib:make_jid(User, Server, ""),
+ Proc = gen_mod:get_module_proc(Server, ?PROCNAME),
+ gen_server:cast(Proc, {unsubscribe, Subscriber, Owner});
+in_subscription(_, _, _, _, _, _) ->
ok.
%% -------
delete_node(Host, ["home", LServer, LUser], Owner),
{noreply, State};
+handle_cast({unsubscribe, Subscriber, Owner}, State) ->
+ Host = State#state.host,
+ BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
+ lists:foreach(fun(PType) ->
+ {result, Subscriptions} = node_action(Host, PType, get_entity_subscriptions, [Host, Subscriber]),
+ lists:foreach(fun
+ ({Node, subscribed, JID}) ->
+ Action = fun(#pubsub_node{options = Options, owners = Owners, type = Type, id = NodeId}) ->
+ case get_option(Options, access_model) of
+ presence ->
+ case lists:member(BJID, Owners) of
+ true ->
+ node_call(Type, unsubscribe_node, [NodeId, Subscriber, JID, all]);
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end
+ end,
+ transaction(Host, Node, Action, sync_dirty);
+ (_) ->
+ ok
+ end, Subscriptions)
+ end, State#state.plugins),
+ {noreply, State};
+
handle_cast(_Msg, State) ->
{noreply, State}.
ejabberd_hooks:delete(disco_sm_features, ServerHost, ?MODULE, disco_sm_features, 75),
ejabberd_hooks:delete(disco_sm_items, ServerHost, ?MODULE, disco_sm_items, 75),
ejabberd_hooks:delete(presence_probe_hook, ServerHost, ?MODULE, presence_probe, 50),
+ ejabberd_hooks:delete(roster_in_subscription, ServerHost, ?MODULE, in_subscription, 50),
ejabberd_hooks:delete(roster_out_subscription, ServerHost, ?MODULE, out_subscription, 50),
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE, remove_user, 50),
ejabberd_hooks:delete(anonymous_purge_hook, ServerHost, ?MODULE, remove_user, 50),
end, [], xml:remove_cdata(Els)),
get_items(Host, Node, From, SubId, MaxItems, ItemIDs);
{get, "subscriptions"} ->
- get_subscriptions(Host, From, Plugins);
+ get_subscriptions(Host, Node, From, Plugins);
{get, "affiliations"} ->
get_affiliations(Host, From, Plugins);
{get, "options"} ->
J -> jlib:jid_tolower(J)
end,
SubId = uniqid(),
- Action = fun(#pubsub_node{options = Options, type = Type, id = NodeId}) ->
- Features = features(Type),
- SubscribeFeature = lists:member("subscribe", Features),
- SubscribeConfig = get_option(Options, subscribe),
- AccessModel = get_option(Options, access_model),
- SendLast = get_option(Options, send_last_published_item),
- AllowedGroups = get_option(Options, roster_groups_allowed, []),
- {PresenceSubscription, RosterGroup} =
- case Host of
- {OUser, OServer, _} ->
- get_roster_info(OUser, OServer,
- Subscriber, AllowedGroups);
- _ ->
- case Subscriber of
- {"", "", ""} -> {false, false};
- {U, S, _} -> get_roster_info(U, S, Subscriber,
- AllowedGroups)
- end
- end,
- if
- not SubscribeFeature ->
- %% Node does not support subscriptions
- {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
- not SubscribeConfig ->
- %% Node does not support subscriptions
- {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
- true ->
- node_call(Type, subscribe_node,
- [NodeId, From, Subscriber,
+ Action = fun(#pubsub_node{options = Options, owners = [Owner|_], type = Type, id = NodeId}) ->
+ Features = features(Type),
+ SubscribeFeature = lists:member("subscribe", Features),
+ SubscribeConfig = get_option(Options, subscribe),
+ AccessModel = get_option(Options, access_model),
+ SendLast = get_option(Options, send_last_published_item),
+ AllowedGroups = get_option(Options, roster_groups_allowed, []),
+ {PresenceSubscription, RosterGroup} =
+ case Host of
+ {OUser, OServer, _} ->
+ get_roster_info(OUser, OServer,
+ Subscriber, AllowedGroups);
+ _ ->
+ case Subscriber of
+ {"", "", ""} ->
+ {false, false};
+ _ ->
+ {OU, OS, _} = Owner,
+ get_roster_info(OU, OS,
+ Subscriber, AllowedGroups)
+ end
+ end,
+ if
+ not SubscribeFeature ->
+ %% Node does not support subscriptions
+ {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
+ not SubscribeConfig ->
+ %% Node does not support subscriptions
+ {error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "subscribe")};
+ true ->
+ node_call(Type, subscribe_node,
+ [NodeId, From, Subscriber,
AccessModel, SendLast,
PresenceSubscription, RosterGroup])
- end
- end,
+ end
+ end,
Reply = fun(Subscription) ->
%% TODO, this is subscription-notification, should depends on node features
Fields =
PayloadSize > PayloadMaxSize ->
%% Entity attempts to publish very large payload
{error, extended_error(?ERR_NOT_ACCEPTABLE, "payload-too-big")};
- PayloadCount == 0 ->
+ (PayloadCount == 0) and (Payload == []) ->
%% Publisher attempts to publish to payload node with no payload
{error, extended_error(?ERR_BAD_REQUEST, "payload-required")};
- PayloadCount > 1 ->
+ (PayloadCount > 1) or (PayloadCount == 0) ->
%% Entity attempts to publish item with multiple payload elements
{error, extended_error(?ERR_BAD_REQUEST, "invalid-payload")};
(DeliverPayloads == 0) and (PersistItems == 0) and (PayloadSize > 0) ->
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
Features = features(Type),
RetrieveFeature = lists:member("modify-affiliations", Features),
- Affiliation = node_call(Type, get_affiliation, [NodeId, JID]),
+ {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
if
not RetrieveFeature ->
%% Service does not support modify affiliations
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "modify-affiliations")};
- Affiliation /= {result, owner} ->
+ Affiliation /= owner ->
%% Entity is not an owner
{error, ?ERR_FORBIDDEN};
true ->
end.
-%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
+%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
%% Host = host()
+%% Node = pubsubNode()
%% JID = jid()
%% Plugins = [Plugin::string()]
%% Reason = stanzaError()
%% Response = [pubsubIQResponse()]
%% @doc <p>Return the list of subscriptions as an XMPP response.</p>
-get_subscriptions(Host, JID, Plugins) when is_list(Plugins) ->
+get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
Result = lists:foldl(
fun(Type, {Status, Acc}) ->
Features = features(Type),
case Result of
{ok, Subscriptions} ->
Entities = lists:flatmap(
- fun({_, none}) -> [];
- ({Node, Subscription}) ->
+ fun({_, none}) ->
+ [];
+ ({SubsNode, Subscription}) ->
+ case Node of
+ [] ->
[{xmlelement, "subscription",
- [{"node", node_to_string(Node)},
+ [{"node", node_to_string(SubsNode)},
{"subscription", subscription_to_string(Subscription)}],
[]}];
- ({_, none, _}) -> [];
- ({Node, Subscription, SubJID}) ->
+ SubsNode ->
[{xmlelement, "subscription",
- [{"node", node_to_string(Node)},
+ [{"subscription", subscription_to_string(Subscription)}],
+ []}];
+ _ ->
+ []
+ end;
+ ({_, none, _}) ->
+ [];
+ ({SubsNode, Subscription, SubJID}) ->
+ case Node of
+ [] ->
+ [{xmlelement, "subscription",
+ [{"node", node_to_string(SubsNode)},
{"jid", jlib:jid_to_string(SubJID)},
{"subscription", subscription_to_string(Subscription)}],
- []}]
+ []}];
+ SubsNode ->
+ [{xmlelement, "subscription",
+ [{"jid", jlib:jid_to_string(SubJID)},
+ {"subscription", subscription_to_string(Subscription)}],
+ []}];
+ _ ->
+ []
+ end
end, lists:usort(lists:flatten(Subscriptions))),
{result, [{xmlelement, "pubsub", [{"xmlns", ?NS_PUBSUB}],
[{xmlelement, "subscriptions", [],
Entities}]}]};
{Error, _} ->
Error
- end;
+ end.
get_subscriptions(Host, Node, JID) ->
Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
Features = features(Type),
RetrieveFeature = lists:member("manage-subscriptions", Features),
- Affiliation = node_call(Type, get_affiliation, [NodeId, JID]),
+ {result, Affiliation} = node_call(Type, get_affiliation, [NodeId, JID]),
if
not RetrieveFeature ->
%% Service does not support manage subscriptions
{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, "manage-subscriptions")};
- Affiliation /= {result, owner} ->
+ Affiliation /= owner ->
%% Entity is not an owner
{error, ?ERR_FORBIDDEN};
true ->