aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Romain <christophe.romain@process-one.net>2015-11-10 16:28:57 +0100
committerChristophe Romain <christophe.romain@process-one.net>2015-12-11 12:46:44 +0100
commit6357ea1d5d336ffbd10df22b1fa571d7e3e0d059 (patch)
tree8c1d851fd2f8ef7f468f9bd089c118a664ca6914
parentVirtual nodetree is not attached to any backend (diff)
Limit number of subscriptions per node and allow custom default node configuration
-rw-r--r--src/mod_pubsub.erl52
1 files changed, 51 insertions, 1 deletions
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 116b99cae..a47a03942 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -192,6 +192,8 @@
ignore_pep_from_offline = true,
last_item_cache = false,
max_items_node = ?MAXITEMS,
+ max_subscriptions_node = undefined,
+ default_node_config = [],
nodetree = <<"nodetree_", (?STDTREE)/binary>>,
plugins = [?STDNODE],
db_type
@@ -206,6 +208,8 @@
ignore_pep_from_offline :: boolean(),
last_item_cache :: boolean(),
max_items_node :: non_neg_integer(),
+ max_subscriptions_node :: non_neg_integer()|undefined,
+ default_node_config :: [{atom(), binary()|boolean()|integer()|atom()}],
nodetree :: binary(),
plugins :: [binary(),...],
db_type :: atom()
@@ -259,6 +263,10 @@ init([ServerHost, Opts]) ->
fun(A) when is_boolean(A) -> A end, false),
MaxItemsNode = gen_mod:get_opt(max_items_node, Opts,
fun(A) when is_integer(A) andalso A >= 0 -> A end, ?MAXITEMS),
+ MaxSubsNode = gen_mod:get_opt(max_subscriptions_node, Opts,
+ fun(A) when is_integer(A) andalso A >= 0 -> A end, undefined),
+ DefaultNodeCfg = gen_mod:get_opt(default_node_config, Opts,
+ fun(A) when is_list(A) -> filter_node_options(A) end, []),
pubsub_index:init(Host, ServerHost, Opts),
ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
@@ -270,6 +278,8 @@ init([ServerHost, Opts]) ->
ets:insert(gen_mod:get_module_proc(ServerHost, config), {plugins, Plugins}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {last_item_cache, LastItemCache}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_items_node, MaxItemsNode}),
+ ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_subscriptions_node, MaxSubsNode}),
+ ets:insert(gen_mod:get_module_proc(ServerHost, config), {default_node_config, DefaultNodeCfg}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {pep_mapping, PepMapping}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
@@ -2013,6 +2023,21 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
AccessModel = get_option(Options, access_model),
SendLast = get_option(Options, send_last_published_item),
AllowedGroups = get_option(Options, roster_groups_allowed, []),
+ CanSubscribe = case get_max_subscriptions_node(Host) of
+ Max when is_integer(Max) ->
+ case node_call(Host, Type, get_node_subscriptions, [Nidx]) of
+ {result, NodeSubs} ->
+ SubsNum = lists:foldl(
+ fun ({_, subscribed, _}, Acc) -> Acc+1;
+ (_, Acc) -> Acc
+ end, 0, NodeSubs),
+ SubsNum < Max;
+ _ ->
+ true
+ end;
+ _ ->
+ true
+ end,
if not SubscribeFeature ->
{error,
extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscribe">>)};
@@ -2025,6 +2050,10 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
SubOpts == invalid ->
{error,
extended_error(?ERR_BAD_REQUEST, <<"invalid-options">>)};
+ not CanSubscribe ->
+ %% fallback to closest XEP compatible result, assume we are not allowed to subscribe
+ {error,
+ extended_error(?ERR_NOT_ALLOWED, <<"closed-node">>)};
true ->
Owners = node_owners_call(Host, Type, Nidx, O),
{PS, RG} = get_presence_and_roster_permissions(Host, Subscriber,
@@ -3604,6 +3633,12 @@ get_option(Options, Var, Def) ->
end.
node_options(Host, Type) ->
+ case config(serverhost(Host), default_node_config) of
+ undefined -> node_plugin_options(Host, Type);
+ [] -> node_plugin_options(Host, Type);
+ Config -> Config
+ end.
+node_plugin_options(Host, Type) ->
Module = plugin(Host, Type),
case catch Module:options() of
{'EXIT', {undef, _}} ->
@@ -3612,6 +3647,11 @@ node_options(Host, Type) ->
Result ->
Result
end.
+filter_node_options(Options) ->
+ lists:foldl(fun({Key, Val}, Acc) ->
+ DefaultValue = proplists:get_value(Key, Options, Val),
+ [{Key, DefaultValue}|Acc]
+ end, [], node_flat:options()).
node_owners_action(Host, Type, Nidx, []) ->
case gen_mod:db_type(serverhost(Host), ?MODULE) of
@@ -3906,6 +3946,11 @@ get_max_items_node({_, ServerHost, _}) ->
get_max_items_node(Host) ->
config(serverhost(Host), max_items_node, undefined).
+get_max_subscriptions_node({_, ServerHost, _}) ->
+ get_max_subscriptions_node(ServerHost);
+get_max_subscriptions_node(Host) ->
+ config(serverhost(Host), max_subscriptions_node, undefined).
+
%%%% last item cache handling
is_last_item_cache_enabled({_, ServerHost, _}) ->
@@ -4382,6 +4427,10 @@ mod_opt_type(last_item_cache) ->
fun (A) when is_boolean(A) -> A end;
mod_opt_type(max_items_node) ->
fun (A) when is_integer(A) andalso A >= 0 -> A end;
+mod_opt_type(max_subscriptions_node) ->
+ fun (A) when is_integer(A) andalso A >= 0 -> A end;
+mod_opt_type(default_node_config) ->
+ fun (A) when is_list(A) -> A end;
mod_opt_type(nodetree) ->
fun (A) when is_binary(A) -> A end;
mod_opt_type(pep_mapping) ->
@@ -4391,4 +4440,5 @@ mod_opt_type(plugins) ->
mod_opt_type(_) ->
[access_createnode, db_type, host,
ignore_pep_from_offline, iqdisc, last_item_cache,
- max_items_node, nodetree, pep_mapping, plugins].
+ max_items_node, nodetree, pep_mapping, plugins,
+ max_subscriptions_node, default_node_config].