aboutsummaryrefslogtreecommitdiff
path: root/src/mod_pubsub_ng/pubsub_tools.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_pubsub_ng/pubsub_tools.erl')
-rw-r--r--src/mod_pubsub_ng/pubsub_tools.erl685
1 files changed, 685 insertions, 0 deletions
diff --git a/src/mod_pubsub_ng/pubsub_tools.erl b/src/mod_pubsub_ng/pubsub_tools.erl
new file mode 100644
index 000000000..9bb5d2504
--- /dev/null
+++ b/src/mod_pubsub_ng/pubsub_tools.erl
@@ -0,0 +1,685 @@
+%%% ====================================================================
+%%% ``The contents of this file are subject to the Erlang Public License,
+%%% Version 1.1, (the "License"); you may not use this file except in
+%%% compliance with the License. You should have received a copy of the
+%%% Erlang Public License along with this software. If not, it can be
+%%% retrieved via the world wide web at http://www.erlang.org/.
+%%%
+%%% Software distributed under the License is distributed on an "AS IS"
+%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%%% the License for the specific language governing rights and limitations
+%%% under the License.
+%%%
+%%% The Initial Developer of the Original Code is ProcessOne.
+%%% Portions created by ProcessOne are Copyright 2006-2011, ProcessOne
+%%% All Rights Reserved.''
+%%% This software is copyright 2006-2011, ProcessOne.
+%%%
+%%% @copyright 2006-2011 ProcessOne
+%%% @author Karim Gemayel <karim.gemayel@process-one.net>
+%%% [http://www.process-one.net/]
+%%% @version {@vsn}, {@date} {@time}
+%%% @end
+%%% ====================================================================
+
+%%% @headerfile "pubsub_dev.hrl"
+
+-module(pubsub_tools).
+-author('karim.gemayel@process-one.net').
+
+-compile(export_all).
+
+-include("pubsub_dev.hrl").
+
+jid_to_string({undefined, S, undefined}) ->
+ jlib:jid_to_string({<<>>, S, <<>>});
+jid_to_string({undefined, S, R}) ->
+ jlib:jid_to_string({<<>>, S, R});
+jid_to_string({U, S, undefined}) ->
+ jlib:jid_to_string({U, S, <<>>});
+jid_to_string(USR) ->
+ jlib:jid_to_string(USR).
+
+make_jid({undefined, S, undefined}) ->
+ jlib:make_jid(<<>>, S, <<>>);
+make_jid({U, S, undefined}) ->
+ jlib:make_jid({U, S, <<>>});
+make_jid(Entity) ->
+ jlib:make_jid(Entity).
+
+%% @doc Determine if data is a JID
+-spec(is_jid/1 ::
+(
+ Data :: binary() | undefined)
+ -> xmpp_jid:entity() | {error, 'jid-malformed'}
+).
+
+is_jid(Data) ->
+ try jlib:string_to_jid(Data) of
+ #jid{} = Jid -> Jid
+ catch
+ _Error -> {error, 'jid-malformed'}
+ end.
+
+%%%
+%-spec(get_value/2 ::
+%(
+% Options :: {Node_Options :: pubsub_options:options_node(),
+% Item_Options :: [] | pubsub_options:options_item()}
+% | pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription()
+% | [],
+% Key :: atom())
+% -> Value :: atom()
+% | binary()
+% | boolean()
+% | non_neg_integer()
+% | undefined
+% | []
+% | 'none'
+% | [atom(),...]
+% | [binary(),...]
+% | [boolean(),...]
+% | [non_neg_integer(),...]
+%).
+
+get_value({Node_Options, [] = _Item_Options}, Key) ->
+ get_value(Node_Options, Key, 0);
+%%
+get_value({Node_Options, Item_Options}, Key) ->
+ case get_value(Item_Options, Key, 'none') of
+ 'none' -> get_value(Node_Options, Key, false);
+ Value -> Value
+ end;
+%%
+get_value([], _Key) -> 'none';
+%%
+get_value(Options, Key) ->
+ get_value(Options, Key, 'none').
+
+%%%
+%-spec(get_value/3 ::
+%(
+% Options :: {Node_Options :: pubsub_options:options_node(),
+% Item_Options :: [] | pubsub_options:options_item()}
+% | pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription(),
+% Key :: atom(),
+% Default :: term())
+% -> Value :: atom()
+% | binary()
+% | boolean()
+% | non_neg_integer()
+% | undefined
+% | []
+% | [atom(),...]
+% | [binary(),...]
+% | [boolean(),...]
+% | [non_neg_integer(),...]
+%).
+get_value({Node_Options, [] = _Item_Options}, Key, Default) ->
+ get_value(Node_Options, Key, Default);
+%%
+get_value({Node_Options, Item_Options}, Key, Default) ->
+ case get_value(Item_Options, Key, 'none') of
+ 'none' -> get_value(Node_Options, Key, Default);
+ Value -> Value
+ end;
+%%
+get_value(Options, Key, Default) ->
+ case lists:keyfind(Key, 1, Options) of
+ {_Key, Value} -> Value;
+ false -> Default
+ end.
+
+%%%
+%-spec(set_value/3 ::
+%(
+% Options :: pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription(),
+% Key :: atom(),
+% Value :: atom()
+% | binary()
+% | boolean()
+% | non_neg_integer()
+% | undefined
+% | []
+% | [atom(),...]
+% | [binary(),...]
+% | [boolean(),...]
+% | [non_neg_integer(),...])
+% -> Options :: pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription()
+%).
+
+set_value(Options, Key, Value) ->
+ lists:keyreplace(Key, 1, Options, {Key, Value}).
+
+%%
+
+%-spec(get_option/2 ::
+%(
+% Options :: {Node_Options :: pubsub_options:options_node(),
+% Item_Options :: [] | pubsub_options:options_item()}
+% | pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription()
+% | [],
+% Key :: atom())
+% -> Option :: pubsub_options:option_item()
+% | pubsub_options:option_node()
+% | pubsub_options:option_subscription()
+% | 'none'
+%).
+
+get_option({Node_Options, [] = _Item_Options}, Key) ->
+ get_option(Node_Options, Key, 'none');
+%%
+get_option({Node_Options, Item_Options}, Key) ->
+ case get_option(Item_Options, Key, 'none') of
+ 'none' -> get_option(Node_Options, Key, 'none');
+ Option -> Option
+ end;
+%%
+get_option([], _Key) -> 'none';
+%%
+get_option(Options, Key) ->
+ get_option(Options, Key, 'none').
+
+%%%
+%-spec(get_option/3 ::
+%(
+% Options :: {Node_Options :: pubsub_options:options_node(),
+% Item_Options :: [] | pubsub_options:options_item()}
+% | pubsub_options:options_item()
+% | pubsub_options:options_node()
+% | pubsub_options:options_subscription(),
+% Key :: atom(),
+% Default :: term())
+% -> Option :: pubsub_options:option_item()
+% | pubsub_options:option_node()
+% | pubsub_options:option_subscription()
+% | term()
+%).
+
+get_option({Node_Options, [] = _Item_Options}, Key, Default) ->
+ get_option(Node_Options, Key, Default);
+%%
+get_option({Node_Options, Item_Options}, Key, Default) ->
+ case get_option(Item_Options, Key, 'none') of
+ 'none' -> get_option(Node_Options, Key, Default);
+ Option -> Option
+ end;
+%%
+get_option(Options, Key, Default) ->
+ case lists:keyfind(Key, 1, Options) of
+ {_Key, Value} -> _Option = {Key, Value};
+ false -> Default
+ end.
+
+%%
+-spec(get_entity_roster/1 ::
+(
+ Entity :: xmpp_jid:usr_entity()
+ | xmpp_jid:entity())
+ -> Roster::[Roster_Item::#roster{}]
+).
+
+get_entity_roster({U,S, _R} = _USR_Entity) ->
+ _Roster = ejabberd_hooks:run_fold(roster_get, S, [], [{U,S}]);
+get_entity_roster(#jid{luser = U, lserver = S} = _Jid_Entity) ->
+ _Roster = get_entity_roster({U,S, undefined}).
+
+%%
+-spec(check_access_model/3 ::
+(
+ Host :: xmpp_jid:raw_jid_component_bare(),
+ Entity :: xmpp_jid:usr_entity(),
+ Criteria :: {Access_Model :: pubsub_options:access_model(),
+ Affiliation :: exmpp_pubsub:affiliation(),
+ Subscriptions :: exmpp_pubsub:subscriptions(),
+ Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
+ Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed()})
+ -> ok
+ %%%
+ | {error, 'forbidden'}
+).
+
+check_access_model(_Host, _Entity,
+ {_Access_Model, 'outcast' = _Affiliation, _Subscriptions, _Node_Owners,
+ _Rosters_Groups_Allowed}) ->
+ {error, 'forbidden'};
+%%
+check_access_model(_Host, _Entity,
+ {'open' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
+ _Rosters_Groups_Allowed}) ->
+ ok;
+%%
+check_access_model(Host, Entity,
+ {'presence' = _Access_Model, _Affiliation, _Subscriptions, Node_Owners,
+ _Rosters_Groups_Allowed}) ->
+ case is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners) of
+ false ->
+ {error, 'forbidden'};
+ _Node_Owner ->
+ ok
+ end;
+%%
+check_access_model(_Host, Entity,
+ {'roster' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
+ Rosters_Groups_Allowed}) ->
+ case is_contact_in_allowed_roster_groups(Entity, Rosters_Groups_Allowed) of
+ false ->
+ {error, 'forbidden'};
+ {_Node_Owner, _Roster_Group_Allowed} ->
+ ok
+ end;
+%%
+check_access_model(_Host, _Entity,
+ {'whitelist' = _Access_Model, Affiliation, _Subscriptions, _Node_Owners,
+ _Roster_Groups_Allowed})
+ when Affiliation == 'member'
+ %%orelse Affiliation == 'owner'
+ orelse Affiliation == 'publish-only'
+ orelse Affiliation == 'publisher' ->
+ ok;
+check_access_model(_Host, _Entity,
+ {'whitelist' = _Access_Model, _Affiliation, _Subscriptions, _Node_Owners,
+ _Roster_Groups_Allowed}) ->
+ {error, 'forbidden'};
+%%
+check_access_model(_Host, _Entity,
+ {'authorize' = _Access_Model, _Affiliation, Subscriptions, _Node_Owners,
+ _Roster_Groups_Allowed}) ->
+ case has_subscriptions(Subscriptions) of
+ true -> ok;
+ false -> {error, 'forbidden'}
+ end.
+
+
+
+%%
+-spec(check_access_model/7 ::
+(
+ Host :: xmpp_jid:raw_jid_component_bare(),
+ Entity :: xmpp_jid:usr_entity(),
+ Access_Model :: pubsub_options:access_model(),
+ Affiliation :: exmpp_pubsub:affiliation(),
+ Subscriptions :: exmpp_pubsub:subscriptions(),
+ Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...],
+ Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed())
+ -> ok
+ %%%
+ | {error, 'forbidden'}
+ | {error, 'item-not-found'}
+).
+
+check_access_model(_Host, _Entity, _Access_Model, 'owner' = _Affiliation,
+ _Subscriptions, _Node_Owners, _Rosters_Groups_Allowed) ->
+ ok;
+%%
+check_access_model(Host, Entity, Access_Model, 'outcast' = _Affiliation,
+ _Subscriptions, Node_Owners, Rosters_Groups_Allowed) ->
+ case Access_Model of
+ %%
+ 'open'->
+ {error, 'forbidden'};
+ %%
+ 'presence' ->
+ case
+ is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners)
+ of
+ false ->
+ {error, 'item-not-found'};
+ _Node_Owner ->
+ {error, 'forbidden'}
+ end;
+ %%
+ 'roster' ->
+ case
+ is_contact_in_allowed_roster_groups(Entity,
+ Rosters_Groups_Allowed)
+ of
+ false ->
+ {error, 'item-not-found'};
+ {_Node_Owner, _Roster_Group_Allowed} ->
+ {error, 'forbidden'}
+ end;
+ %%
+ 'authorize' ->
+ {error, 'forbidden'};
+ %%
+ 'whitelist' ->
+ {error, 'item-not-found'}
+ end;
+%%
+check_access_model(_Host, _Entity, 'open' = _Access_Model, _Affiliation,
+ _Subscriptions, _Node_Owners, _Rosters_Groups_Allowed) ->
+ ok;
+%%
+check_access_model(Host, Entity, 'presence' = _Access_Model, _Affiliation,
+ _Subscriptions, Node_Owners, _Rosters_Groups_Allowed) ->
+ case is_contact_subscribed_to_node_owners(Host, Entity, Node_Owners) of
+ false ->
+ {error, 'item-not-found'};
+ _Node_Owner ->
+ ok
+ end;
+%%
+check_access_model(_Host, Entity, 'roster' = _Access_Model, _Affiliation,
+ _Subscriptions, _Node_Owners, Rosters_Groups_Allowed) ->
+ case is_contact_in_allowed_roster_groups(Entity, Rosters_Groups_Allowed) of
+ false ->
+ {error, 'item-not-found'};
+ {_Node_Owner, _Roster_Group_Allowed} ->
+ ok
+ end;
+%%
+check_access_model(_Host, _Entity, 'whitelist' = _Access_Model, Affiliation,
+ _Subscriptions, _Node_Owners, _Roster_Groups_Allowed) ->
+ case Affiliation of
+ Affiliation
+ when Affiliation == 'member'
+ orelse Affiliation == 'publish-only'
+ orelse Affiliation == 'publisher' ->
+ ok;
+ _Affiliation ->
+ {error, 'item-not-found'}
+ end;
+%%
+check_access_model(_Host, _Entity, 'authorize' = _Access_Model, _Affiliation,
+ Subscriptions, _Node_Owners, _Roster_Groups_Allowed) ->
+ case has_subscriptions(Subscriptions) of
+ true ->
+ ok;
+ false ->
+ {error, 'item-not-found'}
+ end.
+
+
+-spec(check_publish_model/3 ::
+(
+ Publish_Model :: pubsub_options:publish_model(),
+ Affiliation :: exmpp_pubsub:affiliation(),
+ Subscriptions :: exmpp_pubsub:subscriptions())
+ -> ok
+ %%%
+ | {error, 'forbidden'}
+).
+
+check_publish_model('open' = _Publish_Model, _Affiliation, _Subscriptions) ->
+ ok;
+%%
+check_publish_model('publishers' = _Publish_Model, Affiliation, _Subscriptions) ->
+ case Affiliation of
+ Affiliation
+ when Affiliation == 'owner'
+ orelse Affiliation == 'publish-only'
+ orelse Affiliation == 'publisher' ->
+ ok;
+ _Affiliation ->
+ {error, 'forbidden'}
+ end;
+%%
+check_publish_model('subscribers' = _Publish_Model, Affiliation, Subscriptions) ->
+ case Affiliation of
+ 'owner' ->
+ ok;
+ _Affiliation ->
+ case has_subscriptions(Subscriptions) of
+ true ->
+ ok;
+ false ->
+ {error, 'forbidden'}
+ end
+ end.
+
+%%
+-spec(has_subscriptions/1 ::
+(
+ Subscriptions :: exmpp_pubsub:subscriptions())
+ -> Has_Subscriptions::boolean()
+).
+
+has_subscriptions([] = _Subscriptions) ->
+ false;
+has_subscriptions(
+ [{'pending' = _Subscription_State, _SubId, _Resource, _Subscription_Options}]) ->
+ false;
+has_subscriptions(_Subscriptions) ->
+ true.
+
+%%
+%% @doc Check if a contact is subscribed to at least one local node owner
+%% amongst a list of (remote and local) node owners
+-spec(is_contact_subscribed_to_node_owners/3 ::
+(
+ Host :: xmpp_jid:raw_jid_component_bare(),
+ Contact :: xmpp_jid:usr_entity(),
+ Node_Owners :: [] | [Node_Owner::xmpp_jid:usr_bare(),...])
+ -> Is_Contact_Subscribed_To_Node_Owners :: false | xmpp_jid:usr_bare()
+).
+
+is_contact_subscribed_to_node_owners(_Host, _Contact, [] = _Node_Owners) ->
+ false;
+%% The node owner is a local entity, check if the contact is subscribed to it
+is_contact_subscribed_to_node_owners(Host, Contact,
+ [{_U, Host, _R} = Local_Node_Owner | Node_Owners] = _Node_Owners) ->
+ _Is_Contact_Subscribed_To_Node_Owners = case
+ is_contact_subscribed_to_entity(Contact,
+ _Local_Node_Owner_Roster = get_entity_roster(Local_Node_Owner))
+ of
+ true ->
+ Local_Node_Owner;
+ false ->
+ is_contact_subscribed_to_node_owners(Host, Contact, Node_Owners)
+ end;
+%% The node owner is a remote entity, don't check if the contact is subscribed to it
+is_contact_subscribed_to_node_owners(Host, Contact,
+ [_Remote_Node_Owner | Node_Owners] = _Node_Owners) ->
+ is_contact_subscribed_to_node_owners(Host, Contact, Node_Owners).
+
+%%
+%% @doc Check if an entity is in a #roster{}
+%% with #roster.subscription == 'from' or
+%% with #roster.subscription == 'both'
+-spec(is_contact_subscribed_to_entity/2 ::
+(
+ Contact :: xmpp_jid:usr_entity(),
+ Entity_Roster :: [] | [Roster_Item::#roster{},...])
+ -> Is_Contact_Subscribed::boolean()
+).
+
+is_contact_subscribed_to_entity(_Contact, [] = _Entity_Roster) ->
+ _Is_Contact_Subscribed = false;
+%% Contact is included in a #roster{} item with
+%% a subscription of type 'from' and 'both' (i.e. is subscribed to it)
+is_contact_subscribed_to_entity({U, S, _R} = _Contact,
+ [#roster{jid = {U, S, _}, subscription = Subscription} = _Roster_Item
+ | _Roster_Items] = _Entity_Roster)
+ when Subscription == 'from' orelse Subscription == 'both' ->
+ _Is_Contact_Subscribed = true;
+%% Contact is not included in a #roster{} item
+%% or has a subscription of type different than 'from' and 'both'
+%% ( i.e. is not subscribed to it),
+%% check next roster items
+is_contact_subscribed_to_entity(Contact,
+ [_Roster_Item | Roster_Items] = _Entity_Roster) ->
+ _Is_Contact_Subscribed = is_contact_subscribed_to_entity(Contact,
+ Roster_Items).
+
+%%
+-spec(is_contact_in_allowed_roster_groups/2 ::
+(
+ Contact :: xmpp_jid:usr_entity(),
+ Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed())
+ -> Roster_Group_Allowed :: false
+ | {Entity :: xmpp_jid:usr_bare(),
+ Roster_Group :: pubsub_options:roster_group()}
+).
+
+is_contact_in_allowed_roster_groups(_Contact, [] = _Rosters_Groups_Allowed) ->
+ false;
+%%
+is_contact_in_allowed_roster_groups(Contact,
+ [{Entity, Entity_Roster_Groups_Allowed} | Rosters_Groups_Allowed]
+ = _Rosters_Groups_Allowed) ->
+ case
+ is_contact_in_allowed_roster_group(Contact,
+ _Entity_Roster = get_entity_roster(Entity),
+ Entity_Roster_Groups_Allowed)
+ of
+ false ->
+ is_contact_in_allowed_roster_groups(Contact, Rosters_Groups_Allowed);
+ Roster_Group ->
+ {Entity, Roster_Group}
+ end.
+
+%%
+-spec(is_contact_in_allowed_roster_group/3 ::
+(
+ Contact :: xmpp_jid:usr_entity(),
+ Entity_Roster :: [] | [Roster_Item::#roster{groups::pubsub_options:roster_groups()}],
+ Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group(),...])
+ -> Roster_Group :: false | pubsub_options:roster_group()
+).
+
+is_contact_in_allowed_roster_group(_Contact, [] = _Entity_Roster,
+ _Roster_Groups_Allowed) ->
+ false;
+%%
+is_contact_in_allowed_roster_group({U, S, _R} = _Contact,
+ [#roster{jid = {U, S, _}, subscription = Subscription, groups = Roster_Groups}
+ | _Other_Roster_Items] = _Entity_Roster, Roster_Groups_Allowed)
+ when Subscription == 'from' orelse Subscription == 'both' ->
+ is_contact_in_allowed_roster_group(Roster_Groups, Roster_Groups_Allowed);
+%%
+is_contact_in_allowed_roster_group(Contact,
+ [_Roster_Item | Roster_Items] = _Entity_Roster, Roster_Groups_Allowed) ->
+ is_contact_in_allowed_roster_group(Contact, Roster_Items,
+ Roster_Groups_Allowed).
+
+%%
+-spec(is_contact_in_allowed_roster_group/2 ::
+(
+ Roster_Groups :: [] | [Roster_Group::pubsub_options:roster_group()],
+ Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group(),...])
+ -> Roster_Group :: false | pubsub_options:roster_group()
+).
+
+is_contact_in_allowed_roster_group([] = _Roster_Groups, _Roster_Groups_Allowed) ->
+ false;
+%%
+is_contact_in_allowed_roster_group([Roster_Group | Roster_Groups]
+ = _Roster_Groups, Roster_Groups_Allowed) ->
+ case lists:member(Roster_Group, Roster_Groups_Allowed) of
+ true ->
+ Roster_Group;
+ false ->
+ is_contact_in_allowed_roster_group(Roster_Groups,
+ Roster_Groups_Allowed)
+ end.
+
+%%
+-spec(get_user_resources/2 ::
+(
+ User :: binary(),
+ Server :: binary())
+ -> Resources::[Resource::xmpp_jid:resource_jid()]
+).
+
+get_user_resources(User, Server) ->
+ ejabberd_sm:get_user_resources(User, Server).
+
+%%
+-spec(get_resources_show/2 ::
+(
+ User :: binary(),
+ Server :: binary())
+ -> Resources_Show :: [{Resource :: xmpp_jid:resource_jid(),
+ Show :: 'away' | 'chat' | 'dnd' | 'online' | 'xa' }]
+).
+
+get_resources_show(User, Server) ->
+ _Resources_Show = lists:foldl(fun
+ (Resource, Resources_Show) ->
+ case ejabberd_sm:get_session_pid(User, Server, Resource) of
+ C2SPid when is_pid(C2SPid) ->
+ case ejabberd_c2s:get_presence(C2SPid) of
+ {_User, _Resource, Show, _} ->
+ [{Resource, list_to_atom(Show)} | Resources_Show];
+ _ ->
+ Resources_Show
+ end;
+ _ ->
+ Resources_Show
+ end
+ end, [], _Resources = ejabberd_sm:get_user_resources(User, Server)).
+
+
+%%
+-spec(rosters_groups_allowed_cache/2 ::
+(
+ Rosters_Groups_Allowed :: pubsub_options:rosters_groups_allowed(),
+ Rosters_Groups_Allowed_Cache :: pubsub_options:rosters_groups_allowed())
+ -> Is_Roster_Groups_Cached::boolean()
+).
+
+rosters_groups_allowed_cache(_Rosters_Groups_Allowed,
+ [] = _Rosters_Groups_Allowed_Cache) ->
+ false;
+%%
+rosters_groups_allowed_cache([] = _Rosters_Groups_Allowed,
+ _Rosters_Groups_Allowed_Cache) ->
+ false;
+%%
+rosters_groups_allowed_cache(
+ [{Entity, Roster_Groups_Allowed} | Rosters_Groups_Allowed],
+ Rosters_Groups_Allowed_Cache) ->
+ case lists:keyfind(Entity, 1, Rosters_Groups_Allowed_Cache) of
+ {_Entity, Roster_Groups_Allowed_Cache} ->
+ case
+ roster_groups_allowed_cache(Roster_Groups_Allowed,
+ Roster_Groups_Allowed_Cache)
+ of
+ true ->
+ true;
+ false ->
+ rosters_groups_allowed_cache(Rosters_Groups_Allowed,
+ Rosters_Groups_Allowed_Cache)
+ end;
+ false ->
+ rosters_groups_allowed_cache(Rosters_Groups_Allowed,
+ Rosters_Groups_Allowed_Cache)
+ end.
+
+%%
+-spec(roster_groups_allowed_cache/2 ::
+(
+ Roster_Groups_Allowed :: [Roster_Group_Allowed::pubsub_options:roster_group()],
+ Roster_Groups_Allowed_Cache :: [Roster_Group_Allowed_Cache::pubsub_options:roster_group()])
+ -> Is_Roster_Groups_Cached :: boolean()
+).
+
+roster_groups_allowed_cache(_Roster_Groups_Allowed,
+ [] = _Roster_Groups_Allowed_Cache) ->
+ false;
+%%
+roster_groups_allowed_cache([] = _Roster_Groups_Allowed,
+ _Roster_Groups_Allowed_Cache) ->
+ false;
+%%
+roster_groups_allowed_cache([Roster_Group_Allowed | Roster_Groups_Allowed],
+ Roster_Groups_Allowed_Cache) ->
+ case lists:member(Roster_Group_Allowed, Roster_Groups_Allowed_Cache) of
+ true ->
+ true;
+ false ->
+ roster_groups_allowed_cache(Roster_Groups_Allowed,
+ Roster_Groups_Allowed_Cache)
+ end.
+