2012-09-11 15:45:59 +02:00
|
|
|
%%% ====================================================================
|
|
|
|
%%% ``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.
|
2013-03-27 10:36:48 +01:00
|
|
|
%%% Portions created by ProcessOne are Copyright 2006-2013, ProcessOne
|
2012-09-11 15:45:59 +02:00
|
|
|
%%% All Rights Reserved.''
|
2013-03-27 10:36:48 +01:00
|
|
|
%%% This software is copyright 2006-2013, ProcessOne.
|
2012-09-11 15:45:59 +02:00
|
|
|
%%%
|
2013-03-27 10:36:48 +01:00
|
|
|
%%% @copyright 2006-2013 ProcessOne
|
2012-09-11 15:45:59 +02:00
|
|
|
%%% @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.
|
|
|
|
|