diff options
Diffstat (limited to 'mod_pubsub_ng/pubsub_db.erl')
| -rw-r--r-- | mod_pubsub_ng/pubsub_db.erl | 1575 |
1 files changed, 1575 insertions, 0 deletions
diff --git a/mod_pubsub_ng/pubsub_db.erl b/mod_pubsub_ng/pubsub_db.erl new file mode 100644 index 000000000..37e8e4473 --- /dev/null +++ b/mod_pubsub_ng/pubsub_db.erl @@ -0,0 +1,1575 @@ +%%% ==================================================================== +%%% ``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-2013, ProcessOne +%%% All Rights Reserved.'' +%%% This software is copyright 2006-2013, ProcessOne. +%%% +%%% @copyright 2006-2013 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_db). +-author('karim.gemayel@process-one.net'). + +-compile(export_all). + +-include("pubsub_dev.hrl"). +-include("pubsub_api.hrl"). + + +-import(pubsub_tools, +[ + get_option/2, + get_option/3, + get_value/2, + get_value/3, + set_value/3, + %% + check_access_model/3, + check_access_model/7, + check_publish_model/3, + is_contact_subscribed_to_node_owners/3, + is_contact_in_allowed_roster_groups/2, + has_subscriptions/1 +]). + + + +%% @doc Intialize mnesia + +-spec(init/2 :: +( + Backend :: 'mnesia' | 'odbc', + Suffix :: atom()) + -> 'ok' +). + +init('mnesia', Suffix) -> + pubsub_db_mnesia:init(Suffix); +init('odbc', _Suffix) -> + ok. + + +transaction('mnesia', Module, Function, Arguments) -> + pubsub_db_mnesia:transaction(Module, Function, Arguments). + + +%% +db_transaction(Module, Function, Arguments) -> + case mnesia:transaction( + fun() -> + apply(Module, Function, Arguments) + end) + of + {atomic, Result} -> + Result; + {aborted, Reason} -> + ?INFO_MSG("DB TRANSACTION ERROR ~p", [Reason]), + {error, 'internal-server-error'} + end. + +%-- Create_Node --% +-spec(create_node/5 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host_pubsub(), + Entity :: xmpp_jid:usr_bare(), + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId(), + Node_Options :: pubsub_options:options_node()}) + -> %% + {result, + NodeId :: exmpp_pubsub:nodeId(), + Notify :: [] + | [Notify_Subscription :: { + Notification_Type :: pubsub_options:notification_type(), + Subscription :: exmpp_pubsub:subscription_subscribed() + },...] + } + % + %%% + | {error, 'conflict'} +). + +create_node(Host, Pubsub_Host, {_U,S,_R} = Entity, Pubsub_Features, + _Parameters = {undefined = _NodeId, Node_Options}) -> + case + pubsub_db_mnesia:read_node('dev', + Pubsub_Host, NodeId = exmpp_pubsub:nodeId()) + of + undefined -> + {result, + NodeId, + _Notify = case + pubsub_db_mnesia:create_node('dev', Pubsub_Host, Entity, NodeId, + _Level = 1, Node_Options, Pubsub_Features) + of + undefined -> + []; + Subscription -> + [_Notify_Subscription = { + _Notification_Type = get_value(Node_Options, + 'notification_type', 'headline'), + Subscription + }] + end}; + _Pubsub_Node -> + create_node(Host, Pubsub_Host, Entity, Pubsub_Features, + {_NodeId = undefined, Node_Options}) + end; +%% +create_node(Host, Pubsub_Host, {_U,S,_R} = Entity, Pubsub_Features, + _Parameters = {NodeId, Node_Options}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {result, + NodeId, + _Notify = case + pubsub_db_mnesia:create_node('dev', Pubsub_Host, Entity, NodeId, + _Level = 1, Node_Options, Pubsub_Features) + of + undefined -> + []; + Subscription -> + [_Notify_Subscription = { + _Notification_Type = get_value(Node_Options, + 'notification_type', 'headline'), + Subscription + }] + end}; + _Pubsub_Node -> + {error, 'conflict'} + end. + +%-- Delete_Node --% +-spec(delete_node/3 :: +( + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> %% + {result, + Notify :: [] + | [Notify_Delete :: { + Notification_Type::pubsub_options:notification_type(), + Recipients::[Entity::xmpp_jid:usr_bare()] + }] + } + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} +). + +delete_node(_Pubsub_Host, _Entity, {undefined = _NodeId}) -> + {error, 'item-not-found'}; +%% +delete_node(Pubsub_Host, Entity, {NodeId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + Pubsub_Node -> + case + lists:member(Entity, Owners = Pubsub_Node#pubsub_node_dev.owners) + of + true -> + {result, + _Notify = case + pubsub_db_mnesia:delete_node('dev', Pubsub_Node) + of + undefined -> + []; + Subscribers -> + [_Notify_Delete = { + _Notification_Type = get_value( + Pubsub_Node#pubsub_node_dev.options, + 'notification_type', 'headline'), + _Recipients = lists:append(Owners, Subscribers) + }] + end}; + false -> + {error, 'forbidden'} + end + end. + +%-- Purge_Node --% +-spec(purge_node/3 :: +( + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> %% + {result, + Notify :: [] + | [Notify_Purge :: { + Notification_Type::pubsub_options:notification_type(), + Recipients::[Entity::xmpp_jid:usr_bare()] + }] + } + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + % + | {error, 'feature-not-implemented', 'persistent-items'} +). + +purge_node(_Pubsub_Host, _Entity, {undefined = _NodeId}) -> + {error, 'item-not-found'}; +%% +purge_node(Pubsub_Host, Entity, {NodeId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + Pubsub_Node -> + case lists:member(Entity, Pubsub_Node#pubsub_node_dev.owners) of + true -> + case + _Persist_Items = get_value( + Node_Options = Pubsub_Node#pubsub_node_dev.options, + 'persist_items', true) + of + true -> + {result, + _Notify = case + pubsub_db_mnesia:purge_node('dev', Pubsub_Node) + of + undefined -> + []; + Subscribers -> + [_Notify_Purge = { + _Notification_Type = get_value( + Node_Options, + 'notification_type', 'headline'), + _Recipients = Subscribers + }] + end}; + false -> + {error, 'feature-not-implemented', 'persistent-items'} + end; + false -> + {error, 'forbidden'} + end + end. + +%-- Publish_Item --% +-spec(publish_item/6 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host_pubsub(), + Entity :: xmpp_jid:usr_entity(), + Plugin :: exmpp_pubsub:plugin(), + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Parameters :: { + NodeId :: undefined | exmpp_pubsub:nodeId(), + Item :: 0 | 1, + ItemId :: undefined | exmpp_pubsub:itemId(), + Payload :: exmpp_pubsub:payload() | [Xmlel::xmlel(),...], + Options :: {Node_Options :: [] | pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item()} + }) + -> {result, + NodeId :: exmpp_pubsub:nodeId(), + ItemId :: exmpp_pubsub:itemId(), + Node_Options :: pubsub_options:options_node(), + Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...], + SubId :: undefined | exmpp_pubsub:subId(), + Subscription :: undefined | exmpp_pubsub:subscription_subscribed(), + Broadcasted_Items :: { + Published_Items :: [Published_Item::{ + ItemId :: exmpp_pubsub:itemId(), + Item_Options :: [] | pubsub_options:options_item(), + Payload :: exmpp_pubsub:payload(), + Publisher :: xmpp_jid:usr_entity() + }], + % + Retracted_Items :: [Retracted_Item::{ + ItemId :: exmpp_pubsub:itemId(), + Item_Options :: [] | pubsub_options:options_item() + }] + }, + Pubsub_States::[Pubsub_State::mod_pubsub_dev:pubsub_state(),...]} + %%% + | {error, 'forbidden'} + | {error, 'item-not-found'} + % + | {error, 'bad-request', 'invalid-payload'} + | {error, 'bad-request', 'item-required'} + | {error, 'bad-request', 'item-forbidden'} + | {error, 'not-acceptable', 'payload-too-big'} +). + +%% +publish_item(Host, Pubsub_Host, {U,S,R} = Entity, Plugin, Pubsub_Features, + {undefined = _NodeId, Item, _ItemId, Payload, + {[] = _Node_Options, Item_Options}}) -> + case + pubsub_db_mnesia:read_node('dev', + Pubsub_Host, NodeId = exmpp_pubsub:nodeId()) + of + undefined -> + case + options_on_publish_item(none, {Item, Payload, + Options = { + Node_Options = Plugin:node_options('leaf'), + Item_Options + }}) + of + ok -> + {ItemId, Pubsub_State} = pubsub_db_mnesia:publish_item1('dev', + Pubsub_Host, Entity, NodeId, _Level = 1, _ItemId, Payload, + Options, Pubsub_Features), + Subscription = case + Pubsub_State#pubsub_state_dev.subscriptions + of + [] -> undefined; + [_Subscription] -> _Subscription + end, + {result, NodeId, ItemId, Node_Options, + _Node_Owners = [{U,S,undefined}], + _SubId = case Subscription of + undefined -> + undefined; + {_Subscription_State, SubId, _Resource, + _Subscription_Options} -> + SubId + end, + Subscription, + {_Published_Items = case + get_value({Node_Options, Item_Options}, + 'deliver_notifications', false) + of + true -> + [{ItemId, + Item_Options, + _Payload = case + get_value({Node_Options, Item_Options}, + 'deliver_payloads', false) + of + true -> + case Payload of + [Xmlel] -> Xmlel; + [] -> [] + end; + false -> [] + end, + _Publisher = {U,S,R} + }]; + false -> + [] + end, + _Retracted_Items = []}, + _Pubsub_States = [Pubsub_State]}; + Error -> + Error + end; + _Pusbub_Node -> + publish_item(Host, Pubsub_Host, {U,S,R}, Plugin, Pubsub_Features, + {undefined, Item, _ItemId, Payload, {[], Item_Options}}) + end; + +%% +publish_item(Host, Pubsub_Host, {U,S,R} = Entity, Plugin, Pubsub_Features, + {NodeId, Item, _ItemId, Payload, {[] = _Node_Options, Item_Options}}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + case + checks_on_publish_item(Host, {U,S,undefined}, Plugin, + {Item, Payload, _Node_Options = [], Item_Options}) + of + {ok, Pubsub_Features, Node_Options} -> + Options = {Node_Options, Item_Options}, + {ItemId, Pubsub_State} = pubsub_db_mnesia:publish_item1('dev', + Pubsub_Host, Entity, NodeId, _Level = 1, _ItemId, Payload, + Options, Pubsub_Features), + Subscription = case + Pubsub_State#pubsub_state_dev.subscriptions + of + [] -> undefined; + [_Subscription] -> _Subscription + end, + {result, NodeId, ItemId, Node_Options, + _Node_Owners = [{U,S,undefined}], + _SubId = case Subscription of + undefined -> + undefined; + {_Subscription_State, SubId, _Resource, + _Subscription_Options} -> + SubId + end, + Subscription, + {_Published_Items = case + get_value({Node_Options, Item_Options}, + 'deliver_notifications', false) + of + true -> + [{ItemId, + Item_Options, + _Payload = case + get_value({Node_Options, Item_Options}, + 'deliver_payloads', false) + of + true -> + case Payload of + [Xmlel] -> Xmlel; + [] -> [] + end; + false -> [] + end, + _Publisher = {U,S,R} + }]; + false -> + [] + end, + _Retracted_Items = []}, + _Pubsub_States = [Pubsub_State]}; + Error -> + Error + end; + Pubsub_Node -> + NodeIdx = Pubsub_Node#pubsub_node_dev.idx, + Pubsub_State = pubsub_db_mnesia:read_state('dev', + {U,S,undefined}, NodeIdx), + case + options_on_publish_item(Host, Entity, + _Affiliation = case Pubsub_State of + undefined -> 'none'; + _ -> Pubsub_State#pubsub_state_dev.affiliation + end, + _Subscriptions = case Pubsub_State of + undefined -> []; + _ -> Pubsub_State#pubsub_state_dev.subscriptions + end, + _Options = { + Node_Options = Pubsub_Node#pubsub_node_dev.options, + Item_Options + }, + Node_Owners = Pubsub_Node#pubsub_node_dev.owners, + Item, + Payload) + of + ok -> + {ItemId, Retracted_Items, Pubsub_States} = + pubsub_db_mnesia:publish_item2('dev', Pubsub_Host, + {U,S,R}, Pubsub_State, Pubsub_Node, + case _ItemId of + undefined -> + undefined; + _ -> + case + pubsub_db_mnesia:read_item('dev', + NodeIdx, _ItemId) + of + undefined -> _ItemId; + Pubsub_Item -> Pubsub_Item + end + end, + Payload, Item_Options, Pubsub_Features), + {result, NodeId, ItemId, Node_Options, Node_Owners, + _SubId = undefined, _Subscription = undefined, + {_Published_Items = case + get_value({Node_Options, Item_Options}, + 'deliver_notifications', false) + of + true -> + [{ItemId, + Item_Options, + _Payload = case + get_value({Node_Options, Item_Options}, + 'deliver_payloads', false) + of + true -> + case Payload of + [Xmlel] -> Xmlel; + [] -> [] + end; + false -> [] + end, + _Publisher = {U,S,R} + }]; + false -> + [] + end, + Retracted_Items}, + Pubsub_States}; + Error -> + Error + end + end. + + +%% +-spec(options_on_publish_item/8 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Entity :: xmpp_jid:usr_bare(), + Affiliation :: exmpp_pubsub:affiliation(), + Subscriptions :: exmpp_pubsub:subscriptions(), + Options :: {Node_Options :: pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item()}, + Node_Owners :: [Node_Owner::xmpp_jid:usr_bare(),...], + Item :: 0 | 1, + Payload :: exmpp_pubsub:payload() | [Xmlel::xmlel(),...]) + -> ok + %%% + | {error, 'forbidden'} + | {error, 'item-not-found'} + % + | {error, 'bad-request', 'invalid-payload'} + | {error, 'bad-request', 'item-required'} + | {error, 'bad-request', 'item-forbidden'} + | {error, 'not-acceptable', 'payload-too-big'} +). + +options_on_publish_item(_Host, _Entity, 'owner' = _Affiliation, _Subscriptions, + Options, _Node_Owners, Item, Payload) -> + options_on_publish_item(none, {Item, Payload, Options}); +%% +options_on_publish_item(Host, Entity, 'outcast' = _Affiliation, _Subscriptions, + {Node_Options, _Item_Options}, Node_Owners, _Item, _Payload) -> + case get_value(Node_Options, '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 = get_value(Node_Options, + 'roster_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; +%% +options_on_publish_item(Host, Entity, Affiliation, Subscriptions, + {Node_Options, Item_Options} = _Options, Node_Owners, Item, Payload) -> + %% 'pubsub#access_model' + case + check_access_model(Host, Entity, + _Access_Model = get_value(Node_Options, 'access_model'), + Affiliation, Subscriptions, Node_Owners, + _Rosters_Groups_Allowed = get_value(Node_Options, + 'roster_groups_allowed')) + of + ok -> + %% 'pubsub#publish_model' + case + check_publish_model( + _Publish_Model = get_value(Node_Options, 'publish_model'), + Affiliation, Subscriptions) + of + ok -> + options_on_publish_item(none, + {Item, Payload, {Node_Options, Item_Options}}); + Error -> + Error + end; + Error -> + Error + + end. + +%% +-spec(options_on_publish_item/2 :: +( + + Option :: none + | {pubsub_options:option_node_deliver_payloads(), + pubsub_options:option_node_persist_items()} + | pubsub_options:option_node_max_payload_size() + | pubsub_options:option_node_type(), + %% + Criteria :: {Item :: 0 | 1, + Payload :: exmpp_pubsub:payload() | [Xmlel::xmlel(),...], + Options :: {Node_Options :: pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item()}} + | {Item :: 0 | 1, + Payload :: exmpp_pubsub:payload(), + Options :: {Node_Options :: pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item()}} + % + | {Payload :: exmpp_pubsub:payload(), + Options :: {Node_Options :: pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item()}} + % + | {Payload :: exmpp_pubsub:payload()}) + -> %% + ok + %%% + | {error, 'bad-request', 'invalid-payload'} + | {error, 'bad-request', 'item-required'} + | {error, 'bad-request', 'payload-required'} + | {error, 'bad-request', 'item-forbidden'} + | {error, 'not-acceptable', 'payload-too-big'} +). + +%% Payloads count +options_on_publish_item(none, {Item, [] = Payload, Options}) -> + options_on_publish_item( + {get_option(Options, 'deliver_payloads', false), + get_option(Options, 'persist_items', false)}, + {Item, Payload, Options}); +%% +options_on_publish_item(none, {Item, [Payload], Options}) -> + options_on_publish_item( + {get_option(Options, 'deliver_payloads', false), + get_option(Options, 'persist_items', false)}, + {Item, Payload, Options}); +%% +options_on_publish_item(none, _Parameters) -> + {error, 'bad-request', 'invalid-payload'}; +%% +%% 'pubsub#deliver_payloads' +%% 'pubsub#persist_items' +options_on_publish_item( + {{'deliver_payloads', _Deliver_Payloads}, {'persist_items', true}}, + {0 = _Item, _Payload, _Options}) -> + {error, 'bad-request', 'item-required'}; +%% +options_on_publish_item( + {{'deliver_payloads', true}, {'persist_items', _Persist_Items}}, + {_Item, [] = _Payload, _Options}) -> + {error, 'bad-request', 'payload-required'}; +%% +options_on_publish_item( + {{'deliver_payloads', false}, {'persist_items', false}}, + {1 = _Item, _Payload, _Options}) -> + {error, 'bad-request', 'item-forbidden'}; +%% +options_on_publish_item( + {{'deliver_payloads', Deliver_Payloads}, {'persist_items', Persist_Items}}, + {_Item, [] = _Payload, Options}) -> + ok; +%% +options_on_publish_item( + {{'deliver_payloads', Deliver_Payloads}, {'persist_items', Persist_Items}}, + {_Item, Payload, {Node_Options, Item_Options} = _Options}) -> + options_on_publish_item( + _Max_Payload_Size = get_option(Node_Options, 'max_payload_size', 0), + {Payload, {Node_Options, Item_Options}}); +%% 'pubsub#max_payload_size' +options_on_publish_item({'max_payload_size', Max_Payload_Size}, + {Payload, Options}) -> + case byte_size(term_to_binary(Payload)) =< Max_Payload_Size of + true -> + options_on_publish_item(get_option(Options, 'type', undefined), + {Payload}); + false -> + {error, 'not-acceptable', 'payload-too-big'} + end; +%% 'pubsub#type' +options_on_publish_item({'type', Type}, {Payload}) + when Type == undefined -> + %%orelse Type == Payload#xmlel.ns -> + ok; +%% +options_on_publish_item(_, _) -> + {error, 'bad-request', 'invalid-payload'}. + + + +%% +-spec(checks_on_publish_item/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Entity :: xmpp_jid:usr_entity(), + Plugin :: exmpp_pubsub:plugin(), + Parameters :: {Item :: 0 | 1, + Payload :: [Xmlel::xmlel()], + Node_Options :: [], + Item_Options :: [] | pubsub_options:options_item()}) + -> {ok, + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Node_Options :: pubsub_options:options_node()} + %%% + | {error, 'forbidden'} + | {error, 'item-not-found'} + % + | {error, 'bad-request', 'invalid-payload'} + | {error, 'bad-request', 'item-required'} + | {error, 'bad-request', 'item-forbidden'} + | {error, 'not-acceptable', 'payload-too-big'} +). + + +checks_on_publish_item({U,S,_} = _Host, {U,S,_} = _Entity, Plugin, + {Item, Payload, [] = _Node_Options, Item_Options}) -> + case + lists:member(<<"auto-create">>, Pubsub_Features = Plugin:pubsub_features()) + of + true -> + case + options_on_publish_item(none, + {Item, + Payload, + _Options = { + Node_Options = Plugin:node_options('leaf'), + Item_Options + }}) + of + ok -> {ok, Pubsub_Features, Node_Options}; + Error -> Error + end; + false -> + {error, 'item-not-found'} + end; +%% +checks_on_publish_item(_Host, _Entity, _Plugin, _Parameters) + when is_tuple(_Host) -> + {error, 'foribdden'}; +%% +checks_on_publish_item(Host, {U,S,_R} = _Entity, Plugin, + {Item, Payload, [] = _Node_Options, Item_Options}) -> + case acl:match_rule(Host, pubsub_createnode, jlib:jid_to_string({U,S, <<>>})) of + allow -> + case + lists:member(<<"auto-create">>, + Pubsub_Features = Plugin:pubsub_features()) + of + true -> + case + options_on_publish_item(none, + {Item, Payload, + _Options = { + Node_Options = Plugin:node_options('leaf'), + Item_Options + }}) + of + ok -> {ok, Pubsub_Features, Node_Options}; + Error -> Error + end; + false -> + {error, 'item-not-found'} + end; + _Deny -> + {error, 'forbidden'} + end. + +%-- Retract_Item --% +-spec(retract_item/3 :: +( + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId(), + ItemId :: undefined | exmpp_pubsub:itemId(), + Adhoc_Notify_Retract :: undefined | boolean()}) + -> {result, + Notify :: [] + | [Notify_Retract :: { + Node_Owners :: [Entity::xmpp_jid:usr_bare(),...], + Node_Options :: pubsub_options:options_node(), + Item_Options :: [] | pubsub_options:options_item(), + Pubsub_States :: mod_pubsub_dev:pubsub_states() + }]} + %%% + | {error, 'bad-request', 'nodeid-required'} + | {error, 'bad-request', 'item-required'} + % + | {error, 'item-not-found'} + | {error, 'forbidden'} +). + +retract_item(_Pubsub_Host, _Entity, + {undefined = _NodeId, _ItemId, _Adhoc_Notify_Retract}) -> + {error, 'bad-request', 'nodeid-required'}; +%% +retract_item(_Pubsub_Host, _Entity, + {_NodeId, undefined = _ItemId, _Adhoc_Notify_Retract}) -> + {error, 'bad-request', 'item-required'}; +%% +retract_item(Pubsub_Host, Entity, {NodeId, ItemId, Adhoc_Notify_Retract}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + Pubsub_Node -> + case + pubsub_db_mnesia:read_state('dev', Entity, + Pubsub_Node#pubsub_node_dev.idx) + of + Pubsub_State + when Pubsub_State == undefined + orelse Pubsub_State#pubsub_state_dev.affiliation == 'outcast' -> + {error, 'forbidden'}; + Pubsub_State -> + case + pubsub_db_mnesia:retract_item('dev', Pubsub_Node, + Pubsub_State, ItemId, Adhoc_Notify_Retract) + of + {ok, undefined} -> + {result, _Notify = []}; + {ok, Item_Options, Pubsub_States} -> + {result, + _Notify = [_Notify_Retract = { + _Node_Owners = Pubsub_Node#pubsub_node_dev.owners, + _Node_Options = Pubsub_Node#pubsub_node_dev.options, + Item_Options, + Pubsub_States + }] + }; + Error -> + Error + end + end + end. + + +%-- Subscribe_Node --% +-spec(subscribe_node/7 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Plugin :: exmpp_pubsub:plugin(), + Options_Module :: module(), + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Parameters :: {NodeId :: exmpp_pubsub:nodeId(), + Resource :: undefined | xmpp_jid:resource_jid(), + Subscribe_Options :: [Xmlel::xmlel(),...] + | [Xmlel::xmlel()]}) + -> {result, + Subscription_Subscribed :: exmpp_pubsub:subscription_subscribed(), + Affiliation :: 'member' | 'owner' | 'publisher', + Node_Owners :: [Entity::xmpp_jid:usr_bare(),...], + Node_Options :: pubsub_options:options_node(), + Pubsub_Last_Item :: undefined | mod_pubsub_dev:pubsub_last_item()} + % + | {result, + Subscription_Pending :: exmpp_pubsub:subscription_pending(), + Affiliation :: 'member', + Node_Owners :: [Entity::xmpp_jid:usr_bare(),...], + Node_Options :: pubsub_options:options_node(), + Pubsub_Last_Item :: undefined} + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + % + | {error, 'invalid-options'} + | {error, 'not-acceptable'} + % + | {error, 'not-authorized', 'presence-subscription-required'} + | {error, 'not-authorized', 'not-in-roster-group'} + | {error, 'not-authorized', 'pending-subscription'} + | {error, 'not-allowed', 'closed-node'} +). + +subscribe_node(_Host, _Pubsub_Host, _Entity, _Plugin, _Options_Module, + _Pubsub_Features, {undefined = _NodeId, _Resource, _Subscribe_Options}) -> + {error, 'item-not-found'}; +%% +subscribe_node(Host, Pubsub_Host, {_, S,_} = Entity, Plugin, Options_Module, + Pubsub_Features, {NodeId, Resource, Subscribe_Options}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, options = Node_Options} -> + case + Options_Module:parse_xmlel_x(Plugin, Pubsub_Features, Entity, + _Entity_Type = case S == Host of + true -> 'local'; + false -> 'remote' + end, + 'subscribe_options', + _Node_Type = get_value(Node_Options, 'node_type', 'leaf'), + Subscribe_Options) + of + {ok, Subscription_Options} -> + Pubsub_State = case + pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) + of + undefined -> + #pubsub_state_dev{ + id = {Entity, NodeIdx}, + nodeidx = NodeIdx + }; + _Pubsub_State -> + _Pubsub_State + end, + case + pubsub_db_mnesia:subscribe_node('dev', Host, Node_Options, + Node_Owners, Pubsub_State, Resource, + Subscription_Options, Pubsub_Features) + of + {result, Subscription, Pubsub_Last_Item} -> + {result, + Subscription, + case Pubsub_State#pubsub_state_dev.affiliation of + 'none' -> 'member'; + Affiliation -> Affiliation + end, + Node_Owners, Node_Options, Pubsub_Last_Item}; + Error -> + Error + end; + Error -> + Error + end + end. + +%-- Unsubscribe_Node --% +-spec(unsubscribe_node/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: exmpp_pubsub:nodeId(), + _Resource :: undefined + | xmpp_jid:resource_jid() + | {'caps', xmpp_jid:resource_jid()}, + _SubId :: exmpp_pubsub:subId()}) + -> {result, + SubId :: exmpp_pubsub:subId()} + | + {result, + SubId :: exmpp_pubsub:subId(), + Node_Owners :: [Entity::xmpp_jid:usr_bare(),...], + Notification_Type :: pubsub_options:notification_type()} + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + | {error, 'unexpected', 'not-subscribed'} + % + | {error, 'unexpected', 'not-subscribed'} + | {error, 'not-acceptable', 'invalid-subid'} + | {error, 'bad-request', 'subid-required'} + | {error, 'not-acceptable', 'invalid-subid'} +). + +unsubscribe_node(_Host, _Pubsub_Host, _Entity, + {undefined = _NodeId, _Resource, _SubId}) -> + {error, 'item-not-found'}; +%% +unsubscribe_node(Host, Pubsub_Host, Entity, {NodeId, _Resource, _SubId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, options = Node_Options} -> + case pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) of + Pubsub_State + when Pubsub_State == undefined + orelse Pubsub_State#pubsub_state_dev.affiliation == 'none' + orelse Pubsub_State#pubsub_state_dev.subscriptions == [] -> + {error, 'unexpected', 'not-subscribed'}; + Pubsub_State + when Pubsub_State#pubsub_state_dev.affiliation == 'publish-only'-> + {error, 'forbidden'}; + Pubsub_State -> + case + pubsub_db_mnesia:unsubscribe_node('dev', Host, + _Node_Access_Model = get_value(Node_Options, 'access_model'), + Pubsub_State, _Resource, _SubId) + of + {ok, Resource, SubId} -> + case get_value(Node_Options, 'notify_sub', false) of + true -> + {result, SubId, Node_Owners, + get_value(Node_Options, 'notification_type', + 'headline')}; + false -> + {result, SubId} + end; + Error -> + Error + end + end + end. + +%-- Set_Configure_Subscription --% +-spec(set_configure_subscription/7 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Plugin :: exmpp_pubsub:plugin(), + Options_Module :: module(), + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId(), + SubId :: undefined | exmpp_pubsub:subId(), + Resource :: undefined + | xmpp_jid:resource_jid() + | {'caps', xmpp_jid:resource_jid()}, + Subscribe_Options :: [Xmlel::xmlel(),...] + | [Xmlel::xmlel()]}) + -> {result, []} + %%% + | {error, 'item-not-found'} + | {error, 'unexpected-request', 'not-subscribed'} + | {error, 'forbidden'} + | {error, 'bad-request', 'invalid-options'} + % + | {error, 'unexpected-request', 'not-subscribed'} + | {error, 'bad-request', 'subid-required'} + | {error, 'forbidden'} + | {error, 'not-acceptable', 'invalid-subid'} +). + +set_configure_subscription(_Host, _Pubsub_Host, _Entity, _Plugin, _Options_Module, + _Pubsub_Features, {undefined = _NodeId, _SubId, _Resource, _Subscribe_Options}) -> + {error, 'item-not-found'}; +%% +set_configure_subscription(Host, Pubsub_Host, {_,S,_} = Entity, Plugin, Options_Module, + Pubsub_Features, {NodeId, SubId, Resource, Subscribe_Options}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, options = Node_Options} -> + case pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) of + undefined -> + {error, 'unexpected-request', 'not-subscribed'}; + #pubsub_state_dev{affiliation = Affiliation} + when Affiliation == 'publish-only' + orelse Affiliation == 'outcast' -> + {error, 'forbidden'}; + #pubsub_state_dev{subscriptions = []} -> + {error, 'unexpected-request', 'not-subscribed'}; + Pubsub_State -> + case + Options_Module:parse_xmlel_x(Plugin, Pubsub_Features, Entity, + _Entity_Type = case S == Host of + true -> 'local'; + false -> 'remote' + end, + 'subscribe_options', + _Node_Type = get_value(Node_Options, 'node_type', 'leaf'), + Subscribe_Options) + of + {ok, Subscription_Options} -> + case + pubsub_db_mnesia:set_configure_subscription('dev', + Pubsub_State, SubId, Resource, + Subscription_Options) + of + ok -> + {result, []}; + Error -> + Error + end; + _Error -> + {error, 'bad-request', 'invalid-options'} + end + end + end. + +%-- Get_Items --% +-spec(get_items/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pusbub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId(), + _SubId :: undefined | exmpp_pubsub:subId(), + Max_Items :: non_neg_integer(), + ItemIds :: undefined} + | {NodeId :: undefined | exmpp_pubsub:nodeId(), + _SubId :: undefined | exmpp_pubsub:subId(), + Max_Items :: undefined, + ItemIds :: [] | exmpp_pubsub:itemIds()}) + -> {result, + Pubsub_Items :: [] | mod_pubsub_dev:pubsub_items(), + Cache :: {Presence_Cache :: undefined, + Groups_Cache :: undefined} + | {Presence_Cache :: true, + Groups_Cache :: undefined} + | {Presence_Cache :: undefined, + Groups_Cache :: [{Node_Owner :: xmpp_jid:usr_bare(), + Groups :: [Group::binary()]}]}, + Node_Owners :: mod_pubsub_dev:node_owners(), + Node_Access_Model :: pubsub_options:access_model(), + Node_Groups :: pubsub_options:rosters_groups_allowed(), + Affiliation :: exmpp_pubsub:affiliation(), + Access :: undefined | 'presence' | 'roster' | 'pending' | 'authorize'} + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + | {error, 'not-authorized', 'presence-subscription-required'} + | {error, 'not-authorized', 'not-in-roster-group'} + | {error, 'not-allowed', 'closed-node'} +). + +get_items(_Host, _Pubsub_Host, _Entity, + {undefined = _NodeId, _SubId, _Max_Items, _ItemIds}) -> + {error, 'item-not-found'}; +%% +get_items(Host, Pubsub_Host, Entity, {NodeId, _SubId, Max_Items, ItemIds}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, + options = Node_Options, itemids = Node_ItemIds} -> + {Affiliation, Access} = case lists:member(Entity, Node_Owners) of + true -> + {'owner', undefined}; + false -> + case pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) of + undefined -> + {'none', undefined}; + #pubsub_state_dev{affiliation = _Affiliation, + access = _Access} -> + {_Affiliation, _Access} + end + end, + Node_Access_Model = get_value(Node_Options, 'access_model'), + Node_Groups = get_value(Node_Options, 'roster_groups_allowed', []), + case + pubsub_db_mnesia:get_items('dev', Host, + {NodeIdx, Node_Owners, Node_ItemIds, Node_Access_Model, Node_Groups}, + {Entity, Affiliation, Access}, + {Max_Items, ItemIds, _SubId}) + of + {ok, Pubsub_Items, Cache} -> + {result, Pubsub_Items, Cache, Node_Owners, Node_Access_Model, + Node_Groups, Affiliation, Access}; + Error -> + Error + end + end. + +%-- Get_Entity_Affiliations --% +-spec(get_entity_affiliations/3 :: +( + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> Entity_Affiliations :: [Entity_Affiliation :: { + NodeId :: exmpp_pubsub:nodeId(), + Affiliation :: exmpp_pubsub:affiliation() + }] +). + +get_entity_affiliations(Pubsub_Host, Entity, {NodeId}) -> + pubsub_db_mnesia:get_entity_affiliations('dev', Pubsub_Host, Entity, NodeId). + +%-- Get_Entity_Subscriptions --% +-spec(get_entity_subscriptions/3 :: +( + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> Entity_Subscriptions :: [{ + NodeId :: exmpp_pubsub:nodeId(), + Subscriptions :: _%exmpp_pubsub:subscriptions() + }] +). + +get_entity_subscriptions(Pubsub_Host, Entity, {NodeId}) -> + pubsub_db_mnesia:get_entity_subscriptions('dev', Pubsub_Host, Entity, NodeId). + + +%-- Get_Node_Affiliations --% +-spec(get_node_affiliations/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> {result, Pubsub_States::[Pubsub_State::mod_pubsub_dev:pubsub_state(),...]} + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + +). + +get_node_affiliations(_Host, _Pubsub_Host, _Entity, {undefined = _NodeId}) -> + {error, 'item-not-found'}; +%% +get_node_affiliations(Host, Pubsub_Host, Entity, {NodeId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Owners, options = Options} -> + case lists:member(Entity, Owners) of + true -> + {result, + _Pubsub_States = pubsub_db_mnesia:index_read_state('dev', + NodeIdx)}; + false -> + %% Depending on the node access_model, the returned error + %% can be 'forbidden' or 'item-not-found' to not disclose + %% private nodes + case get_value(Options, 'access_model') of + %% + 'open' -> + {error, 'forbidden'}; + %% + 'presence' -> + case + is_contact_subscribed_to_node_owners(Host, + Entity, Owners) + of + false -> + {error, 'item-not-found'}; + _Node_Owner -> + {error, 'forbidden'} + end; + %% + 'roster' -> + case + is_contact_in_allowed_roster_groups(Entity, + _Rosters_Groups_Allowed = get_value( + Options, 'roster_groups_allowed')) + of + false -> + {error, 'item-not-found'}; + {_Node_Owner, _Roster_Group_Allowed} -> + {error, 'forbidden'} + end; + %% + 'authorize' -> + {error, 'forbidden'}; + %% + 'whitelist' -> + case + pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) + of + #pubsub_state_dev{affiliation = Affiliation} + when Affiliation == 'member' + orelse Affiliation == 'publish-only' + orelse Affiliation == 'publisher' -> + {error, 'forbidden'}; + _Pubsub_State -> + {error, 'item-not-found'} + end + end + end + end. + +%-- Get_Node_Subscriptions --% +-spec(get_node_subscriptions/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> {result, Pubsub_States::[Pubsub_State::mod_pubsub_dev:pubsub_state(),...]} + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} + +). + +get_node_subscriptions(_Host, _Pubsub_Host, _Entity, {undefined = _NodeId}) -> + {error, 'item-not-found'}; +%% +get_node_subscriptions(Host, Pubsub_Host, Entity, {NodeId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Owners, options = Options} -> + case lists:member(Entity, Owners) of + true -> + {result, + _Pubsub_States = pubsub_db_mnesia:index_read_state('dev', + NodeIdx)}; + false -> + %% Depending on the node access_model, the returned error + %% can be 'forbidden' or 'item-not-found' to not disclose + %% private nodes + case get_value(Options, 'access_model') of + %% + 'open' -> + {error, 'forbidden'}; + %% + 'presence' -> + case + is_contact_subscribed_to_node_owners(Host, + Entity, Owners) + of + false -> + {error, 'item-not-found'}; + _Node_Owner -> + {error, 'forbidden'} + end; + %% + 'roster' -> + case + is_contact_in_allowed_roster_groups(Entity, + _Rosters_Groups_Allowed = get_value(Options, + 'roster_groups_allowed')) + of + false -> + {error, 'item-not-found'}; + {_Node_Owner, _Roster_Group_Allowed} -> + {error, 'forbidden'} + end; + %% + 'authorize' -> + {error, 'forbidden'}; + %% + 'whitelist' -> + case + pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) + of + #pubsub_state_dev{affiliation = Affiliation} + when Affiliation == 'member' + orelse Affiliation == 'publish-only' + orelse Affiliation == 'publisher' -> + {error, 'forbidden'}; + _Pubsub_State -> + {error, 'item-not-found'} + end + end + end + end. + +%% Get_Configure_Subscription_Default +-spec(get_configure_subscription_default/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + NodeId :: exmpp_pubsub:nodeId()) + -> ok + %%% + | {error, 'item-not-found'} + | {error, 'forbidden'} +). + +get_configure_subscription_default(Host, Pubsub_Host, Entity, NodeId) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, options = Node_Options} -> + case lists:member(Entity, Node_Owners) of + true -> + ok; + false -> + Node_Access_Model = get_value(Node_Options, 'access_model'), + case pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) of + undefined + when Node_Access_Model == 'open' -> + ok; + undefined + when Node_Access_Model == 'presence' -> + case + is_contact_subscribed_to_node_owners(Host, + Entity, Node_Owners) + of + false -> {error, 'forbidden'}; + _Node_Owner -> ok + end; + undefined + when Node_Access_Model == 'roster' -> + case + is_contact_in_allowed_roster_groups(Entity, + _Rosters_Groups_Allowed = + get_value(Node_Options, + 'roster_groups_allowed', [])) + of + false -> {error, 'forbidden'}; + _ -> ok + end; + undefined -> + {error, 'forbidden'}; + #pubsub_state_dev{affiliation = Affiliation, + access = Access} + when Affiliation == 'publisher' + orelse Affiliation == 'member' + orelse (Affiliation == 'none' + andalso + Node_Access_Model == 'open') + orelse Access == 'presence' + orelse Access == 'roster' -> + ok; + #pubsub_state_dev{affiliation = 'none'} + when Node_Access_Model == 'presence' -> + case + is_contact_subscribed_to_node_owners(Host, + Entity, Node_Owners) + of + false -> + {error, 'forbidden'}; + _Node_Owner -> + ok + end; + #pubsub_state_dev{affiliation = 'none'} + when Node_Access_Model == 'roster' -> + case + is_contact_in_allowed_roster_groups(Entity, + _Rosters_Groups_Allowed = + get_value(Node_Options, + 'roster_groups_allowed', [])) + of + false -> + {error, 'forbidden'}; + _ -> + ok + end; + _ -> + {error, 'forbidden'} + + end + end + end. + +%-- Get_Configure_Subscription --% +-spec(get_configure_subscription/6 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Plugin :: exmpp_pubsub:plugin(), + Pubsub_Features :: exmpp_pubsub:pubsub_features(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId(), + SubId :: undefined | exmpp_pubsub:subId(), + Resource :: undefined + | xmpp_jid:resource_jid() + | {'caps', xmpp_jid:resource_jid()} + }) + -> {result, + Subscription :: exmpp_pubsub:subscription_subscribed() + | exmpp_pubsub:subscription_unconfigured()} + %%% + | {error, 'item-not-found'} + | {error, 'unexpected-request', 'not-subscribed'} + | {error, 'forbidden'} + % + | {error, 'unexpected-request', 'not-subscribed'} + | {error, 'bad-request', 'subid-required'} + | {error, 'forbidden'} + | {error, 'not-acceptable', 'invalid-subid'} +). + +get_configure_subscription(_Host, _Pubsub_Host, _Entity, _Plugin, + _Pubsub_Features, {undefined = _NodeId, _SubId, _Resource}) -> + {error, 'item-not-found'}; +%% +get_configure_subscription(_Host, Pubsub_Host, Entity, Plugin, + Pubsub_Features, {NodeId, SubId, Resource}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{idx = NodeIdx, owners = Node_Owners, options = Node_Options} -> + case pubsub_db_mnesia:read_state('dev', Entity, NodeIdx) of + undefined -> + {error, 'unexpected-request', 'not-subscribed'}; + #pubsub_state_dev{affiliation = Affiliation} + when Affiliation == 'publish-only' + orelse Affiliation == 'outcast' -> + {error, 'forbidden'}; + #pubsub_state_dev{subscriptions = []} -> + {error, 'unexpected-request', 'not-subscribed'}; + #pubsub_state_dev{subscriptions = Subscriptions} -> + case filter_subscription(Subscriptions, SubId, Resource) of + {ok, Subscription} -> {result, Subscription}; + Error -> Error + end + end + end. + +-spec(filter_subscription/3 :: +( + Subscriptions :: exmpp_pubsub:subscriptions(), + SubId :: undefined | exmpp_pubsub:subId(), + Resource :: undefined + | xmpp_jid:resource_jid() + | {'caps', xmpp_jid:resource_jid()}) + -> {ok, + Subscription :: exmpp_pubsub:subscription_subscribed() + | exmpp_pubsub:subscription_unconfigured()} + %%% + | {error, 'unexpected-request', 'not-subscribed'} + | {error, 'bad-request', 'subid-required'} + | {error, 'forbidden'} + | {error, 'not-acceptable', 'invalid-subid'} +). + +filter_subscription(Subscriptions, undefined = _SubId, Resource) -> + case lists:keytake(Resource, 3, Subscriptions) of + {value, {Subscription_State, SubId, Resource, Subscription_Options}, + _Subscriptions} -> + case lists:keymember(Resource, 3, _Subscriptions) of + false + when Subscription_State == 'pending' -> + {error, 'forbidden'}; + false -> + {ok, + {Subscription_State, SubId, Resource, Subscription_Options}}; + true -> + {error, 'bad-request', 'subid-required'} + end; + false -> + {error, 'unexpected-request', 'not-subscribed'} + end; +%% +filter_subscription(Subscriptions, SubId, Resource) -> + case lists:keyfind(SubId, 2, Subscriptions) of + %% + {'pending', _, Resource, _} -> + {error, 'forbidden'}; + %% + {Subscription_State, SubId, Resource, Subscription_Options} -> + {ok, {Subscription_State, SubId, Resource, Subscription_Options}}; + %% + {_, SubId, _, _} -> + {error, 'unexpected-request', 'not-subscribed'}; + false -> + case lists:keyfind(Resource, 3, Subscriptions) of + {'pending', _, Resource, _} -> + {error, 'forbidden'}; + {_, _, Resource, _} -> + {error, 'not-acceptable', 'invalid-subid'}; + _ -> + {error, 'unexpected-request', 'not-subscribed'} + end + end. + +%-- Get_Configure_Node --% +-spec(get_configure_node/4 :: +( + Host :: xmpp_jid:raw_jid_component_bare(), + Pubsub_Host :: exmpp_pubsub:host(), + Entity :: xmpp_jid:usr_bare(), + Parameters :: {NodeId :: undefined | exmpp_pubsub:nodeId()}) + -> {result, Node_Options :: pubsub_options:options_node()} + %%% + | {error, 'bad-request', 'nodeid-required'} + | {error, 'item-not-found'} + | {error, 'forbidden'} +). + +get_configure_node(_Host, _Pubsub_Host, _Entity, {undefined = _NodeId}) -> + {error, 'bad-request', 'nodeid-required'}; +%% +get_configure_node(_Host, Pubsub_Host, Entity, {NodeId}) -> + case pubsub_db_mnesia:read_node('dev', Pubsub_Host, NodeId) of + undefined -> + {error, 'item-not-found'}; + #pubsub_node_dev{owners = Node_Owners, options = Node_Options} -> + case lists:member(Entity, Node_Owners) of + true -> {result, Node_Options}; + false -> {error, 'forbidden'} + end + end. |
