25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

PubSub improvements

This commit contains
- code cleanup
- use of db_type instead of old mod_pubsub_odbc
- some minor optimizations
- some minor bugfixes
This commit is contained in:
Christophe Romain 2015-04-08 17:12:05 +02:00
parent 63926efd20
commit e0563e3918
31 changed files with 6999 additions and 15289 deletions

View File

@ -27,8 +27,7 @@
%% ------------------------------- %% -------------------------------
%% Pubsub constants %% Pubsub constants
-define(ERR_EXTENDED(E, C), -define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)).
mod_pubsub:extended_error(E, C)).
%% The actual limit can be configured with mod_pubsub's option max_items_node %% The actual limit can be configured with mod_pubsub's option max_items_node
-define(MAXITEMS, 10). -define(MAXITEMS, 10).
@ -40,7 +39,6 @@
%% ------------------------------- %% -------------------------------
%% Pubsub types %% Pubsub types
%% @type hostPubsub() = string().
-type(hostPubsub() :: binary()). -type(hostPubsub() :: binary()).
%% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be %% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
%% <tt>"pubsub.localhost"</tt>.</p> %% <tt>"pubsub.localhost"</tt>.</p>
@ -59,12 +57,15 @@
-type(nodeId() :: binary()). -type(nodeId() :: binary()).
%% @type nodeId() = binary(). %% @type nodeId() = binary().
%% <p>A node is defined by a list of its ancestors. The last element is the name %% <p>A node is defined by a list of its ancestors. The last element is the name
%% of the current node. For example:
%% of the current node. For example: %% of the current node. For example:
%% ```<<"/home/localhost/user">>'''</p> %% ```<<"/home/localhost/user">>'''</p>
-type(nodeIdx() :: pos_integer()). -type(nodeIdx() :: pos_integer() | binary()).
%% @type nodeIdx() = integer(). %% @type nodeIdx() = integer() | binary().
%% note: pos_integer() should always be used, but we allow anything else coded
%% as binary, so one can have a custom implementation of nodetree with custom
%% indexing (see nodetree_virtual). this also allows to use any kind of key for
%% indexing nodes, as this can be usefull with external backends such as odbc.
-type(itemId() :: binary()). -type(itemId() :: binary()).
%% @type itemId() = string(). %% @type itemId() = string().
@ -72,28 +73,12 @@
-type(subId() :: binary()). -type(subId() :: binary()).
%% @type subId() = string(). %% @type subId() = string().
%% @type payload() = [#xmlelement{} | #xmlcdata{}].
%% @type stanzaError() = #xmlelement{}.
%% Example:
%% Example:
%% ```{xmlelement, "error",
%% [{"code", Code}, {"type", Type}],
%% [{xmlelement, Condition, [{"xmlns", ?NS_STANZAS}], []}]}'''
%% @type pubsubIQResponse() = #xmlelement{}.
%% Example:
%% ```{xmlelement, "pubsub",
%% [{"xmlns", ?NS_PUBSUB_EVENT}],
%% [{xmlelement, "affiliations", [],
%% []}]}'''
-type(nodeOption() :: -type(nodeOption() ::
{Option::atom(), {Option::atom(),
Value::binary() | [binary()] | boolean() | non_neg_integer() Value::atom() | [binary()] | boolean() | non_neg_integer()
}). }).
-type(nodeOptions() :: [NodeOption::mod_pubsub:nodeOption(),...]). -type(nodeOptions() :: [mod_pubsub:nodeOption(),...]).
%% @type nodeOption() = {Option, Value} %% @type nodeOption() = {Option, Value}
%% Option = atom() %% Option = atom()
@ -106,26 +91,9 @@
Value::binary() | [binary()] | boolean() Value::binary() | [binary()] | boolean()
}). }).
-type(subOptions() :: [SubOption::mod_pubsub:subOption(),...]). -type(subOptions() :: [mod_pubsub:subOption(),...]).
%% @type nodeType() = string().
%% <p>The <tt>nodeType</tt> is a string containing the name of the PubSub
%% plugin to use to manage a given node. For example, it can be
%% <tt>"flat"</tt>, <tt>"hometree"</tt> or <tt>"blog"</tt>.</p>
%% @type jid() = {jid, User, Server, Resource, LUser, LServer, LResource}
%% User = string()
%% Server = string()
%% Resource = string()
%% LUser = string()
%% LServer = string()
%% LResource = string().
%-type(ljid() :: {binary(), binary(), binary()}).
%% @type ljid() = {User, Server, Resource}
%% User = string()
%% Server = string()
%% Resource = string().
-type(affiliation() :: 'none' -type(affiliation() :: 'none'
| 'owner' | 'owner'
@ -151,16 +119,11 @@
). ).
%% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'. %% @type accessModel() = 'open' | 'presence' | 'roster' | 'authorize' | 'whitelist'.
%% @type pubsubIndex() = {pubsub_index, Index, Last, Free}
%% Index = atom()
%% Last = integer()
%% Free = [integer()].
%% internal pubsub index table
-type(publishModel() :: 'publishers' -type(publishModel() :: 'publishers'
| 'subscribers' | 'subscribers'
| 'open' | 'open'
). ).
%% @type publishModel() = 'publishers' | 'subscribers' | 'open'
-record(pubsub_index, -record(pubsub_index,
{ {
@ -169,91 +132,42 @@
free :: [mod_pubsub:nodeIdx()] free :: [mod_pubsub:nodeIdx()]
}). }).
%% @type pubsubNode() = {pubsub_node, NodeId, Id, Parents, Type, Owners, Options}
%% NodeId = {host() | ljid(), nodeId()}
%% Id = nodeIdx()
%% Parents = [nodeId()]
%% Type = nodeType()
%% Owners = [ljid()]
%% Options = [nodeOption()].
%% <p>This is the format of the <tt>nodes</tt> table. The type of the table
%% is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%% <p>The <tt>Parents</tt> and <tt>type</tt> fields are indexed.</p>
%% <tt>id</tt> can be anything you want.
-record(pubsub_node, -record(pubsub_node,
{ {
nodeid ,%:: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()}, nodeid ,% :: {mod_pubsub:host(), mod_pubsub:nodeId()},
id ,%:: mod_pubsub:nodeIdx(), id ,% :: mod_pubsub:nodeIdx(),
parents = [] ,%:: [Parent_NodeId::mod_pubsub:nodeId()], parents = [] ,% :: [mod_pubsub:nodeId(),...],
type = <<"flat">> ,%:: binary(), type = <<"flat">>,% :: binary(),
owners = [] ,%:: [Owner::ljid(),...], owners = [] ,% :: [jlib:ljid(),...],
options = [] %:: mod_pubsub:nodeOptions() options = [] % :: mod_pubsub:nodeOptions()
}). }).
%% @type pubsubState() = {pubsub_state, StateId, Items, Affiliation, Subscriptions}
%% StateId = {ljid(), nodeIdx()}
%% Items = [itemId()]
%% Affiliation = affiliation()
%% Subscriptions = [{subscription(), subId()}].
%% <p>This is the format of the <tt>affiliations</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%-record(pubsub_state,
% {stateid, items = [], affiliation = none,
% subscriptions = []}).
-record(pubsub_state, -record(pubsub_state,
{ {
stateid ,%:: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()}, stateid ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
items = [] ,%:: [ItemId::mod_pubsub:itemId()], items = [] ,% :: [mod_pubsub:itemId(),...],
affiliation = 'none' ,%:: mod_pubsub:affiliation(), affiliation = 'none',% :: mod_pubsub:affiliation(),
subscriptions = [] %:: [{mod_pubsub:subscription(), mod_pubsub:subId()}] subscriptions = [] % :: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
}). }).
%% @type pubsubItem() = {pubsub_item, ItemId, Creation, Modification, Payload}
%% ItemId = {itemId(), nodeIdx()}
%% Creation = {now(), ljid()}
%% Modification = {now(), ljid()}
%% Payload = payload().
%% <p>This is the format of the <tt>published items</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>disc</tt>,<tt>fragmented</tt>.</p>
%-record(pubsub_item,
% {itemid, creation = {unknown, unknown},
% modification = {unknown, unknown}, payload = []}).
-record(pubsub_item, -record(pubsub_item,
{ {
itemid ,%:: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()}, itemid ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
creation = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()}, creation = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
modification = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()}, modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
payload = [] %:: mod_pubsub:payload() payload = [] % :: mod_pubsub:payload()
}). }).
%% @type pubsubSubscription() = {pubsub_subscription, SubId, Options}
%% SubId = subId()
%% Options = [nodeOption()].
%% <p>This is the format of the <tt>subscriptions</tt> table. The type of the
%% table is: <tt>set</tt>,<tt>ram/disc</tt>.</p>
%-record(pubsub_subscription, {subid, options}).
-record(pubsub_subscription, -record(pubsub_subscription,
{ {
subid ,%:: mod_pubsub:subId(), subid ,% :: mod_pubsub:subId(),
options %:: [] | mod_pubsub:subOptions() options = [] % :: mod_pubsub:subOptions()
}). }).
%% @type pubsubLastItem() = {pubsub_last_item, NodeId, ItemId, Creation, Payload}
%% NodeId = nodeIdx()
%% ItemId = itemId()
%% Creation = {now(),ljid()}
%% Payload = payload().
%% <p>This is the format of the <tt>last items</tt> table. it stores last item payload
%% for every node</p>
%-record(pubsub_last_item,
% {nodeid, itemid, creation, payload}).
-record(pubsub_last_item, -record(pubsub_last_item,
{ {
nodeid ,%:: mod_pubsub:nodeIdx(), nodeid ,% :: mod_pubsub:nodeIdx(),
itemid ,%:: mod_pubsub:itemId(), itemid ,% :: mod_pubsub:itemId(),
creation ,%:: {erlang:timestamp(), ljid()}, creation ,% :: {erlang:timestamp(), jlib:ljid()},
payload %:: mod_pubsub:payload() payload % :: mod_pubsub:payload()
}). }).

View File

@ -32,191 +32,146 @@
-include("jlib.hrl"). -include("jlib.hrl").
-type(host() :: mod_pubsub:host() -type(host() :: mod_pubsub:host()).
| mod_pubsub_odbc:host() -type(nodeId() :: mod_pubsub:nodeId()).
). -type(nodeIdx() :: mod_pubsub:nodeIdx()).
-type(itemId() :: mod_pubsub:itemId()).
-type(nodeId() :: mod_pubsub:nodeId() -type(pubsubNode() :: mod_pubsub:pubsubNode()).
| mod_pubsub_odbc:nodeId() -type(pubsubState() :: mod_pubsub:pubsubState()).
). -type(pubsubItem() :: mod_pubsub:pubsubItem()).
-type(subOptions() :: mod_pubsub:subOptions()).
-type(nodeIdx() :: mod_pubsub:nodeIdx() -type(affiliation() :: mod_pubsub:affiliation()).
| mod_pubsub_odbc:nodeIdx() -type(subscription() :: mod_pubsub:subscription()).
). -type(subId() :: mod_pubsub:subId()).
-type(accessModel() :: mod_pubsub:accessModel()).
-type(itemId() :: mod_pubsub:itemId() -type(publishModel() :: mod_pubsub:publishModel()).
| mod_pubsub_odbc:itemId() -type(payload() :: mod_pubsub:payload()).
).
-type(pubsubNode() :: mod_pubsub:pubsubNode()
| mod_pubsub_odbc:pubsubNode()
).
-type(pubsubState() :: mod_pubsub:pubsubState()
| mod_pubsub_odbc:pubsubState()
).
-type(pubsubItem() :: mod_pubsub:pubsubItem()
| mod_pubsub_odbc:pubsubItem()
).
-type(nodeOptions() :: mod_pubsub:nodeOptions()
| mod_pubsub_odbc:nodeOptions()
).
-type(subOptions() :: mod_pubsub:subOptions()
| mod_pubsub_odbc:subOptions()
).
-type(affiliation() :: mod_pubsub:affiliation()
| mod_pubsub_odbc:affiliation()
).
-type(subscription() :: mod_pubsub:subscription()
| mod_pubsub_odbc:subscription()
).
-type(subId() :: mod_pubsub:subId()
| mod_pubsub_odbc:subId()
).
-type(accessModel() :: mod_pubsub:accessModel()
| mod_pubsub_odbc:accessModel()
).
-type(publishModel() :: mod_pubsub:publishModel()
| mod_pubsub_odbc:publishModel()
).
-type(payload() :: mod_pubsub:payload()
| mod_pubsub_odbc:payload()
).
-callback init(Host :: binary(), -callback init(Host :: binary(),
ServerHost :: binary(), ServerHost :: binary(),
Opts :: [any()]) -> atom(). Opts :: [any()]) -> atom().
-callback terminate(Host :: host(), -callback terminate(Host :: host(),
ServerHost :: binary()) -> atom(). ServerHost :: binary()) -> atom().
-callback options() -> [{atom(), any()}]. -callback options() -> [{atom(), any()}].
-callback features() -> [binary()]. -callback features() -> [binary()].
-callback create_node_permission(Host :: host(), -callback create_node_permission(Host :: host(),
ServerHost :: binary(), ServerHost :: binary(),
Node :: nodeId(), Node :: nodeId(),
ParentNode :: nodeId(), ParentNode :: nodeId(),
Owner :: jid(), Access :: atom()) -> Owner :: jid(), Access :: atom()) ->
{result, boolean()}. {result, boolean()}.
-callback create_node(NodeIdx :: nodeIdx(), -callback create_node(NodeIdx :: nodeIdx(),
Owner :: jid()) -> Owner :: jid()) ->
{result, {default, broadcast}}. {result, {default, broadcast}}.
-callback delete_node(Nodes :: [pubsubNode(),...]) -> -callback delete_node(Nodes :: [pubsubNode(),...]) ->
{result, {result,
{default, broadcast, {default, broadcast,
[{pubsubNode(), [{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...] [{ljid(), [{subscription(), subId()}]},...]},...]
} }
} }
| |
{result, {result,
{[], {[],
[{pubsubNode(), [{pubsubNode(),
[{ljid(), [{subscription(), subId()}]},...]},...] [{ljid(), [{subscription(), subId()}]},...]},...]
} }
}. }.
-callback purge_node(NodeIdx :: nodeIdx(), -callback purge_node(NodeIdx :: nodeIdx(),
Owner :: jid()) -> Owner :: jid()) ->
{result, {default, broadcast}} | {result, {default, broadcast}} |
{error, xmlel()}. {error, xmlel()}.
-callback subscribe_node(NodeIdx :: nodeIdx(), -callback subscribe_node(NodeIdx :: nodeIdx(),
Sender :: jid(), Sender :: jid(),
Subscriber :: ljid(), Subscriber :: jid(),
AccessModel :: accessModel(), AccessModel :: accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence', SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(), PresenceSubscription :: boolean(),
RosterGroup :: boolean(), RosterGroup :: boolean(),
Options :: subOptions()) -> Options :: subOptions()) ->
{result, {default, subscribed, subId()}} | {result, {default, subscribed, subId()}} |
{result, {default, subscribed, subId(), send_last}} | {result, {default, subscribed, subId(), send_last}} |
{result, {default, pending, subId()}} | {result, {default, pending, subId()}} |
{error, xmlel()}. {error, xmlel()}.
-callback unsubscribe_node(NodeIdx :: nodeIdx(), -callback unsubscribe_node(NodeIdx :: nodeIdx(),
Sender :: jid(), Sender :: jid(),
Subscriber :: ljid(), Subscriber :: jid(),
SubId :: subId()) -> SubId :: subId()) ->
{result, default} | {result, default} |
{error, xmlel()}. {error, xmlel()}.
-callback publish_item(NodeId :: nodeIdx(), -callback publish_item(NodeId :: nodeIdx(),
Publisher :: jid(), Publisher :: jid(),
PublishModel :: publishModel(), PublishModel :: publishModel(),
Max_Items :: non_neg_integer(), Max_Items :: non_neg_integer(),
ItemId :: <<>> | itemId(), ItemId :: <<>> | itemId(),
Payload :: payload()) -> Payload :: payload()) ->
{result, {default, broadcast, [itemId()]}} | {result, {default, broadcast, [itemId()]}} |
{error, xmlel()}. {error, xmlel()}.
-callback delete_item(NodeIdx :: nodeIdx(), -callback delete_item(NodeIdx :: nodeIdx(),
Publisher :: jid(), Publisher :: jid(),
PublishModel :: publishModel(), PublishModel :: publishModel(),
ItemId :: <<>> | itemId()) -> ItemId :: <<>> | itemId()) ->
{result, {default, broadcast}} | {result, {default, broadcast}} |
{error, xmlel()}. {error, xmlel()}.
-callback remove_extra_items(NodeIdx :: nodeIdx(), -callback remove_extra_items(NodeIdx :: nodeIdx(),
Max_Items :: unlimited | non_neg_integer(), Max_Items :: unlimited | non_neg_integer(),
ItemIds :: [itemId()]) -> ItemIds :: [itemId()]) ->
{result, {[itemId()], [itemId()]} {result, {[itemId()], [itemId()]}
}. }.
-callback get_node_affiliations(NodeIdx :: nodeIdx()) -> -callback get_node_affiliations(NodeIdx :: nodeIdx()) ->
{result, [{ljid(), affiliation()}]}. {result, [{ljid(), affiliation()}]}.
-callback get_entity_affiliations(Host :: host(), -callback get_entity_affiliations(Host :: host(),
Owner :: jid()) -> Owner :: jid()) ->
{result, [{pubsubNode(), affiliation()}]}. {result, [{pubsubNode(), affiliation()}]}.
-callback get_affiliation(NodeIdx :: nodeIdx(), -callback get_affiliation(NodeIdx :: nodeIdx(),
Owner :: jid()) -> Owner :: jid()) ->
{result, affiliation()}. {result, affiliation()}.
-callback set_affiliation(NodeIdx :: nodeIdx(), -callback set_affiliation(NodeIdx :: nodeIdx(),
Owner :: ljid(), Owner :: jid(),
Affiliation :: affiliation()) -> Affiliation :: affiliation()) ->
ok | ok |
{error, xmlel()}. {error, xmlel()}.
-callback get_node_subscriptions(NodeIdx :: nodeIdx()) -> -callback get_node_subscriptions(NodeIdx :: nodeIdx()) ->
{result, {result,
[{ljid(), subscription(), subId()}] | [{ljid(), subscription(), subId()}] |
[{ljid(), none},...] [{ljid(), none},...]
}. }.
-callback get_entity_subscriptions(Host :: host(), -callback get_entity_subscriptions(Host :: host(),
Owner :: jid()) -> Key :: jid()) ->
{result, [{pubsubNode(), subscription(), subId(), ljid()}] {result, [{pubsubNode(), subscription(), subId(), ljid()}]
}. }.
-callback get_subscriptions(NodeIdx :: nodeIdx(), -callback get_subscriptions(NodeIdx :: nodeIdx(),
Owner :: ljid()) -> Owner :: jid()) ->
{result, [{subscription(), subId()}]}. {result, [{subscription(), subId()}]}.
-callback get_pending_nodes(Host :: host(), -callback get_pending_nodes(Host :: host(),
Owner :: jid()) -> Owner :: jid()) ->
{result, [nodeId()]}. {result, [nodeId()]}.
-callback get_states(NodeIdx::nodeIdx()) -> -callback get_states(NodeIdx::nodeIdx()) ->
{result, [pubsubState()]}. {result, [pubsubState()]}.
-callback get_state(NodeIdx :: nodeIdx(), -callback get_state(NodeIdx :: nodeIdx(),
JID :: ljid()) -> Key :: ljid()) ->
pubsubState(). pubsubState().
-callback set_state(State::pubsubState()) -> -callback set_state(State::pubsubState()) ->
@ -224,30 +179,32 @@
{error, xmlel()}. {error, xmlel()}.
-callback get_items(NodeIdx :: nodeIdx(), -callback get_items(NodeIdx :: nodeIdx(),
JID :: jid(), JID :: jid(),
AccessModel :: accessModel(), AccessModel :: accessModel(),
Presence_Subscription :: boolean(), Presence_Subscription :: boolean(),
RosterGroup :: boolean(), RosterGroup :: boolean(),
SubId :: subId()) -> SubId :: subId(),
{result, [pubsubItem()]} | RSM :: none | rsm_in()) ->
{result, {[pubsubItem()], none | rsm_out()}} |
{error, xmlel()}. {error, xmlel()}.
-callback get_items(NodeIdx :: nodeIdx(), -callback get_items(NodeIdx :: nodeIdx(),
From :: jid()) -> From :: jid(),
{result, [pubsubItem()]}. RSM :: none | rsm_in()) ->
{result, {[pubsubItem()], none | rsm_out()}}.
-callback get_item(NodeIdx :: nodeIdx(), -callback get_item(NodeIdx :: nodeIdx(),
ItemId :: itemId(), ItemId :: itemId(),
JID :: jid(), JID :: jid(),
AccessModel :: accessModel(), AccessModel :: accessModel(),
PresenceSubscription :: boolean(), PresenceSubscription :: boolean(),
RosterGroup :: boolean(), RosterGroup :: boolean(),
SubId :: subId()) -> SubId :: subId()) ->
{result, pubsubItem()} | {result, pubsubItem()} |
{error, xmlel()}. {error, xmlel()}.
-callback get_item(NodeIdx :: nodeIdx(), -callback get_item(NodeIdx :: nodeIdx(),
ItemId :: itemId()) -> ItemId :: itemId()) ->
{result, pubsubItem()} | {result, pubsubItem()} |
{error, xmlel()}. {error, xmlel()}.
@ -256,8 +213,8 @@
% | {error, _}. % | {error, _}.
-callback get_item_name(Host :: host(), -callback get_item_name(Host :: host(),
ServerHost :: binary(), ServerHost :: binary(),
Node :: nodeId()) -> Node :: nodeId()) ->
itemId(). itemId().
-callback node_to_path(Node :: nodeId()) -> -callback node_to_path(Node :: nodeId()) ->

View File

@ -32,49 +32,31 @@
-include("jlib.hrl"). -include("jlib.hrl").
-type(host() :: mod_pubsub:host() -type(host() :: mod_pubsub:host()).
| mod_pubsub_odbc:host() -type(nodeId() :: mod_pubsub:nodeId()).
). -type(nodeIdx() :: mod_pubsub:nodeIdx()).
-type(pubsubNode() :: mod_pubsub:pubsubNode()).
-type(nodeId() :: mod_pubsub:nodeId() -type(nodeOptions() :: mod_pubsub:nodeOptions()).
| mod_pubsub_odbc:nodeId()
).
-type(nodeIdx() :: mod_pubsub:nodeIdx()
| mod_pubsub_odbc:nodeIdx()
).
-type(itemId() :: mod_pubsub:itemId()
| mod_pubsub_odbc:itemId()
).
-type(pubsubNode() :: mod_pubsub:pubsubNode()
| mod_pubsub_odbc:pubsubNode()
).
-type(nodeOptions() :: mod_pubsub:nodeOptions()
| mod_pubsub_odbc:nodeOptions()
).
-callback init(Host :: host(), -callback init(Host :: host(),
ServerHost :: binary(), ServerHost :: binary(),
Opts :: [any()]) -> atom(). Opts :: [any()]) -> atom().
-callback terminate(Host :: host(), ServerHost :: binary()) -> atom(). -callback terminate(Host :: host(), ServerHost :: binary()) -> atom().
-callback options() -> nodeOptions(). -callback options() -> nodeOptions().
-callback set_node(PubsubNode :: pubsubNode()) -> -callback set_node(PubsubNode :: pubsubNode()) ->
ok | {result, NodeIdx::mod_pubsub_odbc:nodeIdx()} | {error, xmlel()}. ok | {result, NodeIdx::nodeIdx()} | {error, xmlel()}.
-callback get_node(Host :: host(), -callback get_node(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
From :: jid()) -> From :: jid()) ->
pubsubNode() | pubsubNode() |
{error, xmlel()}. {error, xmlel()}.
-callback get_node(Host :: host(), -callback get_node(Host :: host(),
NodeId :: nodeId()) -> NodeId :: nodeId()) ->
pubsubNode() | pubsubNode() |
{error, xmlel()}. {error, xmlel()}.
@ -83,42 +65,43 @@
{error, xmlel()}. {error, xmlel()}.
-callback get_nodes(Host :: host(), -callback get_nodes(Host :: host(),
From :: jid())-> From :: jid())->
[pubsubNode()]. [pubsubNode()].
-callback get_nodes(Host :: host())-> -callback get_nodes(Host :: host())->
[pubsubNode()]. [pubsubNode()].
-callback get_parentnodes(Host :: host(), -callback get_parentnodes(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
From :: jid()) -> From :: jid()) ->
[pubsubNode()] | [pubsubNode()] |
{error, xmlel()}. {error, xmlel()}.
-callback get_parentnodes_tree(Host :: host(), -callback get_parentnodes_tree(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
From :: jid()) -> From :: jid()) ->
[{0, [pubsubNode(),...]}]. [{0, [pubsubNode(),...]}].
-callback get_subnodes(Host :: host(), -callback get_subnodes(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
From :: ljid()) -> From :: jid()) ->
[pubsubNode()]. [pubsubNode()].
-callback get_subnodes_tree(Host :: host(), -callback get_subnodes_tree(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
From :: ljid()) -> From :: jid()) ->
[pubsubNode()]. [pubsubNode()].
-callback create_node(Host :: host(), -callback create_node(Host :: host(),
NodeId :: nodeId(), NodeId :: nodeId(),
Type :: binary(), Type :: binary(),
Owner :: jid(), Owner :: jid(),
Options :: nodeOptions(), Options :: nodeOptions(),
Parents :: [nodeId()]) -> Parents :: [nodeId()]) ->
{ok, NodeIdx::nodeIdx()} | {ok, NodeIdx::nodeIdx()} |
{error, xmlel()}. {error, xmlel()} |
{error, {virtual, {host(), nodeId()}}}.
-callback delete_node(Host :: host(), -callback delete_node(Host :: host(),
NodeId :: nodeId()) -> NodeId :: nodeId()) ->
[pubsubNode()]. [pubsubNode()].

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,194 +0,0 @@
%%% ====================================================================
%%% ``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-2015, ProcessOne
%%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne.
%%%
%%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time}
%%% @end
%%% ====================================================================
-module(__TO_BE_DEFINED__).
-author(__TO_BE_DEFINED__).
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% API definition
-export([init/3, terminate/2,
options/0, features/0,
create_node_permission/6,
create_node/2,
delete_node/1,
purge_node/2,
subscribe_node/8,
unsubscribe_node/4,
publish_item/6,
delete_item/4,
remove_extra_items/3,
get_entity_affiliations/2,
get_node_affiliations/1,
get_affiliation/2,
set_affiliation/3,
get_entity_subscriptions/2,
get_node_subscriptions/1,
get_subscriptions/2,
set_subscriptions/4,
get_pending_nodes/2,
get_states/1,
get_state/2,
set_state/1,
get_items/6,
get_items/2,
get_item/7,
get_item/2,
set_item/1,
get_item_name/3
]).
init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost).
options() ->
[{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() ->
["create-nodes",
"delete-nodes",
"delete-items",
"instant-nodes",
"outcast-affiliation",
"persistent-items",
"publish",
"purge-nodes",
"retract-items",
"retrieve-affiliations",
"retrieve-items",
"retrieve-subscriptions",
"subscribe",
"subscription-notifications"
].
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) ->
node_hometree:create_node(NodeId, Owner).
delete_node(Removed) ->
node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
node_hometree:unsubscribe_node(NodeId, Sender, Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(NodeId, Publisher, Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, PublishModel, ItemId).
purge_node(NodeId, Owner) ->
node_hometree:purge_node(NodeId, Owner).
get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) ->
node_hometree:get_node_affiliations(NodeId).
get_affiliation(NodeId, Owner) ->
node_hometree:get_affiliation(NodeId, Owner).
set_affiliation(NodeId, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) ->
node_hometree:get_node_subscriptions(NodeId).
get_subscriptions(NodeId, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, Subscription, SubId).
get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) ->
node_hometree:get_states(NodeId).
get_state(NodeId, JID) ->
node_hometree:get_state(NodeId, JID).
set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) ->
node_hometree:get_items(NodeId, From).
get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) ->
node_hometree:get_item(NodeId, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id).

View File

@ -4,58 +4,45 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_buddy). -module(node_buddy).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,127 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, true}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, true},
{access_model, presence}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, never}, {access_model, presence},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, false}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() -> features() ->
[<<"create-nodes">>, <<"delete-nodes">>, [<<"create-nodes">>,
<<"delete-items">>, <<"instant-nodes">>, <<"item-ids">>, <<"delete-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>, <<"delete-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"instant-nodes">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>, <<"item-ids">>,
<<"retrieve-subscriptions">>, <<"subscribe">>, <<"outcast-affiliation">>,
<<"subscription-notifications">>]. <<"persistent-items">>,
<<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeId, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeId, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeId). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeId, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -1,61 +1,48 @@
%%% ==================================================================== %% ====================================================================
%%% ``The contents of this file are subject to the Erlang Public License, %%% ``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 %%% 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 %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_club). -module(node_club).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, true}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, true},
{access_model, authorize}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, never}, {access_model, authorize},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, false}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() -> features() ->
[<<"create-nodes">>, <<"delete-nodes">>, [<<"create-nodes">>,
<<"delete-items">>, <<"instant-nodes">>, <<"delete-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>, <<"delete-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"instant-nodes">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>, <<"outcast-affiliation">>,
<<"retrieve-subscriptions">>, <<"subscribe">>, <<"persistent-items">>,
<<"subscription-notifications">>]. <<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeId, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeId, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeId). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeId, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -5,39 +5,37 @@
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%%
%%% @author Brian Cully <bjc@kublai.com> %%% @author Brian Cully <bjc@kublai.com>
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_dag). -module(node_dag).
-behaviour(gen_pubsub_node).
-author('bjc@kublai.com'). -author('bjc@kublai.com').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -51,113 +49,108 @@ options() ->
features() -> features() ->
[<<"multi-collection">> | node_hometree:features()]. [<<"multi-collection">> | node_hometree:features()].
create_node_permission(_Host, _ServerHost, _Node, create_node_permission(_Host, _ServerHost, _Node, _ParentNode, _Owner, _Access) ->
_ParentNode, _Owner, _Access) ->
{result, true}. {result, true}.
create_node(NodeID, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeID, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeID, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeID, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeID, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeID, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeID, Publisher, Model, MaxItems, ItemID, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> case nodetree_dag:get_node(Nidx) of
case nodetree_dag:get_node(NodeID) of #pubsub_node{options = Options} ->
#pubsub_node{options = Options} -> case find_opt(node_type, Options) of
case find_opt(node_type, Options) of collection ->
collection -> {error,
{error, ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)};
?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)}; _ ->
_ -> node_hometree:publish_item(Nidx, Publisher, Model,
node_hometree:publish_item(NodeID, Publisher, Model, MaxItems, ItemId, Payload)
MaxItems, ItemID, Payload) end;
end; Err -> Err
Err -> Err
end. end.
find_opt(_, []) -> false; find_opt(_, []) -> false;
find_opt(Option, [{Option, Value} | _]) -> Value; find_opt(Option, [{Option, Value} | _]) -> Value;
find_opt(Option, [_ | T]) -> find_opt(Option, T). find_opt(Option, [_ | T]) -> find_opt(Option, T).
remove_extra_items(NodeID, MaxItems, ItemIDs) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeID, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIDs).
delete_item(NodeID, Publisher, PublishModel, ItemID) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeID, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemID).
purge_node(NodeID, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeID, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeID) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeID). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeID, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeID, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeID, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeID, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeID) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeID). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeID, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeID, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeID, Owner, Subscription, SubID) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeID, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubID).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeID) -> node_hometree:get_states(NodeID). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeID, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeID, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeID, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeID, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeID, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubID) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeID, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubID).
get_item(NodeID, ItemID) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeID, ItemID). node_hometree:get_item(Nidx, ItemId).
get_item(NodeID, ItemID, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubID) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeID, ItemID, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubID).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, ID) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, ID). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_hometree:node_to_path(Node). node_to_path(Node) ->
node_hometree:node_to_path(Node).
path_to_node(Path) -> node_hometree:path_to_node(Path). path_to_node(Path) ->
node_hometree:path_to_node(Path).

View File

@ -4,56 +4,51 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_dispatch). -module(node_dispatch).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%%% @doc <p>The <strong>{@module}</strong> module is a PubSub plugin whose %%% @doc <p>The <strong>{@module}</strong> module is a PubSub plugin whose
%%% goal is to republished each published item to all its children.</p> %%% goal is to republished each published item to all its children.</p>
%%% <p>Users cannot subscribe to this node, but are supposed to subscribe to %%% <p>Users cannot subscribe to this node, but are supposed to subscribe to
%%% its children.</p> %%% its children.</p>
%%% This module can not work with virtual nodetree %%% This module can not work with virtual nodetree
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -62,115 +57,129 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, true}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, true},
{access_model, open}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, never}, {access_model, presence},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, false}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() -> features() ->
[<<"create-nodes">>, <<"delete-nodes">>, [<<"create-nodes">>,
<<"instant-nodes">>, <<"outcast-affiliation">>, <<"delete-nodes">>,
<<"persistent-items">>, <<"publish">>, <<"instant-nodes">>,
<<"retrieve-items">>]. <<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>,
<<"retrieve-items">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Nodes) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Nodes).
subscribe_node(_NodeId, _Sender, _Subscriber, subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription,
_AccessModel, _SendLast, _PresenceSubscription, _RosterGroup, _Options) ->
_RosterGroup, _Options) ->
{error, ?ERR_FORBIDDEN}. {error, ?ERR_FORBIDDEN}.
unsubscribe_node(_NodeId, _Sender, _Subscriber, unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) ->
_SubID) ->
{error, ?ERR_FORBIDDEN}. {error, ?ERR_FORBIDDEN}.
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
Payload) -> case nodetree_tree:get_node(Nidx) of
lists:foreach(fun (SubNode) -> #pubsub_node{nodeid = {Host, Node}} ->
node_hometree:publish_item(SubNode#pubsub_node.id, lists:foreach(fun (SubNode) ->
Publisher, Model, MaxItems, node_hometree:publish_item(SubNode#pubsub_node.id,
ItemId, Payload) Publisher, PublishModel, MaxItems,
end, ItemId, Payload)
nodetree_tree:get_subnodes(NodeId, Publisher, end,
Publisher)). nodetree_tree:get_subnodes(Host, Node, Publisher)),
{result, {default, broadcast, []}};
Error ->
Error
end.
remove_extra_items(_NodeId, _MaxItems, ItemIds) -> remove_extra_items(_Nidx, _MaxItems, ItemIds) ->
{result, {ItemIds, []}}. {result, {ItemIds, []}}.
delete_item(_NodeId, _Publisher, _PublishModel, delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) ->
_ItemId) ->
{error, ?ERR_ITEM_NOT_FOUND}. {error, ?ERR_ITEM_NOT_FOUND}.
purge_node(_NodeId, _Owner) -> {error, ?ERR_FORBIDDEN}. purge_node(_Nidx, _Owner) ->
{error, ?ERR_FORBIDDEN}.
get_entity_affiliations(_Host, _Owner) -> {result, []}. get_entity_affiliations(_Host, _Owner) ->
{result, []}.
get_node_affiliations(_NodeId) -> {result, []}. get_node_affiliations(_Nidx) ->
{result, []}.
get_affiliation(_NodeId, _Owner) -> {result, []}. get_affiliation(_Nidx, _Owner) ->
{result, none}.
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(_Host, _Owner) -> {result, []}. get_entity_subscriptions(_Host, _Owner) ->
{result, []}.
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(_NodeId, _Owner) -> {result, []}. get_subscriptions(_Nidx, _Owner) ->
{result, []}.
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -4,13 +4,13 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
@ -25,162 +25,156 @@
%%% ==================================================================== %%% ====================================================================
-module(node_flat). -module(node_flat).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(_Host, _ServerHost, _Opts) ->
node_hometree:init(Host, ServerHost, Opts). pubsub_subscription:init(),
mnesia:create_table(pubsub_state,
[{disc_copies, [node()]},
{type, ordered_set},
{attributes, record_info(fields, pubsub_state)}]),
mnesia:create_table(pubsub_item,
[{disc_only_copies, [node()]},
{attributes, record_info(fields, pubsub_item)}]),
ItemsFields = record_info(fields, pubsub_item),
case mnesia:table_info(pubsub_item, attributes) of
ItemsFields -> ok;
_ -> mnesia:transform_table(pubsub_item, ignore, ItemsFields)
end,
ok.
terminate(Host, ServerHost) -> terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, node_hometree:options().
{notify_delete, false}, {notify_retract, true},
{purge_offline, false}, {persist_items, true},
{max_items, ?MAXITEMS}, {subscribe, true},
{access_model, open}, {roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() -> node_hometree:features(). features() ->
node_hometree:features().
%% use same code as node_hometree, but do not limite node to %% use same code as node_hometree, but do not limite node to
%% the home/localhost/user/... hierarchy %% the home/localhost/user/... hierarchy
%% any node is allowed %% any node is allowed
create_node_permission(Host, ServerHost, _Node, create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) ->
_ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner), LOwner = jlib:jid_tolower(Owner),
Allowed = case LOwner of Allowed = case LOwner of
{<<"">>, Host, <<"">>} -> {<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed true; % pubsub service always allowed
_ -> _ ->
acl:match_rule(ServerHost, Access, LOwner) =:= allow acl:match_rule(ServerHost, Access, LOwner) =:= allow
end, end,
{result, Allowed}. {result, Allowed}.
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber,
AccessModel, SendLast, PresenceSubscription, AccessModel, SendLast, PresenceSubscription,
RosterGroup, Options). RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeId, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeId, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeId). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeId, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> [(Node)]. node_to_path(Node) ->
[(Node)].
path_to_node(Path) -> path_to_node(Path) ->
case Path of case Path of
% default slot % default slot
[Node] -> iolist_to_binary(Node); [Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format % handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) -> [Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>)); iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example) % default case (used by PEP for example)
_ -> iolist_to_binary(Path) _ -> iolist_to_binary(Path)
end. end.

View File

@ -4,13 +4,13 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
@ -25,30 +25,25 @@
%%% ==================================================================== %%% ====================================================================
-module(node_flat_odbc). -module(node_flat_odbc).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_entity_subscriptions/2, get_node_subscriptions/1,
get_entity_subscriptions_for_send_last/2, get_subscriptions/2, set_subscriptions/4,
get_node_subscriptions/1, get_subscriptions/2, get_pending_nodes/2, get_states/1, get_state/2,
set_subscriptions/4, get_pending_nodes/2, get_states/1, set_state/1, get_items/7, get_items/3, get_item/7,
get_state/2, set_state/1, get_items/7, get_items/6, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
get_items/3, get_items/2, get_item/7, get_item/2, path_to_node/1,
set_item/1, get_item_name/3, get_last_items/3, get_entity_subscriptions_for_send_last/2, get_last_items/3]).
node_to_path/1, path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree_odbc:init(Host, ServerHost, Opts). node_hometree_odbc:init(Host, ServerHost, Opts).
@ -57,157 +52,115 @@ terminate(Host, ServerHost) ->
node_hometree_odbc:terminate(Host, ServerHost). node_hometree_odbc:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, [{odbc, true}, {rsm, true} | node_flat:options()].
{notify_config, false},
{notify_delete, false},
{notify_retract, true},
{purge_offline, false},
{persist_items, true},
{max_items, ?MAXITEMS},
{subscribe, true},
{access_model, open},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, false}, {odbc, true},
{rsm, true}].
features() -> node_hometree_odbc:features(). features() ->
[<<"rsm">> | node_flat:features()].
%% use same code as node_hometree_odbc, but do not limite node to %% use same code as node_hometree_odbc, but do not limite node to
%% the home/localhost/user/... hierarchy %% the home/localhost/user/... hierarchy
%% any node is allowed %% any node is allowed
create_node_permission(Host, ServerHost, _Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
_ParentNode, Owner, Access) -> node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
LOwner = jlib:jid_tolower(Owner),
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
acl:match_rule(ServerHost, Access, LOwner) =:= allow
end,
{result, Allowed}.
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree_odbc:create_node(NodeId, Owner). node_hometree_odbc:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree_odbc:delete_node(Removed). node_hometree_odbc:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(NodeId, Sender, node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options).
PresenceSubscription, RosterGroup,
Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree_odbc:unsubscribe_node(NodeId, Sender, node_hometree_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree_odbc:publish_item(NodeId, Publisher,
Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(NodeId, MaxItems, node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(NodeId, Publisher, node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree_odbc:purge_node(NodeId, Owner). node_hometree_odbc:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree_odbc:get_entity_affiliations(Host, Owner). node_hometree_odbc:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree_odbc:get_node_affiliations(NodeId). node_hometree_odbc:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree_odbc:get_affiliation(NodeId, Owner). node_hometree_odbc:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(NodeId, Owner, node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree_odbc:get_entity_subscriptions(Host, node_hometree_odbc:get_entity_subscriptions(Host, Owner).
Owner).
get_entity_subscriptions_for_send_last(Host, Owner) -> get_entity_subscriptions_for_send_last(Host, Owner) ->
node_hometree_odbc:get_entity_subscriptions_for_send_last(Host, node_hometree_odbc:get_entity_subscriptions_for_send_last(Host, Owner).
Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree_odbc:get_node_subscriptions(NodeId). node_hometree_odbc:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree_odbc:get_subscriptions(NodeId, Owner). node_hometree_odbc:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(NodeId, Owner, node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree_odbc:get_pending_nodes(Host, Owner). node_hometree_odbc:get_pending_nodes(Host, Owner).
get_states(NodeId) -> get_states(Nidx) ->
node_hometree_odbc:get_states(NodeId). node_hometree_odbc:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree_odbc:get_state(NodeId, JID). node_hometree_odbc:get_state(Nidx, JID).
set_state(State) -> node_hometree_odbc:set_state(State). set_state(State) ->
node_hometree_odbc:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree_odbc:get_items(NodeId, From). node_hometree_odbc:get_items(Nidx, From, RSM).
get_items(NodeId, From, RSM) -> get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(NodeId, From, RSM). node_hometree_odbc:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_items(NodeId, JID, AccessModel, get_item(Nidx, ItemId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree_odbc:get_item(Nidx, ItemId).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, none).
get_items(NodeId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId, RSM) -> node_hometree_odbc:get_item(Nidx, ItemId, JID,
node_hometree_odbc:get_items(NodeId, JID, AccessModel, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId, RSM).
get_item(NodeId, ItemId) -> set_item(Item) ->
node_hometree_odbc:get_item(NodeId, ItemId). node_hometree_odbc:set_item(Item).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(NodeId, ItemId, JID,
AccessModel, PresenceSubscription, RosterGroup,
SubId).
set_item(Item) -> node_hometree_odbc:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree_odbc:get_item_name(Host, Node, Id). node_hometree_odbc:get_item_name(Host, Node, Id).
get_last_items(NodeId, From, Count) -> get_last_items(Nidx, From, Count) ->
node_hometree_odbc:get_last_items(NodeId, From, Count). node_hometree_odbc:get_last_items(Nidx, From, Count).
node_to_path(Node) -> [(Node)]. node_to_path(Node) ->
[(Node)].
path_to_node(Path) -> path_to_node(Path) ->
case Path of case Path of
% default slot % default slot
[Node] -> iolist_to_binary(Node); [Node] -> iolist_to_binary(Node);
% handle old possible entries, used when migrating database content to new format % handle old possible entries, used when migrating database content to new format
[Node | _] when is_binary(Node) -> [Node | _] when is_binary(Node) ->
iolist_to_binary(str:join([<<"">> | Path], <<"/">>)); iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
% default case (used by PEP for example) % default case (used by PEP for example)
_ -> iolist_to_binary(Path) _ -> iolist_to_binary(Path)
end. end.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,62 +4,54 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Eric Cestari <eric@ohmforce.com> %%% @author Eric Cestari <eric@ohmforce.com>
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_mb).
-behaviour(gen_pubsub_node).
-author('eric@ohmforce.com').
-include("pubsub.hrl").
-include("jlib.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep microblog PubSub plugin. %%% @doc The module <strong>{@module}</strong> is the pep microblog PubSub plugin.
%%% <p> To be used, mod_pubsub must be configured : %%% <p> To be used, mod_pubsub must be configured :
%%% {mod_pubsub, [ % requires mod_caps %%% {mod_pubsub, [ % requires mod_caps
%%% {access_createnode, pubsub_createnode}, %%% {access_createnode, pubsub_createnode},
%%% {plugins, ["default", "pep","mb"]}, %%% {plugins, ["default", "pep","mb"]},
%%% {pep_mapping, [{"urn:xmpp:microblog", "mb"}]} %%% {pep_mapping, [{"urn:xmpp:microblog", "mb"}]}
%%% ]}, %%% ]},
%%% </p> %%% </p>
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p> %%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_mb).
-author('eric@ohmforce.com').
-include("ejabberd.hrl").
-include("logger.hrl").
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_pep:init(Host, ServerHost, Opts). node_pep:init(Host, ServerHost, Opts).
@ -68,124 +60,127 @@ terminate(Host, ServerHost) ->
node_pep:terminate(Host, ServerHost), ok. node_pep:terminate(Host, ServerHost), ok.
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, false}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, false},
{access_model, presence}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, on_sub_and_presence}, {access_model, presence},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, true}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
features() -> features() ->
[<<"create-nodes">>, %* [<<"create-nodes">>,
<<"auto-create">>, %* <<"auto-create">>,
<<"auto-subscribe">>, %* <<"auto-subscribe">>,
<<"delete-nodes">>, %* <<"delete-nodes">>,
<<"delete-items">>, %* <<"delete-items">>,
<<"filtered-notifications">>, %* <<"filtered-notifications">>,
<<"modify-affiliations">>, <<"outcast-affiliation">>, <<"modify-affiliations">>,
<<"persistent-items">>, <<"outcast-affiliation">>,
<<"publish">>, %* <<"persistent-items">>,
<<"purge-nodes">>, <<"retract-items">>, <<"publish">>,
<<"retrieve-affiliations">>, <<"purge-nodes">>,
<<"retrieve-items">>, %* <<"retract-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>]. <<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_pep:create_node_permission(Host, ServerHost, Node,
ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_pep:create_node(NodeId, Owner). node_pep:create_node(Nidx, Owner).
delete_node(Removed) -> node_pep:delete_node(Removed). delete_node(Removed) ->
node_pep:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_pep:subscribe_node(NodeId, Sender, Subscriber, node_pep:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_pep:unsubscribe_node(NodeId, Sender, Subscriber, node_pep:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_pep:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_pep:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_pep:remove_extra_items(NodeId, MaxItems, ItemIds). node_pep:remove_extra_items(Nidx, MaxItems, ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_pep:delete_item(NodeId, Publisher, PublishModel, node_pep:delete_item(Nidx, Publisher, PublishModel, ItemId).
ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_pep:purge_node(NodeId, Owner). node_pep:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_pep:get_entity_affiliations(Host, Owner). node_pep:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_pep:get_node_affiliations(NodeId). node_pep:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_pep:get_affiliation(NodeId, Owner). node_pep:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_pep:set_affiliation(NodeId, Owner, Affiliation). node_pep:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_pep:get_entity_subscriptions(Host, Owner). node_pep:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_pep:get_node_subscriptions(NodeId). node_pep:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_pep:get_subscriptions(NodeId, Owner). node_pep:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_pep:set_subscriptions(NodeId, Owner, Subscription, node_pep:set_subscriptions(Nidx, Owner, Subscription, SubId).
SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_pep:get_states(NodeId). get_states(Nidx) ->
node_pep:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_pep:get_state(NodeId, JID). node_pep:get_state(Nidx, JID).
set_state(State) -> node_pep:set_state(State). set_state(State) ->
node_pep:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_pep:get_items(NodeId, From). node_pep:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_pep:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
node_pep:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_pep:get_item(NodeId, ItemId). node_pep:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_pep:get_item(Nidx, ItemId, JID, AccessModel,
node_pep:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_pep:set_item(Item). set_item(Item) ->
node_pep:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_pep:get_item_name(Host, Node, Id). node_pep:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_pep:node_to_path(Node). node_to_path(Node) ->
node_pep:node_to_path(Node).
path_to_node(Path) -> node_pep:path_to_node(Path). path_to_node(Path) ->
node_pep:path_to_node(Path).

View File

@ -4,20 +4,19 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe Romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
@ -25,35 +24,29 @@
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_pep).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-include("logger.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin. %%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p> %%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_pep).
-author('christophe.romain@process-one.net').
-include("ejabberd.hrl").
-include("logger.hrl").
-include("pubsub.hrl").
-include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts), node_hometree:init(Host, ServerHost, Opts),
@ -63,423 +56,193 @@ init(Host, ServerHost, Opts) ->
terminate(Host, ServerHost) -> terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost), ok. node_hometree:terminate(Host, ServerHost), ok.
-spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, false}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, 1}, {subscribe, true}, {notify_retract, false},
{access_model, presence}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, false},
{notification_type, headline}, {max_items, 1},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, on_sub_and_presence}, {access_model, presence},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, true}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
-spec(features/0 :: () -> Features::[binary(),...]).
features() -> features() ->
[<<"create-nodes">>, %* [<<"create-nodes">>,
<<"auto-create">>, %* <<"auto-create">>,
<<"auto-subscribe">>, %* <<"auto-subscribe">>,
<<"delete-nodes">>, %* <<"delete-nodes">>,
<<"delete-items">>, %* <<"delete-items">>,
<<"filtered-notifications">>, %* <<"filtered-notifications">>,
<<"modify-affiliations">>, <<"outcast-affiliation">>, <<"modify-affiliations">>,
<<"persistent-items">>, <<"outcast-affiliation">>,
<<"publish">>, %* <<"persistent-items">>,
<<"purge-nodes">>, <<"retract-items">>, <<"publish">>,
<<"retrieve-affiliations">>, <<"purge-nodes">>,
<<"retrieve-items">>, %* <<"retract-items">>,
<<"retrieve-subscriptions">>, <<"subscribe">>]. <<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
-spec(create_node_permission/6 :: <<"subscribe">>].
(
Host :: mod_pubsub:hostPEP(),
ServerHost :: binary(),
NodeId :: mod_pubsub:nodeId(),
_ParentNodeId :: mod_pubsub:nodeId(),
Owner :: jid(),
Access :: atom())
-> {result, boolean()}
).
create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) -> create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) ->
LOwner = jlib:jid_tolower(Owner), LOwner = jlib:jid_tolower(Owner),
{User, Server, _Resource} = LOwner, {User, Server, _Resource} = LOwner,
Allowed = case LOwner of Allowed = case LOwner of
{<<"">>, Host, <<"">>} -> {<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed true; % pubsub service always allowed
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
case Host of
{User, Server, _} -> true;
_ -> false
end;
_ -> _ ->
case acl:match_rule(ServerHost, Access, LOwner) of false
allow -> end
case Host of end,
{User, Server, _} -> true;
_ -> false
end;
E -> ?DEBUG("Create not allowed : ~p~n", [E]), false
end
end,
{result, Allowed}. {result, Allowed}.
-spec(create_node/2 :: create_node(Nidx, Owner) ->
( node_hometree:create_node(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
).
create_node(NodeIdx, Owner) ->
node_hometree:create_node(NodeIdx, Owner).
-spec(delete_node/1 :: delete_node(Nodes) ->
( {result, {_, _, Result}} = node_hometree:delete_node(Nodes),
Nodes :: [mod_pubsub:pubsubNode(),...]) {result, {[], Result}}.
-> {result,
{[],
[{mod_pubsub:pubsubNode(),
[{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]},...]},...]
}
}
).
delete_node(Removed) -> subscribe_node(Nidx, Sender, Subscriber, AccessModel,
case node_hometree:delete_node(Removed) of SendLast, PresenceSubscription, RosterGroup, Options) ->
{result, {_, _, Result}} -> {result, {[], Result}}; node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
Error -> Error PresenceSubscription, RosterGroup, Options).
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
case node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end. end.
-spec(subscribe_node/8 :: publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
( node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
AccessModel :: mod_pubsub:accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: mod_pubsub:subOptions())
-> {result, {default, subscribed, mod_pubsub:subId()}}
| {result, {default, subscribed, mod_pubsub:subId(), send_last}}
| {result, {default, pending, mod_pubsub:subId()}}
%%%
| {error, xmlel()}
).
subscribe_node(NodeIdx, Sender, Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeIdx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options).
-spec(unsubscribe_node/4 :: remove_extra_items(Nidx, MaxItems, ItemIds) ->
( node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
SubId :: subId())
-> {result, default}
%
| {error, xmlel()}
).
unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) ->
case node_hometree:unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
-spec(publish_item/6 :: delete_item(Nidx, Publisher, PublishModel, ItemId) ->
( node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload())
-> {result, {default, broadcast, [mod_pubsub:itemId()]}}
%%%
| {error, xmlel()}
).
publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree:publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload).
-spec(remove_extra_items/3 :: purge_node(Nidx, Owner) ->
( node_hometree:purge_node(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Max_Items :: unlimited | non_neg_integer(),
ItemIds :: [mod_pubsub:itemId()])
-> {result,
{NewItems::[mod_pubsub:itemId()],
OldItems::[mod_pubsub:itemId()]}
}
).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, ItemIds).
get_entity_affiliations(Host, Owner) ->
-spec(delete_item/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
ItemId :: <<>> | mod_pubsub:itemId())
-> {result, {default, broadcast}}
%%%
| {error, xmlel()}
).
delete_item(NodeIdx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeIdx, Publisher, PublishModel, ItemId).
-spec(purge_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
| {error, xmlel()}
).
purge_node(NodeIdx, Owner) ->
node_hometree:purge_node(NodeIdx, Owner).
-spec(get_entity_affiliations/2 ::
(
Host :: mod_pubsub:hostPEP(),
Owner :: jid())
-> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
).
get_entity_affiliations(_Host, Owner) ->
{_, D, _} = SubKey = jlib:jid_tolower(Owner), {_, D, _} = SubKey = jlib:jid_tolower(Owner),
SubKey = jlib:jid_tolower(Owner), SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey), GenKey = jlib:jid_remove_resource(SubKey),
States = mnesia:match_object(#pubsub_state{stateid = States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
{GenKey, '_'}, NodeTree = mod_pubsub:tree(Host),
_ = '_'}), Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
NodeTree = case catch case NodeTree:get_node(N) of
ets:lookup(gen_mod:get_module_proc(D, config), nodetree) #pubsub_node{nodeid = {{_, D, _}, _}} = Node -> [{Node, A} | Acc];
of _ -> Acc
[{nodetree, N}] -> N; end
_ -> nodetree_tree end,
end, [], States),
Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N},
affiliation = A},
Acc) ->
case NodeTree:get_node(N) of
#pubsub_node{nodeid = {{_, D, _}, _}} =
Node ->
[{Node, A} | Acc];
_ -> Acc
end
end,
[], States),
{result, Reply}. {result, Reply}.
-spec(get_node_affiliations/1 :: get_node_affiliations(Nidx) ->
( node_hometree:get_node_affiliations(Nidx).
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [{ljid(), mod_pubsub:affiliation()}]}
).
get_node_affiliations(NodeIdx) ->
node_hometree:get_node_affiliations(NodeIdx).
-spec(get_affiliation/2 :: get_affiliation(Nidx, Owner) ->
( node_hometree:get_affiliation(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, mod_pubsub:affiliation()}
).
get_affiliation(NodeIdx, Owner) ->
node_hometree:get_affiliation(NodeIdx, Owner).
-spec(set_affiliation/3 :: set_affiliation(Nidx, Owner, Affiliation) ->
( node_hometree:set_affiliation(Nidx, Owner, Affiliation).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid(),
Affiliation :: mod_pubsub:affiliation())
-> ok
).
set_affiliation(NodeIdx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeIdx, Owner, Affiliation).
-spec(get_entity_subscriptions/2 :: get_entity_subscriptions(Host, Owner) ->
(
Host :: mod_pubsub:hostPEP(),
Owner :: jid())
-> {result,
[{mod_pubsub:pubsubNode(),
mod_pubsub:subscription(),
mod_pubsub:subId(),
ljid()}]
}
).
get_entity_subscriptions(_Host, Owner) ->
{U, D, _} = SubKey = jlib:jid_tolower(Owner), {U, D, _} = SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey), GenKey = jlib:jid_remove_resource(SubKey),
States = case SubKey of States = case SubKey of
GenKey -> GenKey ->
mnesia:match_object(#pubsub_state{stateid = mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'});
{{U, D, '_'}, '_'}, _ ->
_ = '_'}); mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'})
_ -> ++
mnesia:match_object(#pubsub_state{stateid = mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'})
{GenKey, '_'}, end,
_ = '_'}) NodeTree = mod_pubsub:tree(Host),
++ Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
mnesia:match_object(#pubsub_state{stateid = case NodeTree:get_node(N) of
{SubKey, '_'}, #pubsub_node{nodeid = {{_, D, _}, _}} = Node ->
_ = '_'}) lists:foldl(fun
end, ({subscribed, SubId}, Acc2) ->
NodeTree = case catch [{Node, subscribed, SubId, J} | Acc2];
ets:lookup(gen_mod:get_module_proc(D, config), nodetree) ({pending, _SubId}, Acc2) ->
of [{Node, pending, J} | Acc2];
[{nodetree, N}] -> N; (S, Acc2) ->
_ -> nodetree_tree [{Node, S, J} | Acc2]
end, end,
Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, Acc, Ss);
subscriptions = Ss}, _ ->
Acc) -> Acc
case NodeTree:get_node(N) of end
#pubsub_node{nodeid = {{_, D, _}, _}} = Node -> end,
lists:foldl(fun [], States),
({subscribed, SubID}, Acc2) ->
[{Node, subscribed, SubID, J} | Acc2];
({pending, _SubID}, Acc2) ->
[{Node, pending, J} | Acc2];
(S, Acc2) ->
[{Node, S, J} | Acc2]
end, Acc, Ss);
_ -> Acc
end
end,
[], States),
{result, Reply}. {result, Reply}.
-spec(get_node_subscriptions/1 :: get_node_subscriptions(Nidx) ->
( node_hometree:get_node_subscriptions(Nidx).
NodeIdx::mod_pubsub:nodeIdx())
-> {result,
[{ljid(), mod_pubsub:subscription(), mod_pubsub:subId()}] |
[{ljid(), none},...]
}
).
get_node_subscriptions(NodeIdx) ->
node_hometree:get_node_subscriptions(NodeIdx).
-spec(get_subscriptions/2 :: get_subscriptions(Nidx, Owner) ->
( node_hometree:get_subscriptions(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid())
-> {result, [{mod_pubsub:subscription(), mod_pubsub:subId()}]}
).
get_subscriptions(NodeIdx, Owner) ->
node_hometree:get_subscriptions(NodeIdx, Owner).
-spec(set_subscriptions/4 :: set_subscriptions(Nidx, Owner, Subscription, SubId) ->
( node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid(),
Subscription :: mod_pubsub:subscription(),
SubId :: mod_pubsub:subId())
-> ok
%%%
| {error, xmlel()}
).
set_subscriptions(NodeIdx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeIdx, Owner, Subscription, SubId).
-spec(get_pending_nodes/2 ::
(
Host :: mod_pubsub:hostPubsub(),
Owner :: jid())
-> {result, [mod_pubsub:nodeId()]}
).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
-spec(get_states/1 :: get_states(Nidx) ->
( node_hometree:get_states(Nidx).
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [mod_pubsub:pubsubState()]}
).
get_states(NodeIdx) -> node_hometree:get_states(NodeIdx).
-spec(get_state/2 :: get_state(Nidx, JID) ->
( node_hometree:get_state(Nidx, JID).
NodeIdx :: mod_pubsub:nodeIdx(),
JID :: ljid())
-> mod_pubsub:pubsubState()
).
get_state(NodeIdx, JID) ->
node_hometree:get_state(NodeIdx, JID).
-spec(set_state/1 :: set_state(State) ->
( node_hometree:set_state(State).
State::mod_pubsub:pubsubState())
-> ok
).
set_state(State) -> node_hometree:set_state(State).
-spec(get_items/2 :: get_items(Nidx, From, RSM) ->
( node_hometree:get_items(Nidx, From, RSM).
NodeIdx :: mod_pubsub:nodeIdx(),
_From :: jid())
-> {result, [mod_pubsub:pubsubItem()]}
).
get_items(NodeIdx, From) ->
node_hometree:get_items(NodeIdx, From).
-spec(get_items/6 :: get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
( node_hometree:get_items(Nidx, JID, AccessModel,
NodeIdx :: mod_pubsub:nodeIdx(), PresenceSubscription, RosterGroup, SubId, RSM).
JID :: jid(),
AccessModel :: mod_pubsub:accessModel(),
Presence_Subscription :: boolean(),
RosterGroup :: boolean(),
_SubId :: mod_pubsub:subId())
-> {result, [mod_pubsub:pubsubItem()]}
%%%
| {error, xmlel()}
).
get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree:get_items(NodeIdx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
-spec(get_item/2 :: get_item(Nidx, ItemId) ->
( node_hometree:get_item(Nidx, ItemId).
NodeIdx :: mod_pubsub:nodeIdx(),
ItemId :: mod_pubsub:itemId())
-> {result, mod_pubsub:pubsubItem()}
| {error, xmlel()}
).
get_item(NodeIdx, ItemId) ->
node_hometree:get_item(NodeIdx, ItemId).
-spec(get_item/7 :: get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
( node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
NodeIdx :: mod_pubsub:nodeIdx(), PresenceSubscription, RosterGroup, SubId).
ItemId :: mod_pubsub:itemId(),
JID :: jid(),
AccessModel :: mod_pubsub:accessModel(),
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
SubId :: mod_pubsub:subId())
-> {result, mod_pubsub:pubsubItem()}
| {error, xmlel()}
).
get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup,
SubId) ->
node_hometree:get_item(NodeIdx, ItemId, JID, AccessModel, PresenceSubscription,
RosterGroup, SubId).
-spec(set_item/1 :: set_item(Item) ->
( node_hometree:set_item(Item).
Item::mod_pubsub:pubsubItem())
-> ok
).
set_item(Item) -> node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).
%%% %%%
%%% Internal %%% Internal
@ -491,10 +254,10 @@ path_to_node(Path) -> node_flat:path_to_node(Path).
%% If not, show a warning message in the ejabberd log file. %% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) -> complain_if_modcaps_disabled(ServerHost) ->
case gen_mod:is_loaded(ServerHost, mod_caps) of case gen_mod:is_loaded(ServerHost, mod_caps) of
false -> false ->
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub " ?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps " "of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.", "to be enabled, but it isn't.",
[ServerHost]); [ServerHost]);
true -> ok true -> ok
end. end.

View File

@ -4,20 +4,19 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe Romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
@ -25,39 +24,30 @@
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_pep_odbc).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
-include("pubsub.hrl").
-include("jlib.hrl").
-include("logger.hrl").
%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin. %%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p> %%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-module(node_pep_odbc).
-author('christophe.romain@process-one.net').
-include("ejabberd.hrl").
-include("logger.hrl").
-include("pubsub.hrl").
-include("jlib.hrl").
-define(PUBSUB, mod_pubsub_odbc).
-behaviour(gen_pubsub_node).
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_entity_subscriptions/2, get_node_subscriptions/1,
get_entity_subscriptions_for_send_last/2, get_subscriptions/2, set_subscriptions/4,
get_node_subscriptions/1, get_subscriptions/2, get_pending_nodes/2, get_states/1, get_state/2,
set_subscriptions/4, get_pending_nodes/2, get_states/1, set_state/1, get_items/7, get_items/3, get_item/7,
get_state/2, set_state/1, get_items/7, get_items/6, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
get_items/3, get_items/2, get_item/7, get_item/2, path_to_node/1,
set_item/1, get_item_name/3, get_last_items/3, get_entity_subscriptions_for_send_last/2, get_last_items/3]).
node_to_path/1, path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree_odbc:init(Host, ServerHost, Opts), node_hometree_odbc:init(Host, ServerHost, Opts),
@ -67,362 +57,183 @@ init(Host, ServerHost, Opts) ->
terminate(Host, ServerHost) -> terminate(Host, ServerHost) ->
node_hometree_odbc:terminate(Host, ServerHost), ok. node_hometree_odbc:terminate(Host, ServerHost), ok.
-spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
options() -> options() ->
[{odbc, true}, [{odbc, true}, {rsm, true} | node_pep:options()].
{deliver_payloads, true},
{notify_config, false},
{notify_delete, false},
{notify_retract, false},
{purge_offline, false},
{persist_items, true},
{max_items, 1},
{subscribe, true},
{access_model, presence},
{roster_groups_allowed, []},
{publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, on_sub_and_presence},
{deliver_notifications, true},
{presence_based_delivery, true}].
-spec(features/0 :: () -> Features::[binary(),...]).
features() -> features() ->
[<<"create-nodes">>, %* [<<"rsm">> | node_pep:features()].
<<"auto-create">>, %*
<<"auto-subscribe">>, %*
<<"delete-nodes">>, %*
<<"delete-items">>, %*
<<"filtered-notifications">>, %*
<<"modify-affiliations">>, <<"outcast-affiliation">>,
<<"persistent-items">>,
<<"publish">>, %*
<<"purge-nodes">>, <<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>, %*
<<"retrieve-subscriptions">>, <<"subscribe">>].
-spec(create_node_permission/6 :: create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
( node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
Host :: mod_pubsub:hostPEP(),
ServerHost :: binary(),
NodeId :: mod_pubsub:nodeId(),
_ParentNodeId :: mod_pubsub:nodeId(),
Owner :: jid(),
Access :: atom())
-> {result, boolean()}
).
create_node_permission(Host, ServerHost, _NodeId, _ParentNode, Owner, Access) -> create_node(Nidx, Owner) ->
LOwner = jlib:jid_tolower(Owner), node_hometree_odbc:create_node(Nidx, Owner),
{User, Server, _Resource} = LOwner, {result, {default, broadcast}}.
Allowed = case LOwner of
{<<"">>, Host, <<"">>} ->
true; % pubsub service always allowed
_ ->
case acl:match_rule(ServerHost, Access, LOwner) of
allow ->
case Host of
{User, Server, _} -> true;
_ -> false
end;
E -> ?DEBUG("Create not allowed : ~p~n", [E]), false
end
end,
{result, Allowed}.
-spec(create_node/2 :: delete_node(Nodes) ->
( {result, {_, _, Result}} = node_hometree_odbc:delete_node(Nodes),
NodeIdx :: mod_pubsub:nodeIdx(), {result, {[], Result}}.
Owner :: jid())
-> {result, []} subscribe_node(Nidx, Sender, Subscriber, AccessModel,
). SendLast, PresenceSubscription, RosterGroup, Options) ->
create_node(NodeIdx, Owner) -> node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
case node_hometree_odbc:create_node(NodeIdx, Owner) of PresenceSubscription, RosterGroup, Options).
{result, _} -> {result, []};
Error -> Error
unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
case node_hometree_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end. end.
-spec(delete_node/1 :: publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
( node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
Removed :: [mod_pubsub:pubsubNode(),...])
-> {result,
{[],
[{mod_pubsub:pubsubNode(),
[{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]}]}]
}
}
).
delete_node(Removed) ->
case node_hometree_odbc:delete_node(Removed) of
{result, {_, _, Result}} -> {result, {[], Result}};
Error -> Error
end.
-spec(subscribe_node/8 :: remove_extra_items(Nidx, MaxItems, ItemIds) ->
( node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: ljid(),
AccessModel :: mod_pubsub:accessModel(),
SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
PresenceSubscription :: boolean(),
RosterGroup :: boolean(),
Options :: mod_pubsub:subOptions())
-> {result, {default, subscribed, mod_pubsub:subId()}}
| {result, {default, subscribed, mod_pubsub:subId(), send_last}}
| {result, {default, pending, mod_pubsub:subId()}}
%%%
| {error, _}
| {error, _, binary()}
).
subscribe_node(NodeId, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree_odbc:subscribe_node(NodeId, Sender,
Subscriber, AccessModel, SendLast,
PresenceSubscription, RosterGroup,
Options).
delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
-spec(unsubscribe_node/4 :: purge_node(Nidx, Owner) ->
( node_hometree_odbc:purge_node(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Sender :: jid(),
Subscriber :: jid(),
SubId :: subId())
-> {result, []}
%
| {error, _}
| {error, _, binary()}
).
unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) ->
case node_hometree_odbc:unsubscribe_node(NodeIdx, Sender, Subscriber, SubID) of
{error, Error} -> {error, Error};
{result, _} -> {result, []}
end.
-spec(publish_item/6 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
Max_Items :: non_neg_integer(),
ItemId :: <<>> | mod_pubsub:itemId(),
Payload :: mod_pubsub:payload())
-> {result, {default, broadcast, [mod_pubsub:itemId()]}}
%%%
| {error, _}
).
publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload) ->
node_hometree_odbc:publish_item(NodeIdx, Publisher,
Model, MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) ->
node_hometree_odbc:remove_extra_items(NodeId, MaxItems,
ItemIds).
-spec(delete_item/4 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Publisher :: jid(),
PublishModel :: mod_pubsub:publishModel(),
ItemId :: <<>> | mod_pubsub:itemId())
-> {result, {default, broadcast}}
%%%
| {error, _}
).
delete_item(NodeId, Publisher, PublishModel, ItemId) ->
node_hometree_odbc:delete_item(NodeId, Publisher,
PublishModel, ItemId).
-spec(purge_node/2 ::
(
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: jid())
-> {result, {default, broadcast}}
| {error, _}
).
purge_node(NodeIdx, Owner) ->
node_hometree_odbc:purge_node(NodeIdx, Owner).
-spec(get_entity_affiliations/2 ::
(
Host :: mod_pubsub:hostPubsub(),
Owner :: jid())
-> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
).
get_entity_affiliations(_Host, Owner) -> get_entity_affiliations(_Host, Owner) ->
OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
node_hometree_odbc:get_entity_affiliations(OwnerKey, Owner). node_hometree_odbc:get_entity_affiliations(OwnerKey, Owner).
-spec(get_node_affiliations/1 :: get_node_affiliations(Nidx) ->
( node_hometree_odbc:get_node_affiliations(Nidx).
NodeIdx::mod_pubsub:nodeIdx())
-> {result, [{ljid(), mod_pubsub:affiliation()}]}
).
get_node_affiliations(NodeIdx) ->
node_hometree_odbc:get_node_affiliations(NodeIdx).
-spec(get_affiliation/2 :: get_affiliation(Nidx, Owner) ->
( node_hometree_odbc:get_affiliation(Nidx, Owner).
NodeIdx :: mod_pubsub:nodeIdx(),
Owner :: ljid())
-> {result, mod_pubsub:affiliation()}
).
get_affiliation(NodeIdx, Owner) ->
node_hometree_odbc:get_affiliation(NodeIdx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree_odbc:set_affiliation(NodeId, Owner, Affiliation). node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
get_entity_subscriptions(_Host, Owner) -> get_entity_subscriptions(_Host, Owner) ->
SubKey = jlib:jid_tolower(Owner), SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey), GenKey = jlib:jid_remove_resource(SubKey),
Host = (?PUBSUB):escape(element(2, SubKey)), Host = node_hometree_odbc:encode_host(element(2, SubKey)),
SJ = node_hometree_odbc:encode_jid(SubKey), SJ = node_hometree_odbc:encode_jid(SubKey),
GJ = node_hometree_odbc:encode_jid(GenKey), GJ = node_hometree_odbc:encode_jid(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select host, node, type, i.nodeid, jid, " [<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod" "subscriptions from pubsub_state i, pubsub_node n "
"e n where i.nodeid = n.nodeid and jid " "where i.nodeid = n.nodeid and jid "
"like '">>, "like '">>, GJ, <<"%' and host like '%@">>, Host, <<"';">>];
GJ, <<"%' and host like '%@">>, Host, <<"';">>]; _ ->
_ -> [<<"select host, node, type, i.nodeid, jid, "
[<<"select host, node, type, i.nodeid, jid, " "subscriptions from pubsub_state i, pubsub_node n "
"subscriptions from pubsub_state i, pubsub_nod" "where i.nodeid = n.nodeid and jid "
"e n where i.nodeid = n.nodeid and jid " "in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
"in ('">>, end,
SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
<<"';">>]
end,
Reply = case catch ejabberd_odbc:sql_query_t(Query) of Reply = case catch ejabberd_odbc:sql_query_t(Query) of
{selected, {selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
<<"jid">>, <<"subscriptions">>], RItems} ->
RItems} -> lists:map(fun ([H, N, T, I, J, S]) ->
lists:map(fun ([H, N, T, I, J, S]) -> O = node_hometree_odbc:decode_jid(H),
O = node_hometree_odbc:decode_jid(H), Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
Node = nodetree_tree_odbc:raw_to_node(O, {Node,
[N, node_hometree_odbc:decode_subscriptions(S),
<<"">>, node_hometree_odbc:decode_jid(J)}
T, end,
I]), RItems);
{Node, _ ->
node_hometree_odbc:decode_subscriptions(S), []
node_hometree_odbc:decode_jid(J)} end,
end,
RItems);
_ -> []
end,
{result, Reply}. {result, Reply}.
get_entity_subscriptions_for_send_last(_Host, Owner) -> get_entity_subscriptions_for_send_last(_Host, Owner) ->
SubKey = jlib:jid_tolower(Owner), SubKey = jlib:jid_tolower(Owner),
GenKey = jlib:jid_remove_resource(SubKey), GenKey = jlib:jid_remove_resource(SubKey),
Host = (?PUBSUB):escape(element(2, SubKey)), Host = node_hometree_odbc:encode_host(element(2, SubKey)),
SJ = node_hometree_odbc:encode_jid(SubKey), SJ = node_hometree_odbc:encode_jid(SubKey),
GJ = node_hometree_odbc:encode_jid(GenKey), GJ = node_hometree_odbc:encode_jid(GenKey),
Query = case SubKey of Query = case SubKey of
GenKey -> GenKey ->
[<<"select host, node, type, i.nodeid, jid, " [<<"select host, node, type, i.nodeid, jid, "
"subscriptions from pubsub_state i, pubsub_nod" "subscriptions from pubsub_state i, pubsub_node n, "
"e n, pubsub_node_option o where i.nodeid " "pubsub_node_option o where i.nodeid = n.nodeid "
"= n.nodeid and n.nodeid = o.nodeid and " "and n.nodeid = o.nodeid and name='send_last_published_item' and "
"name='send_last_published_item' and " "val='on_sub_and_presence' and jid like '">>,
"val='on_sub_and_presence' and jid like " GJ, <<"%' and host like '%@">>, Host, <<"';">>];
"'">>, _ ->
GJ, <<"%' and host like '%@">>, Host, <<"';">>]; [<<"select host, node, type, i.nodeid, jid, "
_ -> "subscriptions from pubsub_state i, pubsub_node n, "
[<<"select host, node, type, i.nodeid, jid, " "pubsub_node_option o where i.nodeid = n.nodeid "
"subscriptions from pubsub_state i, pubsub_nod" "and n.nodeid = o.nodeid and name='send_last_published_item' and "
"e n, pubsub_node_option o where i.nodeid " "val='on_sub_and_presence' and jid in ",
"= n.nodeid and n.nodeid = o.nodeid and " "('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
"name='send_last_published_item' and " end,
"val='on_sub_and_presence' and jid in "
"('">>,
SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
<<"';">>]
end,
Reply = case catch ejabberd_odbc:sql_query_t(Query) of Reply = case catch ejabberd_odbc:sql_query_t(Query) of
{selected, {selected,
[<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
<<"jid">>, <<"subscriptions">>], RItems} ->
RItems} -> lists:map(fun ([H, N, T, I, J, S]) ->
lists:map(fun ([H, N, T, I, J, S]) -> O = node_hometree_odbc:decode_jid(H),
O = node_hometree_odbc:decode_jid(H), Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
Node = nodetree_tree_odbc:raw_to_node(O, {Node,
[N, node_hometree_odbc:decode_subscriptions(S),
<<"">>, node_hometree_odbc:decode_jid(J)}
T, end,
I]), RItems);
{Node, _ ->
node_hometree_odbc:decode_subscriptions(S), []
node_hometree_odbc:decode_jid(J)} end,
end,
RItems);
_ -> []
end,
{result, Reply}. {result, Reply}.
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree_odbc:get_node_subscriptions(NodeId). node_hometree_odbc:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree_odbc:get_subscriptions(NodeId, Owner). node_hometree_odbc:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree_odbc:set_subscriptions(NodeId, Owner, node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree_odbc:get_pending_nodes(Host, Owner). node_hometree_odbc:get_pending_nodes(Host, Owner).
get_states(NodeId) -> get_states(Nidx) ->
node_hometree_odbc:get_states(NodeId). node_hometree_odbc:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree_odbc:get_state(NodeId, JID). node_hometree_odbc:get_state(Nidx, JID).
set_state(State) -> node_hometree_odbc:set_state(State). set_state(State) ->
node_hometree_odbc:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree_odbc:get_items(NodeId, From). node_hometree_odbc:get_items(Nidx, From, RSM).
get_items(NodeId, From, RSM) -> get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
node_hometree_odbc:get_items(NodeId, From, RSM). node_hometree_odbc:get_items(Nidx, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_items(NodeId, JID, AccessModel, get_last_items(Nidx, JID, Count) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree_odbc:get_last_items(Nidx, JID, Count).
get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, none).
get_items(NodeId, JID, AccessModel, get_item(Nidx, ItemId) ->
PresenceSubscription, RosterGroup, SubId, RSM) -> node_hometree_odbc:get_item(Nidx, ItemId).
node_hometree_odbc:get_items(NodeId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId, RSM).
get_last_items(NodeId, JID, Count) -> get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_last_items(NodeId, JID, Count). node_hometree_odbc:get_item(Nidx, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> set_item(Item) ->
node_hometree_odbc:get_item(NodeId, ItemId). node_hometree_odbc:set_item(Item).
get_item(NodeId, ItemId, JID, AccessModel,
PresenceSubscription, RosterGroup, SubId) ->
node_hometree_odbc:get_item(NodeId, ItemId, JID,
AccessModel, PresenceSubscription, RosterGroup,
SubId).
set_item(Item) -> node_hometree_odbc:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree_odbc:get_item_name(Host, Node, Id). node_hometree_odbc:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat_odbc:node_to_path(Node). node_to_path(Node) ->
node_flat_odbc:node_to_path(Node).
path_to_node(Path) -> node_flat_odbc:path_to_node(Path). path_to_node(Path) ->
node_flat_odbc:path_to_node(Path).
%%% %%%
%%% Internal %%% Internal
@ -433,16 +244,11 @@ path_to_node(Path) -> node_flat_odbc:path_to_node(Path).
%% Check that the mod_caps module is enabled in that Jabber Host %% Check that the mod_caps module is enabled in that Jabber Host
%% If not, show a warning message in the ejabberd log file. %% If not, show a warning message in the ejabberd log file.
complain_if_modcaps_disabled(ServerHost) -> complain_if_modcaps_disabled(ServerHost) ->
Modules = ejabberd_config:get_option({modules, case gen_mod:is_loaded(ServerHost, mod_caps) of
ServerHost}, false ->
fun(Ms) when is_list(Ms) -> Ms end), ?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
ModCaps = [mod_caps_enabled "of host ~p. This plugin requires mod_caps "
|| {mod_caps, _Opts} <- Modules], "to be enabled, but it isn't.",
case ModCaps of [ServerHost]);
[] -> true -> ok
?WARNING_MSG("The PEP plugin is enabled in mod_pubsub "
"of host ~p. This plugin requires mod_caps "
"to be enabled, but it isn't.",
[ServerHost]);
_ -> ok
end. end.

View File

@ -4,58 +4,45 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_private). -module(node_private).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -64,121 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, true}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, true},
{access_model, whitelist}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, never}, {access_model, whitelist},
{deliver_notifications, false}, {roster_groups_allowed, []},
{presence_based_delivery, false}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, false},
{presence_based_delivery, false}].
features() -> features() ->
[<<"create-nodes">>, <<"delete-nodes">>, [<<"create-nodes">>,
<<"delete-items">>, <<"instant-nodes">>, <<"delete-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>, <<"delete-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"instant-nodes">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>, <<"outcast-affiliation">>,
<<"retrieve-subscriptions">>, <<"subscribe">>, <<"persistent-items">>,
<<"subscription-notifications">>]. <<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeId, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeId, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeId). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeId, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -4,58 +4,45 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%% %%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%% %%%
%%% %%%
%%% The Initial Developer of the Original Code is ProcessOne. %%% The Initial Developer of the Original Code is ProcessOne.
%%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne %%% Portions created by ProcessOne are Copyright 2006-2015, ProcessOne
%%% All Rights Reserved.'' %%% All Rights Reserved.''
%%% This software is copyright 2006-2015, ProcessOne. %%% This software is copyright 2006-2015, ProcessOne.
%%% %%%
%%%
%%% @copyright 2006-2015 ProcessOne %%% @copyright 2006-2015 ProcessOne
%%% @author Christophe romain <christophe.romain@process-one.net> %%% @author Christophe Romain <christophe.romain@process-one.net>
%%% [http://www.process-one.net/] %%% [http://www.process-one.net/]
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
%%% @end %%% @end
%%% ==================================================================== %%% ====================================================================
-module(node_public). -module(node_public).
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_node).
%% Note on function definition
%% included is all defined plugin function
%% it's possible not to define some function at all
%% in that case, warning will be generated at compilation
%% and function call will fail,
%% then mod_pubsub will call function from node_hometree
%% (this makes code cleaner, but execution a little bit longer)
%% API definition
-export([init/3, terminate/2, options/0, features/0, -export([init/3, terminate/2, options/0, features/0,
create_node_permission/6, create_node/2, delete_node/1, create_node_permission/6, create_node/2, delete_node/1,
purge_node/2, subscribe_node/8, unsubscribe_node/4, purge_node/2, subscribe_node/8, unsubscribe_node/4,
publish_item/6, delete_item/4, remove_extra_items/3, publish_item/6, delete_item/4, remove_extra_items/3,
get_entity_affiliations/2, get_node_affiliations/1, get_entity_affiliations/2, get_node_affiliations/1,
get_affiliation/2, set_affiliation/3, get_affiliation/2, set_affiliation/3,
get_entity_subscriptions/2, get_node_subscriptions/1, get_entity_subscriptions/2, get_node_subscriptions/1,
get_subscriptions/2, set_subscriptions/4, get_subscriptions/2, set_subscriptions/4,
get_pending_nodes/2, get_states/1, get_state/2, get_pending_nodes/2, get_states/1, get_state/2,
set_state/1, get_items/6, get_items/2, get_item/7, set_state/1, get_items/7, get_items/3, get_item/7,
get_item/2, set_item/1, get_item_name/3, node_to_path/1, get_item/2, set_item/1, get_item_name/3, node_to_path/1,
path_to_node/1]). path_to_node/1]).
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
node_hometree:init(Host, ServerHost, Opts). node_hometree:init(Host, ServerHost, Opts).
@ -64,123 +51,126 @@ terminate(Host, ServerHost) ->
node_hometree:terminate(Host, ServerHost). node_hometree:terminate(Host, ServerHost).
options() -> options() ->
[{deliver_payloads, true}, {notify_config, false}, [{deliver_payloads, true},
{notify_delete, false}, {notify_retract, true}, {notify_config, false},
{purge_offline, false}, {persist_items, true}, {notify_delete, false},
{max_items, ?MAXITEMS}, {subscribe, true}, {notify_retract, true},
{access_model, open}, {roster_groups_allowed, []}, {purge_offline, false},
{publish_model, publishers}, {persist_items, true},
{notification_type, headline}, {max_items, ?MAXITEMS},
{max_payload_size, ?MAX_PAYLOAD_SIZE}, {subscribe, true},
{send_last_published_item, never}, {access_model, open},
{deliver_notifications, true}, {roster_groups_allowed, []},
{presence_based_delivery, false}]. {publish_model, publishers},
{notification_type, headline},
{max_payload_size, ?MAX_PAYLOAD_SIZE},
{send_last_published_item, never},
{deliver_notifications, true},
{presence_based_delivery, false}].
features() -> features() ->
[<<"create-nodes">>, <<"delete-nodes">>, [<<"create-nodes">>,
<<"delete-items">>, <<"instant-nodes">>, <<"delete-nodes">>,
<<"outcast-affiliation">>, <<"persistent-items">>, <<"delete-items">>,
<<"publish">>, <<"purge-nodes">>, <<"retract-items">>, <<"instant-nodes">>,
<<"retrieve-affiliations">>, <<"retrieve-items">>, <<"outcast-affiliation">>,
<<"retrieve-subscriptions">>, <<"subscribe">>, <<"persistent-items">>,
<<"subscription-notifications">>]. <<"publish">>,
<<"purge-nodes">>,
<<"retract-items">>,
<<"retrieve-affiliations">>,
<<"retrieve-items">>,
<<"retrieve-subscriptions">>,
<<"subscribe">>,
<<"subscription-notifications">>].
create_node_permission(Host, ServerHost, Node, create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
ParentNode, Owner, Access) -> node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
node_hometree:create_node_permission(Host, ServerHost,
Node, ParentNode, Owner, Access).
create_node(NodeId, Owner) -> create_node(Nidx, Owner) ->
node_hometree:create_node(NodeId, Owner). node_hometree:create_node(Nidx, Owner).
delete_node(Removed) -> delete_node(Removed) ->
node_hometree:delete_node(Removed). node_hometree:delete_node(Removed).
subscribe_node(NodeId, Sender, Subscriber, AccessModel, subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) -> SendLast, PresenceSubscription, RosterGroup, Options) ->
node_hometree:subscribe_node(NodeId, Sender, Subscriber, node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
AccessModel, SendLast, PresenceSubscription, PresenceSubscription, RosterGroup, Options).
RosterGroup, Options).
unsubscribe_node(NodeId, Sender, Subscriber, SubID) -> unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
node_hometree:unsubscribe_node(NodeId, Sender, node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
Subscriber, SubID).
publish_item(NodeId, Publisher, Model, MaxItems, ItemId, publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
Payload) -> node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
node_hometree:publish_item(NodeId, Publisher, Model,
MaxItems, ItemId, Payload).
remove_extra_items(NodeId, MaxItems, ItemIds) -> remove_extra_items(Nidx, MaxItems, ItemIds) ->
node_hometree:remove_extra_items(NodeId, MaxItems, node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
ItemIds).
delete_item(NodeId, Publisher, PublishModel, ItemId) -> delete_item(Nidx, Publisher, PublishModel, ItemId) ->
node_hometree:delete_item(NodeId, Publisher, node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
PublishModel, ItemId).
purge_node(NodeId, Owner) -> purge_node(Nidx, Owner) ->
node_hometree:purge_node(NodeId, Owner). node_hometree:purge_node(Nidx, Owner).
get_entity_affiliations(Host, Owner) -> get_entity_affiliations(Host, Owner) ->
node_hometree:get_entity_affiliations(Host, Owner). node_hometree:get_entity_affiliations(Host, Owner).
get_node_affiliations(NodeId) -> get_node_affiliations(Nidx) ->
node_hometree:get_node_affiliations(NodeId). node_hometree:get_node_affiliations(Nidx).
get_affiliation(NodeId, Owner) -> get_affiliation(Nidx, Owner) ->
node_hometree:get_affiliation(NodeId, Owner). node_hometree:get_affiliation(Nidx, Owner).
set_affiliation(NodeId, Owner, Affiliation) -> set_affiliation(Nidx, Owner, Affiliation) ->
node_hometree:set_affiliation(NodeId, Owner, node_hometree:set_affiliation(Nidx, Owner, Affiliation).
Affiliation).
get_entity_subscriptions(Host, Owner) -> get_entity_subscriptions(Host, Owner) ->
node_hometree:get_entity_subscriptions(Host, Owner). node_hometree:get_entity_subscriptions(Host, Owner).
get_node_subscriptions(NodeId) -> get_node_subscriptions(Nidx) ->
node_hometree:get_node_subscriptions(NodeId). node_hometree:get_node_subscriptions(Nidx).
get_subscriptions(NodeId, Owner) -> get_subscriptions(Nidx, Owner) ->
node_hometree:get_subscriptions(NodeId, Owner). node_hometree:get_subscriptions(Nidx, Owner).
set_subscriptions(NodeId, Owner, Subscription, SubId) -> set_subscriptions(Nidx, Owner, Subscription, SubId) ->
node_hometree:set_subscriptions(NodeId, Owner, node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
Subscription, SubId).
get_pending_nodes(Host, Owner) -> get_pending_nodes(Host, Owner) ->
node_hometree:get_pending_nodes(Host, Owner). node_hometree:get_pending_nodes(Host, Owner).
get_states(NodeId) -> node_hometree:get_states(NodeId). get_states(Nidx) ->
node_hometree:get_states(Nidx).
get_state(NodeId, JID) -> get_state(Nidx, JID) ->
node_hometree:get_state(NodeId, JID). node_hometree:get_state(Nidx, JID).
set_state(State) -> node_hometree:set_state(State). set_state(State) ->
node_hometree:set_state(State).
get_items(NodeId, From) -> get_items(Nidx, From, RSM) ->
node_hometree:get_items(NodeId, From). node_hometree:get_items(Nidx, From, RSM).
get_items(NodeId, JID, AccessModel, get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_items(Nidx, JID, AccessModel,
node_hometree:get_items(NodeId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
PresenceSubscription, RosterGroup, SubId).
get_item(NodeId, ItemId) -> get_item(Nidx, ItemId) ->
node_hometree:get_item(NodeId, ItemId). node_hometree:get_item(Nidx, ItemId).
get_item(NodeId, ItemId, JID, AccessModel, get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
PresenceSubscription, RosterGroup, SubId) -> node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
node_hometree:get_item(NodeId, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId).
PresenceSubscription, RosterGroup, SubId).
set_item(Item) -> node_hometree:set_item(Item). set_item(Item) ->
node_hometree:set_item(Item).
%% @doc <p>Return the name of the node if known: Default is to return
%% node id.</p>
get_item_name(Host, Node, Id) -> get_item_name(Host, Node, Id) ->
node_hometree:get_item_name(Host, Node, Id). node_hometree:get_item_name(Host, Node, Id).
node_to_path(Node) -> node_flat:node_to_path(Node). node_to_path(Node) ->
node_flat:node_to_path(Node).
path_to_node(Path) -> node_flat:path_to_node(Path). path_to_node(Path) ->
node_flat:path_to_node(Path).

View File

@ -4,11 +4,13 @@
%%% compliance with the License. You should have received a copy of the %%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be %%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/. %%% retrieved via the world wide web at http://www.erlang.org/.
%%%
%%% %%%
%%% Software distributed under the License is distributed on an "AS IS" %%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations %%% the License for the specific language governing rights and limitations
%%% under the License. %%% under the License.
%%%
%%% %%%
%%% @author Brian Cully <bjc@kublai.com> %%% @author Brian Cully <bjc@kublai.com>
%%% @version {@vsn}, {@date} {@time} %%% @version {@vsn}, {@date} {@time}
@ -16,219 +18,141 @@
%%% ==================================================================== %%% ====================================================================
-module(nodetree_dag). -module(nodetree_dag).
-behaviour(gen_pubsub_nodetree).
-author('bjc@kublai.com'). -author('bjc@kublai.com').
%% API
-export([init/3, terminate/2, options/0, set_node/1,
get_node/3, get_node/2, get_node/1, get_nodes/2,
get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
get_subnodes/3, get_subnodes_tree/3, create_node/6,
delete_node/2]).
-include_lib("stdlib/include/qlc.hrl"). -include_lib("stdlib/include/qlc.hrl").
-include("ejabberd.hrl").
-include("logger.hrl").
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_nodetree). -export([init/3, terminate/2, options/0, set_node/1,
get_node/3, get_node/2, get_node/1, get_nodes/2,
get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
get_subnodes/3, get_subnodes_tree/3, create_node/6,
delete_node/2]).
-define(DEFAULT_NODETYPE, leaf). -define(DEFAULT_NODETYPE, leaf).
-define(DEFAULT_PARENTS, []). -define(DEFAULT_PARENTS, []).
-define(DEFAULT_CHILDREN, []). -define(DEFAULT_CHILDREN, []).
-compile(export_all).
%%====================================================================
%% API
%%====================================================================
init(Host, ServerHost, Opts) -> init(Host, ServerHost, Opts) ->
nodetree_tree:init(Host, ServerHost, Opts). nodetree_tree:init(Host, ServerHost, Opts).
terminate(Host, ServerHost) -> terminate(Host, ServerHost) ->
nodetree_tree:terminate(Host, ServerHost). nodetree_tree:terminate(Host, ServerHost).
-spec(create_node/6 :: set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} = Node) ->
(
Key :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId(),
Type :: binary(),
Owner :: jid(),
Options :: mod_pubsub:nodeOptions(),
Parents :: [mod_pubsub:nodeId()])
-> {ok, NodeIdx::mod_pubsub:nodeIdx()}
| {error, xmlel()}
).
create_node(Key, NodeID, Type, Owner, Options, Parents) ->
OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case find_node(Key, NodeID) of
false ->
NodeIdx = pubsub_index:new(node),
N = #pubsub_node{nodeid = oid(Key, NodeID), id = NodeIdx,
type = Type, parents = Parents, owners = [OwnerJID],
options = Options},
case set_node(N) of
ok -> {ok, NodeIdx};
Other -> Other
end;
_ -> {error, ?ERR_CONFLICT}
end.
-spec(set_node/1 ::
(
PubsubNode::mod_pubsub:pubsubNode())
-> ok
%%%
| {error, xmlel()}
).
set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} =
Node) ->
Parents = find_opt(collection, ?DEFAULT_PARENTS, Options), Parents = find_opt(collection, ?DEFAULT_PARENTS, Options),
case validate_parentage(Key, Owners, Parents) of case validate_parentage(Key, Owners, Parents) of
true -> true -> mnesia:write(Node#pubsub_node{parents = Parents});
mnesia:write(Node#pubsub_node{parents = Parents}); Other -> Other
Other -> Other
end. end.
-spec(delete_node/2 :: create_node(Key, Node, Type, Owner, Options, Parents) ->
( OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
Key :: mod_pubsub:hostPubsub(), case find_node(Key, Node) of
NodeID :: mod_pubsub:nodeId()) false ->
-> [mod_pubsub:pubsubNode(),...] Nidx = pubsub_index:new(node),
%%% N = #pubsub_node{nodeid = oid(Key, Node), id = Nidx,
| {error, xmlel()} type = Type, parents = Parents, owners = [OwnerJID],
). options = Options},
delete_node(Key, NodeID) -> case set_node(N) of
case find_node(Key, NodeID) of ok -> {ok, Nidx};
false -> {error, ?ERR_ITEM_NOT_FOUND}; Other -> Other
Node -> end;
lists:foreach(fun (#pubsub_node{options = Opts} = _ ->
Child) -> {error, ?ERR_CONFLICT}
NewOpts = remove_config_parent(NodeID, Opts),
Parents = find_opt(collection, ?DEFAULT_PARENTS,
NewOpts),
ok = mnesia:write(pubsub_node,
Child#pubsub_node{parents =
Parents,
options =
NewOpts},
write)
end,
get_subnodes(Key, NodeID)),
pubsub_index:free(node, Node#pubsub_node.id),
mnesia:delete_object(pubsub_node, Node, write),
[Node]
end. end.
options() -> nodetree_tree:options(). delete_node(Key, Node) ->
case find_node(Key, Node) of
get_node(Host, NodeID, _From) -> get_node(Host, NodeID). false ->
{error, ?ERR_ITEM_NOT_FOUND};
-spec(get_node/2 :: Record ->
( lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
Host :: mod_pubsub:hostPubsub(), NewOpts = remove_config_parent(Node, Opts),
NodeID :: mod_pubsub:nodeId()) Parents = find_opt(collection, ?DEFAULT_PARENTS, NewOpts),
-> mod_pubsub:pubsubNode() ok = mnesia:write(pubsub_node,
%%% Child#pubsub_node{parents = Parents,
| {error, xmlel} options = NewOpts},
). write)
get_node(Host, NodeID) -> end,
case find_node(Host, NodeID) of get_subnodes(Key, Node)),
false -> {error, ?ERR_ITEM_NOT_FOUND}; pubsub_index:free(node, Record#pubsub_node.id),
Node -> Node mnesia:delete_object(pubsub_node, Record, write),
[Record]
end. end.
-spec(get_node/1 :: options() ->
( nodetree_tree:options().
NodeIdx::mod_pubsub:nodeIdx())
-> mod_pubsub:pubsubNode() get_node(Host, Node, _From) ->
| {error, xmlel()} get_node(Host, Node).
).
get_node(NodeId) -> nodetree_tree:get_node(NodeId). get_node(Host, Node) ->
case find_node(Host, Node) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
Record -> Record
end.
get_node(Node) ->
nodetree_tree:get_node(Node).
get_nodes(Key, From) -> get_nodes(Key, From) ->
nodetree_tree:get_nodes(Key, From). nodetree_tree:get_nodes(Key, From).
-spec(get_nodes/1 :: get_nodes(Key) ->
( nodetree_tree:get_nodes(Key).
Host::mod_pubsub:host())
-> [mod_pubsub:pubsubNode()]
).
get_nodes(Key) -> nodetree_tree:get_nodes(Key).
-spec(get_parentnodes/3 :: get_parentnodes(Host, Node, _From) ->
( case find_node(Host, Node) of
Host :: mod_pubsub:hostPubsub(), false ->
NodeID :: mod_pubsub:nodeId(), {error, ?ERR_ITEM_NOT_FOUND};
_From :: _) #pubsub_node{parents = Parents} ->
-> [mod_pubsub:pubsubNode()] Q = qlc:q([N
%%% || #pubsub_node{nodeid = {NHost, NNode}} = N
| {error, xmlel()}
).
get_parentnodes(Host, NodeID, _From) ->
case find_node(Host, NodeID) of
false -> {error, ?ERR_ITEM_NOT_FOUND};
#pubsub_node{parents = Parents} ->
Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, NNode}} = N
<- mnesia:table(pubsub_node), <- mnesia:table(pubsub_node),
Parent <- Parents, Host == NHost, Parent == NNode]), Parent <- Parents, Host == NHost, Parent == NNode]),
qlc:e(Q) qlc:e(Q)
end. end.
get_parentnodes_tree(Host, NodeID, _From) -> get_parentnodes_tree(Host, Node, _From) ->
Pred = fun (NID, #pubsub_node{nodeid = {_, NNodeID}}) -> Pred = fun (NID, #pubsub_node{nodeid = {_, NNode}}) ->
NID == NNodeID NID == NNode
end, end,
Tr = fun (#pubsub_node{parents = Parents}) -> Parents Tr = fun (#pubsub_node{parents = Parents}) -> Parents
end, end,
traversal_helper(Pred, Tr, Host, [NodeID]). traversal_helper(Pred, Tr, Host, [Node]).
get_subnodes(Host, NodeID, _From) -> get_subnodes(Host, Node, _From) ->
get_subnodes(Host, NodeID). get_subnodes(Host, Node).
-spec(get_subnodes/2 ::
(
Host :: mod_pubsub:hostPubsub(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes(Host, <<>>) -> get_subnodes(Host, <<>>) ->
get_subnodes_helper(Host, <<>>); get_subnodes_helper(Host, <<>>);
get_subnodes(Host, NodeID) -> get_subnodes(Host, Node) ->
case find_node(Host, NodeID) of case find_node(Host, Node) of
false -> {error, ?ERR_ITEM_NOT_FOUND}; false -> {error, ?ERR_ITEM_NOT_FOUND};
_ -> get_subnodes_helper(Host, NodeID) _ -> get_subnodes_helper(Host, Node)
end. end.
-spec(get_subnodes_helper/2 :: get_subnodes_helper(Host, Node) ->
( Q = qlc:q([N
Host :: mod_pubsub:hostPubsub(), || #pubsub_node{nodeid = {NHost, _},
NodeID :: mod_pubsub:nodeId()) parents = Parents} =
-> [mod_pubsub:pubsubNode()] N
). <- mnesia:table(pubsub_node),
get_subnodes_helper(Host, NodeID) -> Host == NHost, lists:member(Node, Parents)]),
Q = qlc:q([Node
|| #pubsub_node{nodeid = {NHost, _},
parents = Parents} =
Node
<- mnesia:table(pubsub_node),
Host == NHost, lists:member(NodeID, Parents)]),
qlc:e(Q). qlc:e(Q).
get_subnodes_tree(Host, NodeID, From) -> get_subnodes_tree(Host, Node, From) ->
Pred = fun (NID, #pubsub_node{parents = Parents}) -> Pred = fun (NID, #pubsub_node{parents = Parents}) ->
lists:member(NID, Parents) lists:member(NID, Parents)
end, end,
Tr = fun (#pubsub_node{nodeid = {_, N}}) -> [N] end, Tr = fun (#pubsub_node{nodeid = {_, N}}) -> [N] end,
traversal_helper(Pred, Tr, 1, Host, [NodeID], traversal_helper(Pred, Tr, 1, Host, [Node],
[{0, [get_node(Host, NodeID, From)]}]). [{0, [get_node(Host, Node, From)]}]).
%%==================================================================== %%====================================================================
%% Internal functions %% Internal functions
@ -236,17 +160,17 @@ get_subnodes_tree(Host, NodeID, From) ->
oid(Key, Name) -> {Key, Name}. oid(Key, Name) -> {Key, Name}.
%% Key = jlib:jid() | host() %% Key = jlib:jid() | host()
%% NodeID = string() %% Node = string()
-spec(find_node/2 :: -spec(find_node/2 ::
( (
Key :: mod_pubsub:hostPubsub(), Key :: mod_pubsub:hostPubsub(),
NodeID :: mod_pubsub:nodeId()) Node :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode() | false -> mod_pubsub:pubsubNode() | false
). ).
find_node(Key, NodeID) -> find_node(Key, Node) ->
case mnesia:read(pubsub_node, oid(Key, NodeID), read) of case mnesia:read(pubsub_node, oid(Key, Node), read) of
[] -> false; [] -> false;
[Node] -> Node [Node] -> Node
end. end.
%% Key = jlib:jid() | host() %% Key = jlib:jid() | host()
@ -254,77 +178,67 @@ find_node(Key, NodeID) ->
%% Options = [{Key = atom(), Value = term()}] %% Options = [{Key = atom(), Value = term()}]
find_opt(Key, Default, Options) -> find_opt(Key, Default, Options) ->
case lists:keysearch(Key, 1, Options) of case lists:keysearch(Key, 1, Options) of
{value, {Key, Val}} -> Val; {value, {Key, Val}} -> Val;
_ -> Default _ -> Default
end. end.
-spec(traversal_helper/4 :: -spec(traversal_helper/4 ::
( (
Pred :: fun(), Pred :: fun(),
Tr :: fun(), Tr :: fun(),
Host :: mod_pubsub:hostPubsub(), Host :: mod_pubsub:hostPubsub(),
NodeId :: [mod_pubsub:pubsubNode(),...]) Nodes :: [mod_pubsub:nodeId(),...])
-> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}] -> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
). ).
traversal_helper(Pred, Tr, Host, Nodes) ->
traversal_helper(Pred, Tr, Host, NodeIDs) -> traversal_helper(Pred, Tr, 0, Host, Nodes, []).
traversal_helper(Pred, Tr, 0, Host, NodeIDs, []).
traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) -> traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) ->
Acc; Acc;
traversal_helper(Pred, Tr, Depth, Host, NodeIDs, Acc) -> traversal_helper(Pred, Tr, Depth, Host, Nodes, Acc) ->
Q = qlc:q([Node Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, _}} = Node || #pubsub_node{nodeid = {NHost, _}} = N
<- mnesia:table(pubsub_node), <- mnesia:table(pubsub_node),
NodeID <- NodeIDs, Host == NHost, Pred(NodeID, Node)]), Node <- Nodes, Host == NHost, Pred(Node, N)]),
Nodes = qlc:e(Q), Nodes = qlc:e(Q),
IDs = lists:flatmap(Tr, Nodes), IDs = lists:flatmap(Tr, Nodes),
traversal_helper(Pred, Tr, Depth + 1, Host, IDs, traversal_helper(Pred, Tr, Depth + 1, Host, IDs, [{Depth, Nodes} | Acc]).
[{Depth, Nodes} | Acc]).
remove_config_parent(NodeID, Options) -> remove_config_parent(Node, Options) ->
remove_config_parent(NodeID, Options, []). remove_config_parent(Node, Options, []).
remove_config_parent(_NodeID, [], Acc) -> remove_config_parent(_Node, [], Acc) ->
lists:reverse(Acc); lists:reverse(Acc);
remove_config_parent(NodeID, [{collection, Parents} | T], Acc) -> remove_config_parent(Node, [{collection, Parents} | T], Acc) ->
remove_config_parent(NodeID, T, remove_config_parent(Node, T, [{collection, lists:delete(Node, Parents)} | Acc]);
[{collection, lists:delete(NodeID, Parents)} | Acc]); remove_config_parent(Node, [H | T], Acc) ->
remove_config_parent(NodeID, [H | T], Acc) -> remove_config_parent(Node, T, [H | Acc]).
remove_config_parent(NodeID, T, [H | Acc]).
-spec(validate_parentage/3 :: -spec(validate_parentage/3 ::
( (
Key :: mod_pubsub:hostPubsub(), Key :: mod_pubsub:hostPubsub(),
Owners :: [ljid(),...], Owners :: [ljid(),...],
Parent_NodeIds :: [mod_pubsub:nodeId()]) Parent_Nodes :: [mod_pubsub:nodeId()])
-> true -> true
%%% %%%
| {error, xmlel()} | {error, xmlel()}
). ).
validate_parentage(_Key, _Owners, []) -> true; validate_parentage(_Key, _Owners, []) ->
true;
validate_parentage(Key, Owners, [[] | T]) -> validate_parentage(Key, Owners, [[] | T]) ->
validate_parentage(Key, Owners, T); validate_parentage(Key, Owners, T);
validate_parentage(Key, Owners, [<<>> | T]) -> validate_parentage(Key, Owners, [<<>> | T]) ->
validate_parentage(Key, Owners, T); validate_parentage(Key, Owners, T);
validate_parentage(Key, Owners, [ParentID | T]) -> validate_parentage(Key, Owners, [ParentID | T]) ->
case find_node(Key, ParentID) of case find_node(Key, ParentID) of
false -> {error, ?ERR_ITEM_NOT_FOUND}; false ->
#pubsub_node{owners = POwners, options = POptions} -> {error, ?ERR_ITEM_NOT_FOUND};
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions), #pubsub_node{owners = POwners, options = POptions} ->
MutualOwners = [O || O <- Owners, PO <- POwners, O == PO], NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
case {MutualOwners, NodeType} of MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
{[], _} -> {error, ?ERR_FORBIDDEN}; case {MutualOwners, NodeType} of
{_, collection} -> validate_parentage(Key, Owners, T); {[], _} -> {error, ?ERR_FORBIDDEN};
{_, _} -> {error, ?ERR_NOT_ALLOWED} {_, collection} -> validate_parentage(Key, Owners, T);
end {_, _} -> {error, ?ERR_NOT_ALLOWED}
end. end
%% @spec (Host) -> jid()
%% Host = host()
%% @doc <p>Generate pubsub service JID.</p>
service_jid(Host) ->
case Host of
{U, S, _} -> jlib:make_jid(U, S, <<>>);
_ -> jlib:make_jid(<<>>, Host, <<>>)
end. end.

View File

@ -36,287 +36,163 @@
%%% improvements.</p> %%% improvements.</p>
-module(nodetree_tree). -module(nodetree_tree).
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include_lib("stdlib/include/qlc.hrl"). -include_lib("stdlib/include/qlc.hrl").
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_nodetree).
-export([init/3, terminate/2, options/0, set_node/1, -export([init/3, terminate/2, options/0, set_node/1,
get_node/3, get_node/2, get_node/1, get_nodes/2, get_node/3, get_node/2, get_node/1, get_nodes/2,
get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
get_subnodes/3, get_subnodes_tree/3, create_node/6, get_subnodes/3, get_subnodes_tree/3, create_node/6,
delete_node/2]). delete_node/2]).
%% ================
%% API definition
%% ================
%% @spec (Host, ServerHost, Options) -> ok
%% Host = string()
%% ServerHost = string()
%% Options = [{atom(), term()}]
%% @doc <p>Called during pubsub modules initialisation. Any pubsub plugin must
%% implement this function. It can return anything.</p>
%% <p>This function is mainly used to trigger the setup task necessary for the
%% plugin. It can be used for example by the developer to create the specific
%% module database schema if it does not exists yet.</p>
init(_Host, _ServerHost, _Options) -> init(_Host, _ServerHost, _Options) ->
mnesia:create_table(pubsub_node, mnesia:create_table(pubsub_node,
[{disc_copies, [node()]}, [{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_node)}]), {attributes, record_info(fields, pubsub_node)}]),
mnesia:add_table_index(pubsub_node, id), mnesia:add_table_index(pubsub_node, id),
NodesFields = record_info(fields, pubsub_node), NodesFields = record_info(fields, pubsub_node),
case mnesia:table_info(pubsub_node, attributes) of case mnesia:table_info(pubsub_node, attributes) of
NodesFields -> ok; NodesFields -> ok;
_ -> ok _ -> ok
end, end,
%% mnesia:transform_table(pubsub_state, ignore, StatesFields) %% mnesia:transform_table(pubsub_state, ignore, StatesFields)
ok. ok.
%% @spec (Host, ServerHost) -> ok
%% Host = string()
%% ServerHost = string()
%% @spec () -> Options terminate(_Host, _ServerHost) ->
%% Options = [mod_pubsub:nodeOption()] ok.
%% @doc Returns the default pubsub node tree options.
terminate(_Host, _ServerHost) -> ok.
options() -> [{virtual_tree, false}]. options() ->
[{virtual_tree, false}].
%% @spec (Node) -> ok | {error, Reason}
%% Node = mod_pubsub:pubsubNode()
%% Reason = mod_pubsub:stanzaError()
-spec(set_node/1 ::
(
Node::mod_pubsub:pubsubNode())
-> ok
).
set_node(Node) when is_record(Node, pubsub_node) -> set_node(Node) when is_record(Node, pubsub_node) ->
mnesia:write(Node). mnesia:write(Node).
%set_node(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}.
get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node, _From) ->
get_node(Host, Node).
%% @spec (Host, NodeId) -> Node | {error, Reason} get_node(Host, Node) ->
%% Host = mod_pubsub:host() case catch mnesia:read({pubsub_node, {Host, Node}}) of
%% NodeId = mod_pubsub:nodeId() [Record] when is_record(Record, pubsub_node) -> Record;
%% Node = mod_pubsub:pubsubNode() _ -> {error, ?ERR_ITEM_NOT_FOUND}
%% Reason = mod_pubsub:stanzaError()
-spec(get_node/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode()
| {error, xmlel()}
).
get_node(Host, NodeId) ->
case catch mnesia:read({pubsub_node, {Host, NodeId}}) of
[Record] when is_record(Record, pubsub_node) -> Record;
[] -> {error, ?ERR_ITEM_NOT_FOUND}
% Error -> Error
end. end.
-spec(get_node/1 :: get_node(Nidx) ->
( case catch mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
NodeIdx::mod_pubsub:nodeIdx()) [Record] when is_record(Record, pubsub_node) -> Record;
-> mod_pubsub:pubsubNode() _ -> {error, ?ERR_ITEM_NOT_FOUND}
| {error, xmlel()}
).
get_node(NodeIdx) ->
case catch mnesia:index_read(pubsub_node, NodeIdx, #pubsub_node.id) of
[Record] when is_record(Record, pubsub_node) -> Record;
[] -> {error, ?ERR_ITEM_NOT_FOUND}
% Error -> Error
end. end.
get_nodes(Host, _From) -> get_nodes(Host). get_nodes(Host, _From) ->
get_nodes(Host).
%% @spec (Host) -> Nodes | {error, Reason}
%% Host = mod_pubsub:host()
%% Nodes = [mod_pubsub:pubsubNode()]
%% Reason = {aborted, atom()}
-spec(get_nodes/1 ::
(
Host::mod_pubsub:host())
-> [mod_pubsub:pubsubNode()]
).
get_nodes(Host) -> get_nodes(Host) ->
mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}). mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}).
%% @spec (Host, Node, From) -> [] get_parentnodes(_Host, _Node, _From) ->
%% Host = mod_pubsub:host() [].
%% NodeId = mod_pubsub:nodeId()
%% From = mod_pubsub:jid()
%% @doc <p>Default node tree does not handle parents, return empty list.</p>
get_parentnodes(_Host, _NodeId, _From) -> [].
%% @spec (Host, NodeId, From) -> [{Depth, Node}] | []
%% Host = mod_pubsub:host()
%% NodeId = mod_pubsub:nodeId()
%% From = mod_pubsub:jid()
%% Depth = integer()
%% Node = mod_pubsub:pubsubNode()
%% @doc <p>Default node tree does not handle parents, return a list %% @doc <p>Default node tree does not handle parents, return a list
%% containing just this node.</p> %% containing just this node.</p>
-spec(get_parentnodes_tree/3 :: get_parentnodes_tree(Host, Node, _From) ->
( case catch mnesia:read({pubsub_node, {Host, Node}}) of
Host :: mod_pubsub:host(), [Record] when is_record(Record, pubsub_node) -> [{0, [Record]}];
NodeId :: mod_pubsub:nodeId(), _ -> []
From :: jid())
-> [{0, [mod_pubsub:pubsubNode(),...]}]
).
get_parentnodes_tree(Host, NodeId, From) ->
case get_node(Host, NodeId, From) of
Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
_Error -> []
end. end.
%% @spec (Host, NodeId, From) -> Nodes get_subnodes(Host, Node, _From) ->
%% Host = mod_pubsub:host() get_subnodes(Host, Node).
%% NodeId = mod_pubsub:nodeId()
%% From = mod_pubsub:jid()
%% Nodes = [mod_pubsub:pubsubNode()]
get_subnodes(Host, NodeId, _From) ->
get_subnodes(Host, NodeId).
-spec(get_subnodes/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes(Host, <<>>) -> get_subnodes(Host, <<>>) ->
Q = qlc:q([N Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, _}, || #pubsub_node{nodeid = {NHost, _},
parents = Parents} = parents = Parents} =
N N
<- mnesia:table(pubsub_node), <- mnesia:table(pubsub_node),
Host == NHost, Parents == []]), Host == NHost, Parents == []]),
qlc:e(Q); qlc:e(Q);
get_subnodes(Host, Node) -> get_subnodes(Host, Node) ->
Q = qlc:q([N Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, _}, || #pubsub_node{nodeid = {NHost, _},
parents = Parents} = parents = Parents} =
N N
<- mnesia:table(pubsub_node), <- mnesia:table(pubsub_node),
Host == NHost, lists:member(Node, Parents)]), Host == NHost, lists:member(Node, Parents)]),
qlc:e(Q). qlc:e(Q).
get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node).
%% @spec (Host, NodeId) -> Nodes get_subnodes_tree(Host, Node) ->
%% Host = mod_pubsub:host() case get_node(Host, Node) of
%% NodeId = mod_pubsub:nodeId() {error, _} ->
%% Nodes = [] | [mod_pubsub:pubsubNode()] [];
-spec(get_subnodes_tree/2 :: Rec ->
( BasePlugin = jlib:binary_to_atom(<<"node_",
Host :: mod_pubsub:host(), (Rec#pubsub_node.type)/binary>>),
NodeId :: mod_pubsub:nodeId()) BasePath = BasePlugin:node_to_path(Node),
-> [mod_pubsub:pubsubNode()] mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R, Acc) ->
). Plugin = jlib:binary_to_atom(<<"node_",
get_subnodes_tree(Host, NodeId) -> (R#pubsub_node.type)/binary>>),
case get_node(Host, NodeId) of Path = Plugin:node_to_path(N),
{error, _} -> []; case lists:prefix(BasePath, Path) and (H == Host) of
Rec -> true -> [R | Acc];
BasePlugin = jlib:binary_to_atom(<<"node_", false -> Acc
(Rec#pubsub_node.type)/binary>>), end
BasePath = BasePlugin:node_to_path(NodeId), end,
mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R, [], pubsub_node)
Acc) ->
Plugin = jlib:binary_to_atom(<<"node_",
(R#pubsub_node.type)/binary>>),
Path = Plugin:node_to_path(N),
case lists:prefix(BasePath, Path) and (H == Host) of
true -> [R | Acc];
false -> Acc
end
end,
[], pubsub_node)
end. end.
%% @spec (Host, NodeId, Type, Owner, Options, Parents) -> create_node(Host, Node, Type, Owner, Options, Parents) ->
%% {ok, NodeIdx} | {error, Reason}
%% Host = mod_pubsub:host()
%% NodeId = mod_pubsub:nodeId()
%% Type = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = [mod_pubsub:nodeOption()]
%% Parents = [] | [mod_pubsub:nodeId()]
%% NodeIdx = mod_pubsub:nodeIdx()
%% Reason = mod_pubsub:stanzaError()
-spec(create_node/6 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId(),
Type :: binary(),
Owner :: jid(),
Options :: mod_pubsub:nodeOptions(),
Parents :: [mod_pubsub:nodeId()])
-> {ok, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, xmlel()}
).
create_node(Host, NodeId, Type, Owner, Options, Parents) ->
BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case catch mnesia:read({pubsub_node, {Host, NodeId}}) of case catch mnesia:read({pubsub_node, {Host, Node}}) of
[] -> [] ->
ParentExists = case Host of ParentExists = case Host of
{_U, _S, _R} -> {_U, _S, _R} ->
%% This is special case for PEP handling %% This is special case for PEP handling
%% PEP does not uses hierarchy %% PEP does not uses hierarchy
true; true;
_ -> _ ->
case Parents of case Parents of
[] -> true; [] ->
[Parent | _] -> true;
case catch mnesia:read({pubsub_node, [Parent | _] ->
{Host, Parent}}) case catch mnesia:read({pubsub_node, {Host, Parent}}) of
of [#pubsub_node{owners = [{[], Host, []}]}] ->
[#pubsub_node{owners = true;
[{[], Host, []}]}] -> [#pubsub_node{owners = Owners}] ->
true; lists:member(BJID, Owners);
[#pubsub_node{owners = Owners}] -> _ ->
lists:member(BJID, Owners); false
_ -> false end;
end; _ ->
_ -> false false
end end
end, end,
case ParentExists of case ParentExists of
true -> true ->
NodeIdx = pubsub_index:new(node), Nidx = pubsub_index:new(node),
mnesia:write(#pubsub_node{nodeid = {Host, NodeId}, mnesia:write(#pubsub_node{nodeid = {Host, Node},
id = NodeIdx, parents = Parents, id = Nidx, parents = Parents,
type = Type, owners = [BJID], type = Type, owners = [BJID],
options = Options}), options = Options}),
{ok, NodeIdx}; {ok, Nidx};
false -> {error, ?ERR_FORBIDDEN} false ->
end; {error, ?ERR_FORBIDDEN}
_ -> {error, ?ERR_CONFLICT} end;
_ ->
{error, ?ERR_CONFLICT}
end. end.
%% @spec (Host, NodeId) -> Removed delete_node(Host, Node) ->
%% Host = mod_pubsub:host() Removed = get_subnodes_tree(Host, Node),
%% NodeId = mod_pubsub:nodeId() lists:foreach(fun (#pubsub_node{nodeid = {_, SubNode}, id = SubNidx}) ->
%% Removed = [mod_pubsub:pubsubNode()] pubsub_index:free(node, SubNidx),
-spec(delete_node/2 :: mnesia:delete({pubsub_node, {Host, SubNode}})
( end,
Host :: mod_pubsub:host(), Removed),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode(),...]
).
delete_node(Host, NodeId) ->
Removed = get_subnodes_tree(Host, NodeId),
lists:foreach(fun (#pubsub_node{nodeid = {_, SubNodeId}, id = SubNodeIdx}) ->
pubsub_index:free(node, SubNodeIdx),
mnesia:delete({pubsub_node, {Host, SubNodeId}})
end,
Removed),
Removed. Removed.

View File

@ -36,437 +36,280 @@
%%% improvements.</p> %%% improvements.</p>
-module(nodetree_tree_odbc). -module(nodetree_tree_odbc).
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-define(PUBSUB, mod_pubsub_odbc).
-define(PLUGIN_PREFIX, <<"node_">>).
-define(ODBC_SUFFIX, <<"_odbc">>).
-behaviour(gen_pubsub_nodetree).
-export([init/3, terminate/2, options/0, set_node/1, -export([init/3, terminate/2, options/0, set_node/1,
get_node/3, get_node/2, get_node/1, get_nodes/2, get_node/3, get_node/2, get_node/1, get_nodes/2,
get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
get_subnodes/3, get_subnodes_tree/3, create_node/6, get_subnodes/3, get_subnodes_tree/3, create_node/6,
delete_node/2]). delete_node/2]).
-export([raw_to_node/2]). -export([raw_to_node/2]).
%% ================ init(_Host, _ServerHost, _Opts) ->
%% API definition ok.
%% ================
%% @spec (Host, ServerHost, Opts) -> any() terminate(_Host, _ServerHost) ->
%% Host = mod_pubsub:host() ok.
%% ServerHost = host()
%% Opts = list()
%% @doc <p>Called during pubsub modules initialisation. Any pubsub plugin must
%% implement this function. It can return anything.</p>
%% <p>This function is mainly used to trigger the setup task necessary for the
%% plugin. It can be used for example by the developer to create the specific
%% module database schema if it does not exists yet.</p>
%% @spec () -> [Option]
%% Option = mod_pubsub:nodetreeOption()
%% @doc Returns the default pubsub node tree options.
%% @spec (Host, Node, From) -> pubsubNode() | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
init(_Host, _ServerHost, _Opts) -> ok.
terminate(_Host, _ServerHost) -> ok. options() ->
[{odbc, true} | nodetree_tree:options()].
options() -> [{virtual_tree, false}, {odbc, true}]. set_node(Record) when is_record(Record, pubsub_node) ->
{Host, Node} = Record#pubsub_node.nodeid,
Parent = case Record#pubsub_node.parents of
[] -> <<>>;
[First | _] -> First
end,
Type = Record#pubsub_node.type,
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
P = ejabberd_odbc:escape(Parent),
Nidx = case nodeidx(Host, Node) of
{result, OldNidx} ->
catch
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node_option where "
"nodeid='">>, OldNidx, <<"';">>]),
catch
ejabberd_odbc:sql_query_t([<<"update pubsub_node set host='">>,
H, <<"' node='">>, N,
<<"' parent='">>, P,
<<"' type='">>, Type,
<<"' where nodeid='">>,
OldNidx, <<"';">>]),
OldNidx;
_ ->
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node(host, node, "
"parent, type) values('">>,
H, <<"', '">>, N, <<"', '">>, P,
<<"', '">>, Type, <<"');">>]),
case nodeidx(Host, Node) of
{result, NewNidx} -> NewNidx;
_ -> none % this should not happen
end
end,
case Nidx of
none ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
lists:foreach(fun ({Key, Value}) ->
SKey = iolist_to_binary(atom_to_list(Key)),
SValue = ejabberd_odbc:escape(
list_to_binary(
lists:flatten(io_lib:fwrite("~p", [Value])))),
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node_option(nodeid, "
"name, val) values('">>,
Nidx, <<"', '">>,
SKey, <<"', '">>, SValue, <<"');">>])
end,
Record#pubsub_node.options),
{result, Nidx}
end.
get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node, _From) ->
get_node(Host, Node).
-spec(get_node/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> mod_pubsub:pubsubNode()
| {error, _}
).
get_node(Host, Node) -> get_node(Host, Node) ->
H = (?PUBSUB):escape(Host), H = node_hometree_odbc:encode_host(Host),
N = (?PUBSUB):escape(Node), N = ejabberd_odbc:escape(Node),
case catch case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from " ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, "pubsub_node where host='">>,
H, <<"' and node='">>, N, <<"';">>]) H, <<"' and node='">>, N, <<"';">>])
of of
{selected, {selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [RItem]} ->
[RItem]} -> raw_to_node(Host, RItem);
raw_to_node(Host, RItem); {'EXIT', _Reason} ->
{'EXIT', _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR};
{error, ?ERR_INTERNAL_SERVER_ERROR}; _ ->
_ -> {error, ?ERR_ITEM_NOT_FOUND} {error, ?ERR_ITEM_NOT_FOUND}
end. end.
-spec(get_node/1 :: get_node(Nidx) ->
(
NodeIdx::mod_pubsub:nodeIdx())
-> mod_pubsub:pubsubNode()
| {error, _}
).
get_node(NodeIdx) ->
case catch case catch
ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from " ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
"pubsub_node where nodeid='">>, "pubsub_node where nodeid='">>,
NodeIdx, <<"';">>]) Nidx, <<"';">>])
of of
{selected, {selected,
[<<"host">>, <<"node">>, <<"parent">>, <<"type">>], [<<"host">>, <<"node">>, <<"parent">>, <<"type">>], [[Host, Node, Parent, Type]]} ->
[[Host, Node, Parent, Type]]} -> raw_to_node(Host, [Node, Parent, Type, Nidx]);
raw_to_node(Host, [Node, Parent, Type, NodeIdx]); {'EXIT', _Reason} ->
{'EXIT', _Reason} -> {error, ?ERR_INTERNAL_SERVER_ERROR};
{error, ?ERR_INTERNAL_SERVER_ERROR}; _ ->
_ -> {error, ?ERR_ITEM_NOT_FOUND} {error, ?ERR_ITEM_NOT_FOUND}
end. end.
%% @spec (Host, From) -> [pubsubNode()] | {error, Reason} get_nodes(Host, _From) ->
%% Host = mod_pubsub:host() | mod_pubsub:jid() get_nodes(Host).
get_nodes(Host, _From) -> get_nodes(Host).
-spec(get_nodes/1 ::
(
Host::mod_pubsub:host())
-> [mod_pubsub:pubsubNode()]
).
get_nodes(Host) -> get_nodes(Host) ->
H = (?PUBSUB):escape(Host), H = node_hometree_odbc:encode_host(Host),
case catch case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from " ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, "pubsub_node where host='">>, H, <<"';">>])
H, <<"';">>]) of
of {selected,
{selected, [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [raw_to_node(Host, Item) || Item <- RItems];
RItems} -> _ ->
lists:map(fun (Item) -> raw_to_node(Host, Item) end, []
RItems);
_ -> []
end. end.
%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason} get_parentnodes(_Host, _Node, _From) ->
%% Host = mod_pubsub:host() | mod_pubsub:jid() [].
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% Depth = integer()
%% Record = pubsubNode()
%% @doc <p>Default node tree does not handle parents, return empty list.</p>
%% @spec (Host, Node, From) -> [{Depth, Record}] | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% Depth = integer()
%% Record = pubsubNode()
%% @doc <p>Default node tree does not handle parents, return a list %% @doc <p>Default node tree does not handle parents, return a list
%% containing just this node.</p> %% containing just this node.</p>
get_parentnodes(_Host, _Node, _From) -> [].
-spec(get_parentnodes_tree/3 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId(),
From :: jid())
-> [{0, [mod_pubsub:pubsubNode(),...]}]
).
get_parentnodes_tree(Host, Node, From) -> get_parentnodes_tree(Host, Node, From) ->
case get_node(Host, Node, From) of case get_node(Host, Node, From) of
N when is_record(N, pubsub_node) -> [{0, [N]}]; {error, _} -> [];
_Error -> [] Record -> [{0, [Record]}]
end. end.
get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node). get_subnodes(Host, Node).
%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
-spec(get_subnodes/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes(Host, Node) -> get_subnodes(Host, Node) ->
H = (?PUBSUB):escape(Host), H = node_hometree_odbc:encode_host(Host),
N = (?PUBSUB):escape(Node), N = ejabberd_odbc:escape(Node),
case catch case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from " ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, "pubsub_node where host='">>,
H, <<"' and parent='">>, N, <<"';">>]) H, <<"' and parent='">>, N, <<"';">>])
of of
{selected, {selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
RItems} -> [raw_to_node(Host, Item) || Item <- RItems];
lists:map(fun (Item) -> raw_to_node(Host, Item) end, _ ->
RItems); []
_ -> []
end. end.
get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node).
%% @spec (Host, Index) -> [pubsubNode()] | {error, Reason}
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
-spec(get_subnodes_tree/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
get_subnodes_tree(Host, Node) -> get_subnodes_tree(Host, Node) ->
H = (?PUBSUB):escape(Host), H = node_hometree_odbc:encode_host(Host),
N = (?PUBSUB):escape(Node), N = ejabberd_odbc:escape(Node),
case catch case catch
ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from " ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
"pubsub_node where host='">>, "pubsub_node where host='">>,
H, <<"' and node like '">>, N, <<"%';">>]) H, <<"' and node like '">>, N, <<"%';">>])
of of
{selected, {selected,
[<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
RItems} -> [raw_to_node(Host, Item) || Item <- RItems];
lists:map(fun (Item) -> raw_to_node(Host, Item) end, _ ->
RItems); []
_ -> []
end. end.
%% @spec (Host, Node, Type, Owner, Options, Parents) -> ok | {error, Reason}
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
%% NodeType = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
%% Parents = list()
-spec(create_node/6 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId(),
Type :: binary(),
Owner :: jid(),
Options :: mod_pubsub:nodeOptions(),
Parents :: [mod_pubsub:nodeId()])
-> {ok, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, _}
).
create_node(Host, Node, Type, Owner, Options, Parents) -> create_node(Host, Node, Type, Owner, Options, Parents) ->
BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)), BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
case nodeid(Host, Node) of case nodeidx(Host, Node) of
{error, ?ERR_ITEM_NOT_FOUND} -> {error, ?ERR_ITEM_NOT_FOUND} ->
ParentExists = case Host of ParentExists = case Host of
{_U, _S, _R} -> {_U, _S, _R} ->
%% This is special case for PEP handling %% This is special case for PEP handling
%% PEP does not uses hierarchy %% PEP does not uses hierarchy
true; true;
_ -> _ ->
case Parents of case Parents of
[] -> true; [] ->
[Parent | _] -> true;
case nodeid(Host, Parent) of [Parent | _] ->
{result, PNodeId} -> case nodeidx(Host, Parent) of
case nodeowners(PNodeId) of {result, PNode} ->
[{<<>>, Host, <<>>}] -> true; case nodeowners(PNode) of
Owners -> [{<<>>, Host, <<>>}] -> true;
lists:member(BJID, Owners) Owners -> lists:member(BJID, Owners)
end; end;
_ -> false _ ->
end; false
_ -> false end;
end _ ->
end, false
case ParentExists of end
true -> end,
case set_node(#pubsub_node{nodeid = {Host, Node}, case ParentExists of
parents = Parents, type = Type, true ->
options = Options}) case set_node(#pubsub_node{nodeid = {Host, Node},
parents = Parents, type = Type,
options = Options})
of of
{result, NodeId} -> {ok, NodeId}; {result, Nidx} -> {ok, Nidx};
Other -> Other Other -> Other
end; end;
false -> {error, ?ERR_FORBIDDEN} false ->
end; {error, ?ERR_FORBIDDEN}
{result, _} -> {error, ?ERR_CONFLICT}; end;
Error -> Error {result, _} ->
{error, ?ERR_CONFLICT};
Error ->
Error
end. end.
%% @spec (Host, Node) -> [mod_pubsub:node()]
%% Host = mod_pubsub:host() | mod_pubsub:jid()
%% Node = mod_pubsub:pubsubNode()
-spec(delete_node/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> [mod_pubsub:pubsubNode()]
).
delete_node(Host, Node) -> delete_node(Host, Node) ->
H = (?PUBSUB):escape(Host), H = node_hometree_odbc:encode_host(Host),
N = (?PUBSUB):escape(Node), N = ejabberd_odbc:escape(Node),
Removed = get_subnodes_tree(Host, Node), Removed = get_subnodes_tree(Host, Node),
catch catch ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>, H, <<"' and node like '">>, N, <<"%';">>]),
H, <<"' and node like '">>, N, <<"%';">>]),
Removed. Removed.
%% helpers %% helpers
-spec(raw_to_node/2 :: raw_to_node(Host, [Node, Parent, Type, Nidx]) ->
(
Host :: mod_pubsub:host(),
_ :: {NodeId::mod_pubsub:nodeId(),
Parent::mod_pubsub:nodeId(),
Type::binary(),
NodeIdx::mod_pubsub:nodeIdx()})
-> mod_pubsub:pubsubNode()
).
raw_to_node(Host, [Node, Parent, Type, NodeIdx]) ->
Options = case catch Options = case catch
ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option " ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
"where nodeid='">>, "where nodeid='">>, Nidx, <<"';">>])
NodeIdx, <<"';">>]) of
of {selected, [<<"name">>, <<"val">>], ROptions} ->
{selected, [<<"name">>, <<"val">>], ROptions} -> DbOpts = lists:map(fun ([Key, Value]) ->
DbOpts = lists:map(fun ([Key, Value]) -> RKey = jlib:binary_to_atom(Key),
RKey = Tokens = element(2, erl_scan:string(binary_to_list(<<Value/binary, ".">>))),
jlib:binary_to_atom(Key), RValue = element(2, erl_parse:parse_term(Tokens)),
Tokens = element(2, {RKey, RValue}
erl_scan:string( end,
binary_to_list(<<Value/binary, ".">>))), ROptions),
RValue = element(2, Module = jlib:binary_to_atom(<<"node_", Type/binary, "_odbc">>),
erl_parse:parse_term(Tokens)), StdOpts = Module:options(),
{RKey, RValue} lists:foldl(fun ({Key, Value}, Acc) ->
end, lists:keyreplace(Key, 1, Acc, {Key, Value})
ROptions),
Module =
jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
Type/binary,
(?ODBC_SUFFIX)/binary>>),
StdOpts = Module:options(),
lists:foldl(fun ({Key, Value}, Acc) ->
lists:keyreplace(Key, 1, Acc,
{Key, Value})
end,
StdOpts, DbOpts);
_ -> []
end,
Parents = case Parent of
<<>> -> [];
_ -> [Parent]
end,
#pubsub_node{nodeid =
{Host, Node},
parents = Parents,
id = NodeIdx, type = Type, options = Options}.
% @spec (NodeRecord) -> ok | {error, Reason}
%% Record = mod_pubsub:pubsub_node()
-spec(set_node/1 ::
(
Record::mod_pubsub:pubsubNode())
-> {result, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, _}
).
set_node(Record) ->
{Host, Node} = Record#pubsub_node.nodeid,
Parent = case Record#pubsub_node.parents of
[] -> <<>>;
[First | _] -> First
end,
Type = Record#pubsub_node.type,
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(Node),
P = (?PUBSUB):escape(Parent),
NodeIdx = case nodeid(Host, Node) of
{result, OldNodeIdx} ->
catch
ejabberd_odbc:sql_query_t([<<"delete from pubsub_node_option where "
"nodeid='">>,
OldNodeIdx, <<"';">>]),
catch
ejabberd_odbc:sql_query_t([<<"update pubsub_node set host='">>,
H, <<"' node='">>, N,
<<"' parent='">>, P,
<<"' type='">>, Type,
<<"' where nodeid='">>,
OldNodeIdx, <<"';">>]),
OldNodeIdx;
_ ->
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node(host, node, "
"parent, type) values('">>,
H, <<"', '">>, N, <<"', '">>, P,
<<"', '">>, Type, <<"');">>]),
case nodeid(Host, Node) of
{result, NewNodeIdx} -> NewNodeIdx;
_ -> none % this should not happen
end
end,
case NodeIdx of
none -> {error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
lists:foreach(fun ({Key, Value}) ->
SKey = iolist_to_binary(atom_to_list(Key)),
SValue =
(?PUBSUB):escape(list_to_binary(lists:flatten(io_lib:fwrite("~p",
[Value])))),
catch
ejabberd_odbc:sql_query_t([<<"insert into pubsub_node_option(nodeid, "
"name, val) values('">>,
NodeIdx, <<"', '">>,
SKey, <<"', '">>,
SValue, <<"');">>])
end,
Record#pubsub_node.options),
{result, NodeIdx}
end.
-spec(nodeid/2 ::
(
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId())
-> {result, NodeIdx::mod_pubsub:nodeIdx()}
%%%
| {error, _}
).
nodeid(Host, NodeId) ->
H = (?PUBSUB):escape(Host),
N = (?PUBSUB):escape(NodeId),
case catch
ejabberd_odbc:sql_query_t([<<"select nodeid from pubsub_node where "
"host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected, [<<"nodeid">>], [[NodeIdx]]} ->
{result, NodeIdx};
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ -> {error, ?ERR_ITEM_NOT_FOUND}
end.
-spec(nodeowners/1 ::
(
NodeIdx::mod_pubsub:nodeIdx())
-> Node_Owners::[ljid()]
).
nodeowners(NodeIdx) ->
{result, Res} = node_hometree_odbc:get_node_affiliations(NodeIdx),
lists:foldl(fun ({LJID, owner}, Acc) -> [LJID | Acc];
(_, Acc) -> Acc
end, end,
[], Res). StdOpts, DbOpts);
_ ->
[]
end,
Parents = case Parent of
<<>> -> [];
_ -> [Parent]
end,
#pubsub_node{nodeid = {Host, Node},
parents = Parents,
id = Nidx, type = Type, options = Options}.
nodeidx(Host, Node) ->
H = node_hometree_odbc:encode_host(Host),
N = ejabberd_odbc:escape(Node),
case catch
ejabberd_odbc:sql_query_t([<<"select nodeid from pubsub_node where "
"host='">>,
H, <<"' and node='">>, N, <<"';">>])
of
{selected, [<<"nodeid">>], [[Nidx]]} ->
{result, Nidx};
{'EXIT', _Reason} ->
{error, ?ERR_INTERNAL_SERVER_ERROR};
_ ->
{error, ?ERR_ITEM_NOT_FOUND}
end.
nodeowners(Nidx) ->
{result, Res} = node_hometree_odbc:get_node_affiliations(Nidx),
[LJID || {LJID, Aff} <- Res, Aff =:= owner].

View File

@ -26,143 +26,84 @@
%%% ==================================================================== %%% ====================================================================
%%% @doc The module <strong>{@module}</strong> is the PubSub node tree plugin that %%% @doc The module <strong>{@module}</strong> is the PubSub node tree plugin that
%%% allow virtual nodes handling. %%% allow virtual nodes handling. This prevent storage of nodes.
%%% <p>PubSub node tree plugins are using the {@link gen_nodetree} behaviour.</p> %%% <p>PubSub node tree plugins are using the {@link gen_nodetree} behaviour.</p>
%%% <p>This plugin development is still a work in progress. Due to optimizations in %%% <p>This plugin development is still a work in progress. Due to optimizations in
%%% mod_pubsub, this plugin can not work anymore without altering functioning. %%% mod_pubsub, this plugin can not work anymore without altering functioning.
%%% Please, send us comments, feedback and improvements.</p> %%% Please, send us comments, feedback and improvements.</p>
-module(nodetree_virtual). -module(nodetree_virtual).
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-behaviour(gen_pubsub_nodetree).
-export([init/3, terminate/2, options/0, set_node/1, -export([init/3, terminate/2, options/0, set_node/1,
get_node/3, get_node/2, get_node/1, get_nodes/2, get_node/3, get_node/2, get_node/1, get_nodes/2,
get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3, get_nodes/1, get_parentnodes/3, get_parentnodes_tree/3,
get_subnodes/3, get_subnodes_tree/3, create_node/6, get_subnodes/3, get_subnodes_tree/3, create_node/6,
delete_node/2]). delete_node/2]).
%% ================ init(_Host, _ServerHost, _Opts) ->
%% API definition ok.
%% ================
%% @spec (Host, ServerHost, Opts) -> any() terminate(_Host, _ServerHost) ->
%% Host = mod_pubsub:host() ok.
%% ServerHost = host()
%% Opts = list()
%% @doc <p>Called during pubsub modules initialisation. Any pubsub plugin must
%% implement this function. It can return anything.</p>
%% <p>This function is mainly used to trigger the setup task necessary for the
%% plugin. It can be used for example by the developer to create the specific
%% module database schema if it does not exists yet.</p>
%% @spec () -> [Option]
%% Option = mod_pubsub:nodetreeOption()
%% @doc <p>Returns the default pubsub node tree options.</p>
%% @spec (NodeRecord) -> ok | {error, Reason}
%% NodeRecord = mod_pubsub:pubsub_node()
%% @doc <p>No node record is stored on database. Just do nothing.</p>
%% @spec (Host, Node, From) -> pubsubNode()
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle a node database. Any node is considered
%% as existing. Node record contains default values.</p>
init(_Host, _ServerHost, _Opts) -> ok.
terminate(_Host, _ServerHost) -> ok. options() ->
[{virtual_tree, true}].
options() -> [{virtual_tree, true}]. set_node(_Node) ->
ok.
set_node(_NodeRecord) -> ok. get_node(Host, Node, _From) ->
get_node(Host, Node).
get_node(Host, Node, _From) -> get_node(Host, Node). get_node(Host, Node) ->
get_node(nodeidx(Host, Node)).
get_node(Host, Node) -> get_node({Host, Node}). get_node(Nidx) ->
{Host, Node} = nodeid(Nidx),
Record = #pubsub_node{nodeid = Node, id = Nidx},
Module = jlib:binary_to_atom(<<"node_", (Record#pubsub_node.type)/binary>>),
Record#pubsub_node{owners = [{<<"">>, Host, <<"">>}],
options = Module:options()}.
get_node({Host, _} = NodeId) -> get_nodes(Host, _From) ->
Record = #pubsub_node{nodeid = NodeId, id = NodeId}, get_nodes(Host).
Module = jlib:binary_to_atom(<<"node_",
(Record#pubsub_node.type)/binary>>),
Options = Module:options(),
Owners = [{<<"">>, Host, <<"">>}],
Record#pubsub_node{owners = Owners, options = Options}.
%% @spec (Host, From) -> [pubsubNode()] get_nodes(_Host) ->
%% Host = mod_pubsub:host() | mod_pubsub:jid() [].
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle a node database. Any node is considered
%% as existing. Nodes list can not be determined.</p>
%% @spec (Host, Node, From) -> [pubsubNode()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
%% @spec (Host, Node, From) -> [pubsubNode()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
%% @spec (Host, Node, From) -> [pubsubNode()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
get_nodes(Host, _From) -> get_nodes(Host).
get_nodes(_Host) -> []. get_parentnodes(_Host, _Node, _From) ->
[].
get_parentnodes(_Host, _Node, _From) -> []. get_parentnodes_tree(Host, Node, From) ->
case get_node(Host, Node, From) of
-spec(get_parentnodes_tree/3 :: Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
( _Error -> []
Host :: mod_pubsub:host(),
NodeId :: mod_pubsub:nodeId(),
From :: jid())
-> [{0, [mod_pubsub:pubsubNode(),...]}]
).
get_parentnodes_tree(Host, NodeId, From) ->
case get_node(Host, NodeId, From) of
Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
_Error -> []
end. end.
get_subnodes(Host, Node, _From) -> get_subnodes(Host, Node, _From) ->
get_subnodes(Host, Node). get_subnodes(Host, Node).
%% @spec (Host, Node, From) -> [pubsubNode()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% From = mod_pubsub:jid()
%% @doc <p>Virtual node tree does not handle parent/child. Child list is empty.</p>
get_subnodes(_Host, _Node) -> []. get_subnodes(_Host, _Node) ->
[].
get_subnodes_tree(Host, Node, _From) -> get_subnodes_tree(Host, Node, _From) ->
get_subnodes_tree(Host, Node). get_subnodes_tree(Host, Node).
%% @spec (Host, Node, Type, Owner, Options, Parents) -> ok
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% Type = mod_pubsub:nodeType()
%% Owner = mod_pubsub:jid()
%% Options = list()
%% @doc <p>No node record is stored on database. Any valid node
%% is considered as already created.</p>
%% <p>default allowed nodes: /home/host/user/any/node/name</p>
%% @spec (Host, Node) -> [mod_pubsub:node()]
%% Host = mod_pubsub:host()
%% Node = mod_pubsub:pubsubNode()
%% @doc <p>Virtual node tree does not handle parent/child.
%% node deletion just affects the corresponding node.</p>
get_subnodes_tree(_Host, _Node) -> []. get_subnodes_tree(_Host, _Node) ->
[].
create_node(Host, Node, _Type, _Owner, _Options, create_node(Host, Node, _Type, _Owner, _Options, _Parents) ->
_Parents) ->
{error, {virtual, {Host, Node}}}. {error, {virtual, {Host, Node}}}.
delete_node(Host, Node) -> [get_node(Host, Node)]. delete_node(Host, Node) ->
[get_node(Host, Node)].
%% internal helper
nodeidx(Host, Node) -> term_to_binary({Host, Node}).
nodeid(Nidx) -> binary_to_term(Nidx).

View File

@ -26,89 +26,77 @@
-include("pubsub.hrl"). -include("pubsub.hrl").
-export([add_subscription/1, read_subscription/1, -export([add_subscription/1, read_subscription/1,
delete_subscription/1, update_subscription/1]). delete_subscription/1, update_subscription/1]).
%% TODO: Those -spec lines produce errors in old Erlang versions. %% TODO: Those -spec lines produce errors in old Erlang versions.
%% They can be enabled again in ejabberd 3.0 because it uses R12B or higher. %% They can be enabled again in ejabberd 3.0 because it uses R12B or higher.
%% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} | notfound. %% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} | notfound.
read_subscription(SubID) -> read_subscription(SubID) ->
case case
ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr" ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
"iption_opt where subid = '">>, "iption_opt where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>]) ejabberd_odbc:escape(SubID), <<"'">>])
of of
{selected, [<<"opt_name">>, <<"opt_value">>], []} -> {selected, [<<"opt_name">>, <<"opt_value">>], []} ->
notfound; notfound;
{selected, [<<"opt_name">>, <<"opt_value">>], {selected, [<<"opt_name">>, <<"opt_value">>], Options} ->
Options} -> {ok,
{ok, #pubsub_subscription{subid = SubID,
#pubsub_subscription{subid = SubID, options = lists:map(fun subscription_opt_from_odbc/1, Options)}}
options =
lists:map(fun subscription_opt_from_odbc/1,
Options)}}
end. end.
%% -spec delete_subscription(SubID :: string()) -> ok. %% -spec delete_subscription(SubID :: string()) -> ok.
delete_subscription(SubID) -> delete_subscription(SubID) ->
%% -spec update_subscription(#pubsub_subscription{}) -> ok . %% -spec update_subscription(#pubsub_subscription{}) -> ok .
%% -spec add_subscription(#pubsub_subscription{}) -> ok. %% -spec add_subscription(#pubsub_subscription{}) -> ok.
%% -------------- Internal utilities ----------------------- %% -------------- Internal utilities -----------------------
ejabberd_odbc:sql_query_t([<<"delete from pubsub_subscription_opt " ejabberd_odbc:sql_query_t([<<"delete from pubsub_subscription_opt "
"where subid = '">>, "where subid = '">>,
ejabberd_odbc:escape(SubID), <<"'">>]), ejabberd_odbc:escape(SubID), <<"'">>]),
ok. ok.
update_subscription(#pubsub_subscription{subid = update_subscription(#pubsub_subscription{subid = SubId} = Sub) ->
SubId} =
Sub) ->
delete_subscription(SubId), add_subscription(Sub). delete_subscription(SubId), add_subscription(Sub).
add_subscription(#pubsub_subscription{subid = SubId, add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) ->
options = Opts}) ->
EscapedSubId = ejabberd_odbc:escape(SubId), EscapedSubId = ejabberd_odbc:escape(SubId),
lists:foreach(fun (Opt) -> lists:foreach(fun (Opt) ->
{OdbcOptName, OdbcOptValue} = {OdbcOptName, OdbcOptValue} = subscription_opt_to_odbc(Opt),
subscription_opt_to_odbc(Opt), ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, " "opt_name, opt_value)values ('">>,
"opt_name, opt_value)values ('">>, EscapedSubId, <<"','">>,
EscapedSubId, <<"','">>, OdbcOptName, <<"','">>,
OdbcOptName, <<"','">>, OdbcOptValue, <<"')">>])
OdbcOptValue, <<"')">>]) end,
end, Opts),
Opts),
ok. ok.
subscription_opt_from_odbc({<<"DELIVER">>, Value}) -> subscription_opt_from_odbc({<<"DELIVER">>, Value}) ->
{deliver, odbc_to_boolean(Value)}; {deliver, odbc_to_boolean(Value)};
subscription_opt_from_odbc({<<"DIGEST">>, Value}) -> subscription_opt_from_odbc({<<"DIGEST">>, Value}) ->
{digest, odbc_to_boolean(Value)}; {digest, odbc_to_boolean(Value)};
subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>, subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>, Value}) ->
Value}) ->
{digest_frequency, odbc_to_integer(Value)}; {digest_frequency, odbc_to_integer(Value)};
subscription_opt_from_odbc({<<"EXPIRE">>, Value}) -> subscription_opt_from_odbc({<<"EXPIRE">>, Value}) ->
{expire, odbc_to_timestamp(Value)}; {expire, odbc_to_timestamp(Value)};
subscription_opt_from_odbc({<<"INCLUDE_BODY">>, subscription_opt_from_odbc({<<"INCLUDE_BODY">>, Value}) ->
Value}) ->
{include_body, odbc_to_boolean(Value)}; {include_body, odbc_to_boolean(Value)};
%%TODO: might be > than 1 show_values value??. %%TODO: might be > than 1 show_values value??.
%% need to use compact all in only 1 opt. %% need to use compact all in only 1 opt.
subscription_opt_from_odbc({<<"SHOW_VALUES">>, subscription_opt_from_odbc({<<"SHOW_VALUES">>, Value}) ->
Value}) ->
{show_values, Value}; {show_values, Value};
subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>, subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>, Value}) ->
Value}) ->
{subscription_type, {subscription_type,
case Value of case Value of
<<"items">> -> items; <<"items">> -> items;
<<"nodes">> -> nodes <<"nodes">> -> nodes
end}; end};
subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>, subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>, Value}) ->
Value}) ->
{subscription_depth, {subscription_depth,
case Value of case Value of
<<"all">> -> all; <<"all">> -> all;
N -> odbc_to_integer(N) N -> odbc_to_integer(N)
end}. end}.
subscription_opt_to_odbc({deliver, Bool}) -> subscription_opt_to_odbc({deliver, Bool}) ->
{<<"DELIVER">>, boolean_to_odbc(Bool)}; {<<"DELIVER">>, boolean_to_odbc(Bool)};
@ -124,19 +112,18 @@ subscription_opt_to_odbc({show_values, Values}) ->
{<<"SHOW_VALUES">>, Values}; {<<"SHOW_VALUES">>, Values};
subscription_opt_to_odbc({subscription_type, Type}) -> subscription_opt_to_odbc({subscription_type, Type}) ->
{<<"SUBSCRIPTION_TYPE">>, {<<"SUBSCRIPTION_TYPE">>,
case Type of case Type of
items -> <<"items">>; items -> <<"items">>;
nodes -> <<"nodes">> nodes -> <<"nodes">>
end}; end};
subscription_opt_to_odbc({subscription_depth, Depth}) -> subscription_opt_to_odbc({subscription_depth, Depth}) ->
{<<"SUBSCRIPTION_DEPTH">>, {<<"SUBSCRIPTION_DEPTH">>,
case Depth of case Depth of
all -> <<"all">>; all -> <<"all">>;
N -> integer_to_odbc(N) N -> integer_to_odbc(N)
end}. end}.
integer_to_odbc(N) -> integer_to_odbc(N) -> iolist_to_binary(integer_to_list(N)).
iolist_to_binary(integer_to_list(N)).
boolean_to_odbc(true) -> <<"1">>; boolean_to_odbc(true) -> <<"1">>;
boolean_to_odbc(false) -> <<"0">>. boolean_to_odbc(false) -> <<"0">>.
@ -147,5 +134,4 @@ odbc_to_integer(N) -> jlib:binary_to_integer(N).
odbc_to_boolean(B) -> B == <<"1">>. odbc_to_boolean(B) -> B == <<"1">>.
odbc_to_timestamp(T) -> odbc_to_timestamp(T) -> jlib:datetime_string_to_timestamp(T).
jlib:datetime_string_to_timestamp(T).

View File

@ -29,7 +29,6 @@
%% new/1 and free/2 MUST be called inside a transaction bloc %% new/1 and free/2 MUST be called inside a transaction bloc
-module(pubsub_index). -module(pubsub_index).
-author('christophe.romain@process-one.net'). -author('christophe.romain@process-one.net').
-include("pubsub.hrl"). -include("pubsub.hrl").
@ -38,30 +37,30 @@
init(_Host, _ServerHost, _Opts) -> init(_Host, _ServerHost, _Opts) ->
mnesia:create_table(pubsub_index, mnesia:create_table(pubsub_index,
[{disc_copies, [node()]}, [{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_index)}]). {attributes, record_info(fields, pubsub_index)}]).
new(Index) -> new(Index) ->
case mnesia:read({pubsub_index, Index}) of case mnesia:read({pubsub_index, Index}) of
[I] -> [I] ->
case I#pubsub_index.free of case I#pubsub_index.free of
[] -> [] ->
Id = I#pubsub_index.last + 1, Id = I#pubsub_index.last + 1,
mnesia:write(I#pubsub_index{last = Id}), mnesia:write(I#pubsub_index{last = Id}),
Id; Id;
[Id | Free] -> [Id | Free] ->
mnesia:write(I#pubsub_index{free = Free}), Id mnesia:write(I#pubsub_index{free = Free}), Id
end; end;
_ -> _ ->
mnesia:write(#pubsub_index{index = Index, last = 1, mnesia:write(#pubsub_index{index = Index, last = 1, free = []}),
free = []}), 1
1
end. end.
free(Index, Id) -> free(Index, Id) ->
case mnesia:read({pubsub_index, Index}) of case mnesia:read({pubsub_index, Index}) of
[I] -> [I] ->
Free = I#pubsub_index.free, Free = I#pubsub_index.free,
mnesia:write(I#pubsub_index{free = [Id | Free]}); mnesia:write(I#pubsub_index{free = [Id | Free]});
_ -> ok _ ->
ok
end. end.

422
src/pubsub_migrate.erl Normal file
View File

@ -0,0 +1,422 @@
%%%----------------------------------------------------------------------
%%% File : pubsub_migrate.erl
%%% Author : Christophe Romain <christophe.romain@process-one.net>
%%% Purpose : Migration/Upgrade code put out of mod_pubsub
%%% Created : 26 Jul 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2015 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(pubsub_migrate).
-include("pubsub.hrl").
-include("logger.hrl").
-export([update_node_database/2, update_state_database/2, update_lastitem_database/2]).
update_item_database_binary() ->
F = fun () ->
case catch mnesia:read({pubsub_last_item, mnesia:first(pubsub_last_item)}) of
[First] when is_list(First#pubsub_last_item.itemid) ->
?INFO_MSG("Binarization of pubsub items table...", []),
lists:foreach(fun (Id) ->
[Node] = mnesia:read({pubsub_last_item, Id}),
ItemId = iolist_to_binary(Node#pubsub_last_item.itemid),
ok = mnesia:delete({pubsub_last_item, Id}),
ok = mnesia:write(Node#pubsub_last_item{itemid=ItemId})
end,
mnesia:all_keys(pubsub_last_item));
_-> no_need
end
end,
case mnesia:transaction(F) of
{aborted, Reason} ->
?ERROR_MSG("Failed to binarize pubsub items table: ~p", [Reason]);
{atomic, no_need} ->
ok;
{atomic, Result} ->
?INFO_MSG("Pubsub items table has been binarized: ~p", [Result])
end.
update_node_database_binary() ->
F = fun () ->
case catch mnesia:read({pubsub_node, mnesia:first(pubsub_node)}) of
[First] when is_list(First#pubsub_node.type) ->
?INFO_MSG("Binarization of pubsub nodes table...", []),
lists:foreach(fun ({H, N}) ->
[Node] = mnesia:read({pubsub_node, {H, N}}),
Type = iolist_to_binary(Node#pubsub_node.type),
BN = case N of
Binary when is_binary(Binary) ->
N;
_ ->
{result, BN1} = mod_pubsub:node_call(H, Type, path_to_node, [N]),
BN1
end,
BP = case [case P of
Binary2 when is_binary(Binary2) -> P;
_ -> element(2, mod_pubsub:node_call(H, Type, path_to_node, [P]))
end
|| P <- Node#pubsub_node.parents] of
[<<>>] -> [];
Parents -> Parents
end,
BH = case H of
{U, S, R} -> {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)};
String -> iolist_to_binary(String)
end,
Owners = [{iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)} ||
{U, S, R} <- Node#pubsub_node.owners],
ok = mnesia:delete({pubsub_node, {H, N}}),
ok = mnesia:write(Node#pubsub_node{nodeid = {BH, BN},
parents = BP,
type = Type,
owners = Owners});
(_) -> ok
end,
mnesia:all_keys(pubsub_node));
_-> no_need
end
end,
case mnesia:transaction(F) of
{aborted, Reason} ->
?ERROR_MSG("Failed to binarize pubsub node table: ~p", [Reason]);
{atomic, no_need} ->
ok;
{atomic, Result} ->
?INFO_MSG("Pubsub nodes table has been binarized: ~p", [Result])
end.
update_node_database(Host, ServerHost) ->
mnesia:del_table_index(pubsub_node, type),
mnesia:del_table_index(pubsub_node, parentid),
case catch mnesia:table_info(pubsub_node, attributes) of
[host_node, host_parent, info] ->
?INFO_MSG("Upgrading pubsub nodes table...", []),
F = fun () ->
{Result, LastIdx} = lists:foldl(fun ({pubsub_node,
NodeId, ParentId,
{nodeinfo, Items,
Options,
Entities}},
{RecList,
NodeIdx}) ->
ItemsList =
lists:foldl(fun
({item,
IID,
Publisher,
Payload},
Acc) ->
C =
{unknown,
Publisher},
M =
{now(),
Publisher},
mnesia:write(#pubsub_item{itemid
=
{IID,
NodeIdx},
creation
=
C,
modification
=
M,
payload
=
Payload}),
[{Publisher,
IID}
| Acc]
end,
[],
Items),
Owners =
dict:fold(fun
(JID,
{entity,
Aff,
Sub},
Acc) ->
UsrItems =
lists:foldl(fun
({P,
I},
IAcc) ->
case
P
of
JID ->
[I
| IAcc];
_ ->
IAcc
end
end,
[],
ItemsList),
mnesia:write({pubsub_state,
{JID,
NodeIdx},
UsrItems,
Aff,
Sub}),
case
Aff
of
owner ->
[JID
| Acc];
_ ->
Acc
end
end,
[],
Entities),
mnesia:delete({pubsub_node,
NodeId}),
{[#pubsub_node{nodeid
=
NodeId,
id
=
NodeIdx,
parents
=
[element(2,
ParentId)],
owners
=
Owners,
options
=
Options}
| RecList],
NodeIdx + 1}
end,
{[], 1},
mnesia:match_object({pubsub_node,
{Host,
'_'},
'_',
'_'})),
mnesia:write(#pubsub_index{index = node, last = LastIdx,
free = []}),
Result
end,
{atomic, NewRecords} = mnesia:transaction(F),
{atomic, ok} = mnesia:delete_table(pubsub_node),
{atomic, ok} = mnesia:create_table(pubsub_node,
[{disc_copies, [node()]},
{attributes,
record_info(fields,
pubsub_node)}]),
FNew = fun () ->
lists:foreach(fun (Record) -> mnesia:write(Record) end,
NewRecords)
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
?INFO_MSG("Pubsub nodes table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub nodes table:~n~p",
[Reason])
end;
[nodeid, parentid, type, owners, options] ->
F = fun ({pubsub_node, NodeId, {_, Parent}, Type,
Owners, Options}) ->
#pubsub_node{nodeid = NodeId, id = 0,
parents = [Parent], type = Type,
owners = Owners, options = Options}
end,
mnesia:transform_table(pubsub_node, F,
[nodeid, id, parents, type, owners, options]),
FNew = fun () ->
LastIdx = lists:foldl(fun (#pubsub_node{nodeid =
NodeId} =
PubsubNode,
NodeIdx) ->
mnesia:write(PubsubNode#pubsub_node{id
=
NodeIdx}),
lists:foreach(fun
(#pubsub_state{stateid
=
StateId} =
State) ->
{JID,
_} =
StateId,
mnesia:delete({pubsub_state,
StateId}),
mnesia:write(State#pubsub_state{stateid
=
{JID,
NodeIdx}})
end,
mnesia:match_object(#pubsub_state{stateid
=
{'_',
NodeId},
_
=
'_'})),
lists:foreach(fun
(#pubsub_item{itemid
=
ItemId} =
Item) ->
{IID,
_} =
ItemId,
{M1,
M2} =
Item#pubsub_item.modification,
{C1,
C2} =
Item#pubsub_item.creation,
mnesia:delete({pubsub_item,
ItemId}),
mnesia:write(Item#pubsub_item{itemid
=
{IID,
NodeIdx},
modification
=
{M2,
M1},
creation
=
{C2,
C1}})
end,
mnesia:match_object(#pubsub_item{itemid
=
{'_',
NodeId},
_
=
'_'})),
NodeIdx + 1
end,
1,
mnesia:match_object({pubsub_node,
{Host, '_'},
'_', '_',
'_', '_',
'_'})
++
mnesia:match_object({pubsub_node,
{{'_',
ServerHost,
'_'},
'_'},
'_', '_',
'_', '_',
'_'})),
mnesia:write(#pubsub_index{index = node,
last = LastIdx, free = []})
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
rename_default_nodeplugin(),
?INFO_MSG("Pubsub nodes table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub nodes table:~n~p",
[Reason])
end;
[nodeid, id, parent, type, owners, options] ->
F = fun ({pubsub_node, NodeId, Id, Parent, Type, Owners,
Options}) ->
#pubsub_node{nodeid = NodeId, id = Id,
parents = [Parent], type = Type,
owners = Owners, options = Options}
end,
mnesia:transform_table(pubsub_node, F,
[nodeid, id, parents, type, owners, options]),
rename_default_nodeplugin();
_ -> ok
end,
update_node_database_binary().
rename_default_nodeplugin() ->
lists:foreach(fun (Node) ->
mnesia:dirty_write(Node#pubsub_node{type =
<<"hometree">>})
end,
mnesia:dirty_match_object(#pubsub_node{type =
<<"default">>,
_ = '_'})).
update_state_database(_Host, _ServerHost) ->
case catch mnesia:table_info(pubsub_state, attributes) of
[stateid, nodeidx, items, affiliation, subscriptions] ->
?INFO_MSG("Upgrading pubsub states table...", []),
F = fun ({pubsub_state, {{U,S,R}, NodeID}, _NodeIdx, Items, Aff, Sub}, Acc) ->
JID = {iolist_to_binary(U), iolist_to_binary(S), iolist_to_binary(R)},
Subs = case Sub of
none ->
[];
[] ->
[];
_ ->
{result, SubID} = pubsub_subscription:subscribe_node(JID, NodeID, []),
[{Sub, SubID}]
end,
NewState = #pubsub_state{stateid = {JID, NodeID},
items = Items,
affiliation = Aff,
subscriptions = Subs},
[NewState | Acc]
end,
{atomic, NewRecs} = mnesia:transaction(fun mnesia:foldl/3,
[F, [], pubsub_state]),
{atomic, ok} = mnesia:delete_table(pubsub_state),
{atomic, ok} = mnesia:create_table(pubsub_state,
[{disc_copies, [node()]},
{attributes, record_info(fields, pubsub_state)}]),
FNew = fun () ->
lists:foreach(fun mnesia:write/1, NewRecs)
end,
case mnesia:transaction(FNew) of
{atomic, Result} ->
?INFO_MSG("Pubsub states table upgraded: ~p",
[Result]);
{aborted, Reason} ->
?ERROR_MSG("Problem upgrading Pubsub states table:~n~p",
[Reason])
end;
_ ->
ok
end.
update_lastitem_database(_Host, _ServerHost) ->
update_item_database_binary().

View File

@ -26,169 +26,111 @@
%% API %% API
-export([init/0, subscribe_node/3, unsubscribe_node/3, -export([init/0, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4, get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]). get_options_xform/2, parse_options_xform/1]).
% Internal function also exported for use in transactional bloc from pubsub plugins % Internal function also exported for use in transactional bloc from pubsub plugins
-export([add_subscription/3, delete_subscription/3, -export([add_subscription/3, delete_subscription/3,
read_subscription/3, write_subscription/4]). read_subscription/3, write_subscription/4]).
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-define(PUBSUB_DIGEST, <<"pubsub#digest">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>).
-define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>).
-define(PUBSUB_DIGEST_FREQUENCY,
<<"pubsub#digest_frequency">>).
-define(PUBSUB_EXPIRE, <<"pubsub#expire">>). -define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>). -define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>). -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, -define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
<<"pubsub#subscription_type">>). -define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
-define(PUBSUB_SUBSCRIPTION_DEPTH, "(aggregations) of notifications or all notifications individually">>).
<<"pubsub#subscription_depth">>). -define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(DELIVER_LABEL, -define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>).
<<"Whether an entity wants to receive or " -define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an "
"disable notifications">>). "XMPP message body in addition to the payload format">>).
-define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>).
-define(DIGEST_LABEL, -define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>).
<<"Whether an entity wants to receive digests " -define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>).
"(aggregations) of notifications or all " -define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>).
"notifications individually">>). -define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(DIGEST_FREQUENCY_LABEL, -define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>).
<<"The minimum number of milliseconds between " -define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>).
"sending any two notification digests">>). -define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>).
-define(EXPIRE_LABEL, -define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>).
<<"The DateTime at which a leased subscription " -define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>).
"will end or has ended">>).
-define(INCLUDE_BODY_LABEL,
<<"Whether an entity wants to receive an "
"XMPP message body in addition to the "
"payload format">>).
-define(SHOW_VALUES_LABEL,
<<"The presence states for which an entity "
"wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL,
<<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL,
<<"Depth from subscription for which to "
"receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL,
<<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL,
<<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL,
<<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL,
<<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL,
<<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL,
<<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL,
<<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL,
<<"Receive notification from direct child "
"nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL,
<<"Receive notification from all descendent "
"nodes">>).
%%==================================================================== %%====================================================================
%% API %% API
%%==================================================================== %%====================================================================
init() -> ok = create_table(). init() -> ok = create_table().
subscribe_node(JID, NodeID, Options) -> subscribe_node(JID, NodeId, Options) ->
case catch mnesia:sync_dirty(fun add_subscription/3, case catch mnesia:sync_dirty(fun add_subscription/3, [JID, NodeId, Options])
[JID, NodeID, Options]) of
of {'EXIT', {aborted, Error}} -> Error;
{'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error};
{error, Error} -> {error, Error}; Result -> {result, Result}
Result -> {result, Result}
end. end.
unsubscribe_node(JID, NodeID, SubID) -> unsubscribe_node(JID, NodeId, SubID) ->
case catch mnesia:sync_dirty(fun delete_subscription/3, case catch mnesia:sync_dirty(fun delete_subscription/3, [JID, NodeId, SubID])
[JID, NodeID, SubID]) of
of {'EXIT', {aborted, Error}} -> Error;
{'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error};
{error, Error} -> {error, Error}; Result -> {result, Result}
Result -> {result, Result}
end. end.
get_subscription(JID, NodeID, SubID) -> get_subscription(JID, NodeId, SubID) ->
case catch mnesia:sync_dirty(fun read_subscription/3, case catch mnesia:sync_dirty(fun read_subscription/3, [JID, NodeId, SubID])
[JID, NodeID, SubID]) of
of {'EXIT', {aborted, Error}} -> Error;
{'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error};
{error, Error} -> {error, Error}; Result -> {result, Result}
Result -> {result, Result}
end. end.
set_subscription(JID, NodeID, SubID, Options) -> set_subscription(JID, NodeId, SubID, Options) ->
case catch mnesia:sync_dirty(fun write_subscription/4, case catch mnesia:sync_dirty(fun write_subscription/4, [JID, NodeId, SubID, Options])
[JID, NodeID, SubID, Options]) of
of {'EXIT', {aborted, Error}} -> Error;
{'EXIT', {aborted, Error}} -> Error; {error, Error} -> {error, Error};
{error, Error} -> {error, Error}; Result -> {result, Result}
Result -> {result, Result}
end. end.
get_options_xform(Lang, Options) -> get_options_xform(Lang, Options) ->
Keys = [deliver, show_values, subscription_type, Keys = [deliver, show_values, subscription_type, subscription_depth],
subscription_depth], XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
XFields = [get_option_xfield(Lang, Key, Options)
|| Key <- Keys],
{result, {result,
#xmlel{name = <<"x">>, #xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA}], attrs = [{<<"xmlns">>, ?NS_XDATA}],
children = children =
[#xmlel{name = <<"field">>, [#xmlel{name = <<"field">>,
attrs = attrs =
[{<<"var">>, <<"FORM_TYPE">>}, [{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}], {<<"type">>, <<"hidden">>}],
children = children =
[#xmlel{name = <<"value">>, attrs = [], [#xmlel{name = <<"value">>, attrs = [],
children = children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}] [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}. ++ XFields}}.
parse_options_xform(XFields) -> parse_options_xform(XFields) ->
case xml:remove_cdata(XFields) of case xml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] -> [#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) -> XData when is_list(XData) ->
Opts = set_xoption(XData, []), Opts = set_xoption(XData, []),
{result, Opts}; {result, Opts};
Other -> Other Other -> Other
end; end;
_ -> {result, []} _ -> {result, []}
end. end.
%%==================================================================== %%====================================================================
@ -196,69 +138,67 @@ parse_options_xform(XFields) ->
%%==================================================================== %%====================================================================
create_table() -> create_table() ->
case mnesia:create_table(pubsub_subscription, case mnesia:create_table(pubsub_subscription,
[{disc_copies, [node()]}, [{disc_copies, [node()]},
{attributes, {attributes,
record_info(fields, pubsub_subscription)}, record_info(fields, pubsub_subscription)},
{type, set}]) {type, set}])
of of
{atomic, ok} -> ok; {atomic, ok} -> ok;
{aborted, {already_exists, _}} -> ok; {aborted, {already_exists, _}} -> ok;
Other -> Other Other -> Other
end. end.
-spec(add_subscription/3 :: -spec(add_subscription/3 ::
( (
_JID :: ljid(), _JID :: ljid(),
_NodeID :: mod_pubsub:nodeIdx(), _NodeId :: mod_pubsub:nodeIdx(),
Options :: [] | mod_pubsub:subOptions()) Options :: [] | mod_pubsub:subOptions())
-> SubId :: mod_pubsub:subId() -> SubId :: mod_pubsub:subId()
). ).
add_subscription(_JID, _NodeID, []) -> make_subid(); add_subscription(_JID, _NodeId, []) -> make_subid();
add_subscription(_JID, _NodeID, Options) -> add_subscription(_JID, _NodeId, Options) ->
SubID = make_subid(), SubID = make_subid(),
mnesia:write(#pubsub_subscription{subid = SubID, mnesia:write(#pubsub_subscription{subid = SubID, options = Options}),
options = Options}),
SubID. SubID.
-spec(delete_subscription/3 :: -spec(delete_subscription/3 ::
( (
_JID :: _, _JID :: _,
_NodeID :: _, _NodeId :: _,
SubId :: mod_pubsub:subId()) SubId :: mod_pubsub:subId())
-> ok -> ok
). ).
delete_subscription(_JID, _NodeID, SubID) -> delete_subscription(_JID, _NodeId, SubID) ->
mnesia:delete({pubsub_subscription, SubID}). mnesia:delete({pubsub_subscription, SubID}).
-spec(read_subscription/3 :: -spec(read_subscription/3 ::
( (
_JID :: ljid(), _JID :: ljid(),
_NodeID :: _, _NodeId :: _,
SubID :: mod_pubsub:subId()) SubID :: mod_pubsub:subId())
-> mod_pubsub:pubsubSubscription() -> mod_pubsub:pubsubSubscription()
| {error, notfound} | {error, notfound}
). ).
read_subscription(_JID, _NodeID, SubID) -> read_subscription(_JID, _NodeId, SubID) ->
case mnesia:read({pubsub_subscription, SubID}) of case mnesia:read({pubsub_subscription, SubID}) of
[Sub] -> Sub; [Sub] -> Sub;
_ -> {error, notfound} _ -> {error, notfound}
end. end.
-spec(write_subscription/4 :: -spec(write_subscription/4 ::
( (
_JID :: ljid(), _JID :: ljid(),
_NodeID :: _, _NodeId :: _,
SubID :: mod_pubsub:subId(), SubID :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions()) Options :: mod_pubsub:subOptions())
-> ok -> ok
). ).
write_subscription(_JID, _NodeID, SubID, Options) -> write_subscription(_JID, _NodeId, SubID, Options) ->
mnesia:write(#pubsub_subscription{subid = SubID, mnesia:write(#pubsub_subscription{subid = SubID, options = Options}).
options = Options}).
-spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()). -spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()).
make_subid() -> make_subid() ->
@ -274,43 +214,42 @@ make_subid() ->
set_xoption([], Opts) -> Opts; set_xoption([], Opts) -> Opts;
set_xoption([{Var, Value} | T], Opts) -> set_xoption([{Var, Value} | T], Opts) ->
NewOpts = case var_xfield(Var) of NewOpts = case var_xfield(Var) of
{error, _} -> Opts; {error, _} -> Opts;
Key -> Key ->
Val = val_xfield(Key, Value), Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val}) lists:keystore(Key, 1, Opts, {Key, Val})
end, end,
set_xoption(T, NewOpts). set_xoption(T, NewOpts).
%% Return the options list's key for an XForm var. %% Return the options list's key for an XForm var.
%% Convert Values for option list's Key. %% Convert Values for option list's Key.
var_xfield(?PUBSUB_DELIVER) -> deliver; var_xfield(?PUBSUB_DELIVER) -> deliver;
var_xfield(?PUBSUB_DIGEST) -> digest; var_xfield(?PUBSUB_DIGEST) -> digest;
var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
digest_frequency;
var_xfield(?PUBSUB_EXPIRE) -> expire; var_xfield(?PUBSUB_EXPIRE) -> expire;
var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body; var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
var_xfield(?PUBSUB_SHOW_VALUES) -> show_values; var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
subscription_type; var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
subscription_depth;
var_xfield(_) -> {error, badarg}. var_xfield(_) -> {error, badarg}.
val_xfield(deliver, [Val]) -> xopt_to_bool(Val); val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest, [Val]) -> xopt_to_bool(Val); val_xfield(digest, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest_frequency, [Val]) -> val_xfield(digest_frequency, [Val]) ->
% jlib:binary_to_integer(Val); case catch jlib:binary_to_integer(Val) of
%val_xfield(expire, [Val]) -> N when is_integer(N) -> N;
% jlib:datetime_string_to_timestamp(Val); _ -> {error, ?ERR_NOT_ACCEPTABLE}
%val_xfield(include_body, [Val]) -> xopt_to_bool(Val); end;
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(show_values, Vals) -> Vals; val_xfield(show_values, Vals) -> Vals;
val_xfield(subscription_type, [<<"items">>]) -> items; val_xfield(subscription_type, [<<"items">>]) -> items;
val_xfield(subscription_type, [<<"nodes">>]) -> nodes; val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
val_xfield(subscription_depth, [<<"all">>]) -> all; val_xfield(subscription_depth, [<<"all">>]) -> all;
val_xfield(subscription_depth, [Depth]) -> val_xfield(subscription_depth, [Depth]) ->
case catch jlib:binary_to_integer(Depth) of case catch jlib:binary_to_integer(Depth) of
N when is_integer(N) -> N; N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE} _ -> {error, ?ERR_NOT_ACCEPTABLE}
end. end.
%% Convert XForm booleans to Erlang booleans. %% Convert XForm booleans to Erlang booleans.
@ -321,31 +260,30 @@ xopt_to_bool(<<"true">>) -> true;
xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}. xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
-spec(get_option_xfield/3 :: -spec(get_option_xfield/3 ::
( (
Lang :: binary(), Lang :: binary(),
Key :: atom(), Key :: atom(),
Options :: mod_pubsub:subOptions()) Options :: mod_pubsub:subOptions())
-> xmlel() -> xmlel()
). ).
%% Return a field for an XForm for Key, with data filled in, if %% Return a field for an XForm for Key, with data filled in, if
%% applicable, from Options. %% applicable, from Options.
get_option_xfield(Lang, Key, Options) -> get_option_xfield(Lang, Key, Options) ->
Var = xfield_var(Key), Var = xfield_var(Key),
Label = xfield_label(Key), Label = xfield_label(Key),
{Type, OptEls} = type_and_options(xfield_type(Key), {Type, OptEls} = type_and_options(xfield_type(Key), Lang),
Lang),
Vals = case lists:keysearch(Key, 1, Options) of Vals = case lists:keysearch(Key, 1, Options) of
{value, {_, Val}} -> {value, {_, Val}} ->
[tr_xfield_values(Vals) [tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)]; || Vals <- xfield_val(Key, Val)];
false -> [] false -> []
end, end,
#xmlel{name = <<"field">>, #xmlel{name = <<"field">>,
attrs = attrs =
[{<<"var">>, Var}, {<<"type">>, Type}, [{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}], {<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}. children = OptEls ++ Vals}.
type_and_options({Type, Options}, Lang) -> type_and_options({Type, Options}, Lang) ->
{Type, [tr_xfield_options(O, Lang) || O <- Options]}; {Type, [tr_xfield_options(O, Lang) || O <- Options]};
@ -353,42 +291,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
tr_xfield_options({Value, Label}, Lang) -> tr_xfield_options({Value, Label}, Lang) ->
#xmlel{name = <<"option">>, #xmlel{name = <<"option">>,
attrs = attrs =
[{<<"label">>, translate:translate(Lang, Label)}], [{<<"label">>, translate:translate(Lang, Label)}],
children = children =
[#xmlel{name = <<"value">>, attrs = [], [#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}. children = [{xmlcdata, Value}]}]}.
tr_xfield_values(Value) -> tr_xfield_values(Value) ->
%% Return the XForm variable name for a subscription option key. %% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key. %% Return the XForm variable type for a subscription option key.
#xmlel{name = <<"value">>, attrs = [], #xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}. children = [{xmlcdata, Value}]}.
-spec(xfield_var/1 ::
(
Var :: 'deliver'
% | 'digest'
% | 'digest_frequency'
% | 'expire'
% | 'include_body'
| 'show_values'
| 'subscription_type'
| 'subscription_depth')
-> binary()
).
xfield_var(deliver) -> ?PUBSUB_DELIVER; xfield_var(deliver) -> ?PUBSUB_DELIVER;
%xfield_var(digest) -> ?PUBSUB_DIGEST; %xfield_var(digest) -> ?PUBSUB_DIGEST;
%xfield_var(digest_frequency) -> %xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
% ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(expire) -> ?PUBSUB_EXPIRE; %xfield_var(expire) -> ?PUBSUB_EXPIRE;
%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY; %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
xfield_var(subscription_type) -> xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_var(subscription_depth) ->
?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_type(deliver) -> <<"boolean">>; xfield_type(deliver) -> <<"boolean">>;
%xfield_type(digest) -> <<"boolean">>; %xfield_type(digest) -> <<"boolean">>;
@ -397,52 +319,31 @@ xfield_type(deliver) -> <<"boolean">>;
%xfield_type(include_body) -> <<"boolean">>; %xfield_type(include_body) -> <<"boolean">>;
xfield_type(show_values) -> xfield_type(show_values) ->
{<<"list-multi">>, {<<"list-multi">>,
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
xfield_type(subscription_type) -> xfield_type(subscription_type) ->
{<<"list-single">>, {<<"list-single">>,
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
xfield_type(subscription_depth) -> xfield_type(subscription_depth) ->
{<<"list-single">>, {<<"list-single">>,
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
%% Return the XForm variable label for a subscription option key. %% Return the XForm variable label for a subscription option key.
xfield_label(deliver) -> ?DELIVER_LABEL; xfield_label(deliver) -> ?DELIVER_LABEL;
%xfield_label(digest) -> ?DIGEST_LABEL; %xfield_label(digest) -> ?DIGEST_LABEL;
%xfield_label(digest_frequency) -> %xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
% ?DIGEST_FREQUENCY_LABEL;
%xfield_label(expire) -> ?EXPIRE_LABEL; %xfield_label(expire) -> ?EXPIRE_LABEL;
%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL; %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
xfield_label(show_values) -> ?SHOW_VALUES_LABEL; xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
%% Return the XForm value for a subscription option key. %% Return the XForm value for a subscription option key.
%% Convert erlang booleans to XForms. %% Convert erlang booleans to XForms.
xfield_label(subscription_type) -> xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
?SUBSCRIPTION_TYPE_LABEL; xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
xfield_label(subscription_depth) ->
?SUBSCRIPTION_DEPTH_LABEL.
-spec(xfield_val/2 ::
(
Field :: 'deliver'
% | 'digest'
% | 'digest_frequency'
% | 'expire'
% | 'include_body'
| 'show_values'
| 'subscription_type'
| 'subscription_depth',
Val :: boolean()
| binary()
| integer()
| [binary()])
% | erlang:timestamp())
-> [binary()]
).
xfield_val(deliver, Val) -> [bool_to_xopt(Val)]; xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
%xfield_val(digest, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest, Val) -> [bool_to_xopt(Val)];
@ -450,7 +351,7 @@ xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
% [iolist_to_binary(integer_to_list(Val))]; % [iolist_to_binary(integer_to_list(Val))];
%xfield_val(expire, Val) -> %xfield_val(expire, Val) ->
% [jlib:now_to_utc_string(Val)]; % [jlib:now_to_utc_string(Val)];
%%xfield_val(include_body, Val) -> [bool_to_xopt(Val)]; %xfield_val(include_body, Val) -> [bool_to_xopt(Val)];
xfield_val(show_values, Val) -> Val; xfield_val(show_values, Val) -> Val;
xfield_val(subscription_type, items) -> [<<"items">>]; xfield_val(subscription_type, items) -> [<<"items">>];
xfield_val(subscription_type, nodes) -> [<<"nodes">>]; xfield_val(subscription_type, nodes) -> [<<"nodes">>];

View File

@ -22,98 +22,45 @@
%%% ==================================================================== %%% ====================================================================
-module(pubsub_subscription_odbc). -module(pubsub_subscription_odbc).
-author("pablo.polvorin@process-one.net"). -author("pablo.polvorin@process-one.net").
%% API %% API
-export([init/0, subscribe_node/3, unsubscribe_node/3, -export([init/0, subscribe_node/3, unsubscribe_node/3,
get_subscription/3, set_subscription/4, get_subscription/3, set_subscription/4,
get_options_xform/2, parse_options_xform/1]). get_options_xform/2, parse_options_xform/1]).
-include("pubsub.hrl"). -include("pubsub.hrl").
-include("jlib.hrl"). -include("jlib.hrl").
-define(PUBSUB_DELIVER, <<"pubsub#deliver">>). -define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-define(PUBSUB_DIGEST, <<"pubsub#digest">>). -define(PUBSUB_DIGEST, <<"pubsub#digest">>).
-define(PUBSUB_DIGEST_FREQUENCY, <<"pubsub#digest_frequency">>).
-define(PUBSUB_DIGEST_FREQUENCY,
<<"pubsub#digest_frequency">>).
-define(PUBSUB_EXPIRE, <<"pubsub#expire">>). -define(PUBSUB_EXPIRE, <<"pubsub#expire">>).
-define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>). -define(PUBSUB_INCLUDE_BODY, <<"pubsub#include_body">>).
-define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>). -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
-define(PUBSUB_SUBSCRIPTION_TYPE, -define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
<<"pubsub#subscription_type">>). -define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
-define(PUBSUB_SUBSCRIPTION_DEPTH, "(aggregations) of notifications or all notifications individually">>).
<<"pubsub#subscription_depth">>). -define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
"sending any two notification digests">>).
-define(DELIVER_LABEL, -define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription will end or has ended">>).
<<"Whether an entity wants to receive or " -define(INCLUDE_BODY_LABEL, <<"Whether an entity wants to receive an "
"disable notifications">>). "XMPP message body in addition to the payload format">>).
-define(SHOW_VALUES_LABEL, <<"The presence states for which an entity wants to receive notifications">>).
-define(DIGEST_LABEL, -define(SUBSCRIPTION_TYPE_LABEL, <<"Type of notification to receive">>).
<<"Whether an entity wants to receive digests " -define(SUBSCRIPTION_DEPTH_LABEL, <<"Depth from subscription for which to receive notifications">>).
"(aggregations) of notifications or all " -define(SHOW_VALUE_AWAY_LABEL, <<"XMPP Show Value of Away">>).
"notifications individually">>). -define(SHOW_VALUE_CHAT_LABEL, <<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL, <<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(DIGEST_FREQUENCY_LABEL, -define(SHOW_VALUE_ONLINE_LABEL, <<"Mere Availability in XMPP (No Show Value)">>).
<<"The minimum number of milliseconds between " -define(SHOW_VALUE_XA_LABEL, <<"XMPP Show Value of XA (Extended Away)">>).
"sending any two notification digests">>). -define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL, <<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL, <<"Receive notification of new nodes only">>).
-define(EXPIRE_LABEL, -define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL, <<"Receive notification from direct child nodes only">>).
<<"The DateTime at which a leased subscription " -define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL, <<"Receive notification from all descendent nodes">>).
"will end or has ended">>).
-define(INCLUDE_BODY_LABEL,
<<"Whether an entity wants to receive an "
"XMPP message body in addition to the "
"payload format">>).
-define(SHOW_VALUES_LABEL,
<<"The presence states for which an entity "
"wants to receive notifications">>).
-define(SUBSCRIPTION_TYPE_LABEL,
<<"Type of notification to receive">>).
-define(SUBSCRIPTION_DEPTH_LABEL,
<<"Depth from subscription for which to "
"receive notifications">>).
-define(SHOW_VALUE_AWAY_LABEL,
<<"XMPP Show Value of Away">>).
-define(SHOW_VALUE_CHAT_LABEL,
<<"XMPP Show Value of Chat">>).
-define(SHOW_VALUE_DND_LABEL,
<<"XMPP Show Value of DND (Do Not Disturb)">>).
-define(SHOW_VALUE_ONLINE_LABEL,
<<"Mere Availability in XMPP (No Show Value)">>).
-define(SHOW_VALUE_XA_LABEL,
<<"XMPP Show Value of XA (Extended Away)">>).
-define(SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL,
<<"Receive notification of new items only">>).
-define(SUBSCRIPTION_TYPE_VALUE_NODES_LABEL,
<<"Receive notification of new nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL,
<<"Receive notification from direct child "
"nodes only">>).
-define(SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL,
<<"Receive notification from all descendent "
"nodes">>).
-define(DB_MOD, pubsub_db_odbc). -define(DB_MOD, pubsub_db_odbc).
%%==================================================================== %%====================================================================
@ -123,99 +70,92 @@
init() -> ok = create_table(). init() -> ok = create_table().
-spec(subscribe_node/3 :: -spec(subscribe_node/3 ::
( (
_JID :: _, _JID :: _,
_NodeID :: _, _NodeId :: _,
Options :: mod_pubsub:subOptions()) Options :: [] | mod_pubsub:subOptions())
-> {result, mod_pubsub:subId()} -> {result, mod_pubsub:subId()}
). ).
subscribe_node(_JID, _NodeID, Options) -> subscribe_node(_JID, _NodeId, Options) ->
SubID = make_subid(), SubID = make_subid(),
(?DB_MOD):add_subscription(#pubsub_subscription{subid = (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}),
SubID,
options = Options}),
{result, SubID}. {result, SubID}.
-spec(unsubscribe_node/3 :: -spec(unsubscribe_node/3 ::
( (
_JID :: _, _JID :: _,
_NodeID :: _, _NodeId :: _,
SubID :: mod_pubsub:subId()) SubID :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()} -> {result, mod_pubsub:subscription()}
| {error, notfound} | {error, notfound}
). ).
unsubscribe_node(_JID, _NodeID, SubID) -> unsubscribe_node(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} -> {ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub};
(?DB_MOD):delete_subscription(SubID), {result, Sub}; notfound -> {error, notfound}
notfound -> {error, notfound}
end. end.
-spec(get_subscription/3 :: -spec(get_subscription/3 ::
( (
_JID :: _, _JID :: _,
_NodeID :: _, _NodeId :: _,
SubId :: mod_pubsub:subId()) SubId :: mod_pubsub:subId())
-> {result, mod_pubsub:subscription()} -> {result, mod_pubsub:subscription()}
| {error, notfound} | {error, notfound}
). ).
get_subscription(_JID, _NodeID, SubID) -> get_subscription(_JID, _NodeId, SubID) ->
case (?DB_MOD):read_subscription(SubID) of case (?DB_MOD):read_subscription(SubID) of
{ok, Sub} -> {result, Sub}; {ok, Sub} -> {result, Sub};
notfound -> {error, notfound} notfound -> {error, notfound}
end. end.
-spec(set_subscription/4 :: -spec(set_subscription/4 ::
( (
_JID :: _, _JID :: _,
_NodeID :: _, _NodeId :: _,
SubId :: mod_pubsub:subId(), SubId :: mod_pubsub:subId(),
Options :: mod_pubsub:subOptions()) Options :: mod_pubsub:subOptions())
-> {result, ok} -> {result, ok}
). ).
set_subscription(_JID, _NodeID, SubID, Options) -> set_subscription(_JID, _NodeId, SubID, Options) ->
case (?DB_MOD):read_subscription(SubID) of case (?DB_MOD):read_subscription(SubID) of
{ok, _} -> {ok, _} ->
(?DB_MOD):update_subscription(#pubsub_subscription{subid (?DB_MOD):update_subscription(#pubsub_subscription{subid = SubID,
= SubID, options = Options}),
options = {result, ok};
Options}), notfound ->
{result, ok}; (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID,
notfound -> options = Options}),
(?DB_MOD):add_subscription(#pubsub_subscription{subid = {result, ok}
SubID,
options = Options}),
{result, ok}
end. end.
get_options_xform(Lang, Options) -> get_options_xform(Lang, Options) ->
Keys = [deliver, show_values, subscription_type, subscription_depth], Keys = [deliver, show_values, subscription_type, subscription_depth],
XFields = [get_option_xfield(Lang, Key, Options) XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
|| Key <- Keys],
{result, {result,
#xmlel{name = <<"x">>, #xmlel{name = <<"x">>,
attrs = [{<<"xmlns">>, ?NS_XDATA}], attrs = [{<<"xmlns">>, ?NS_XDATA}],
children = children =
[#xmlel{name = <<"field">>, [#xmlel{name = <<"field">>,
attrs = attrs =
[{<<"var">>, <<"FORM_TYPE">>}, [{<<"var">>, <<"FORM_TYPE">>},
{<<"type">>, <<"hidden">>}], {<<"type">>, <<"hidden">>}],
children = children =
[#xmlel{name = <<"value">>, attrs = [], [#xmlel{name = <<"value">>, attrs = [],
children = children =
[{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}] [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
++ XFields}}. ++ XFields}}.
parse_options_xform(XFields) -> parse_options_xform(XFields) ->
case xml:remove_cdata(XFields) of case xml:remove_cdata(XFields) of
[#xmlel{name = <<"x">>} = XEl] -> [#xmlel{name = <<"x">>} = XEl] ->
case jlib:parse_xdata_submit(XEl) of case jlib:parse_xdata_submit(XEl) of
XData when is_list(XData) -> XData when is_list(XData) ->
Opts = set_xoption(XData, []), Opts = set_xoption(XData, []),
{result, Opts}; {result, Opts};
Other -> Other Other -> Other
end; end;
_ -> {result, []} _ -> {result, []}
end. end.
%%==================================================================== %%====================================================================
@ -237,43 +177,42 @@ make_subid() ->
set_xoption([], Opts) -> Opts; set_xoption([], Opts) -> Opts;
set_xoption([{Var, Value} | T], Opts) -> set_xoption([{Var, Value} | T], Opts) ->
NewOpts = case var_xfield(Var) of NewOpts = case var_xfield(Var) of
{error, _} -> Opts; {error, _} -> Opts;
Key -> Key ->
Val = val_xfield(Key, Value), Val = val_xfield(Key, Value),
lists:keystore(Key, 1, Opts, {Key, Val}) lists:keystore(Key, 1, Opts, {Key, Val})
end, end,
set_xoption(T, NewOpts). set_xoption(T, NewOpts).
%% Return the options list's key for an XForm var. %% Return the options list's key for an XForm var.
%% Convert Values for option list's Key. %% Convert Values for option list's Key.
var_xfield(?PUBSUB_DELIVER) -> deliver; var_xfield(?PUBSUB_DELIVER) -> deliver;
%var_xfield(?PUBSUB_DIGEST) -> digest; var_xfield(?PUBSUB_DIGEST) -> digest;
%var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
% digest_frequency; var_xfield(?PUBSUB_EXPIRE) -> expire;
%var_xfield(?PUBSUB_EXPIRE) -> expire; var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
%var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
var_xfield(?PUBSUB_SHOW_VALUES) -> show_values; var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
subscription_type; var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
subscription_depth;
var_xfield(_) -> {error, badarg}. var_xfield(_) -> {error, badarg}.
val_xfield(deliver, [Val]) -> xopt_to_bool(Val); val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest, [Val]) -> xopt_to_bool(Val); val_xfield(digest, [Val]) -> xopt_to_bool(Val);
%val_xfield(digest_frequency, [Val]) -> val_xfield(digest_frequency, [Val]) ->
% jlib:binary_to_integer(Val); case catch jlib:binary_to_integer(Val) of
%val_xfield(expire, [Val]) -> N when is_integer(N) -> N;
% jlib:datetime_string_to_timestamp(Val); _ -> {error, ?ERR_NOT_ACCEPTABLE}
%val_xfield(include_body, [Val]) -> xopt_to_bool(Val); end;
val_xfield(expire, [Val]) -> jlib:datetime_string_to_timestamp(Val);
val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
val_xfield(show_values, Vals) -> Vals; val_xfield(show_values, Vals) -> Vals;
val_xfield(subscription_type, [<<"items">>]) -> items; val_xfield(subscription_type, [<<"items">>]) -> items;
val_xfield(subscription_type, [<<"nodes">>]) -> nodes; val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
val_xfield(subscription_depth, [<<"all">>]) -> all; val_xfield(subscription_depth, [<<"all">>]) -> all;
val_xfield(subscription_depth, [Depth]) -> val_xfield(subscription_depth, [Depth]) ->
case catch jlib:binary_to_integer(Depth) of case catch jlib:binary_to_integer(Depth) of
N when is_integer(N) -> N; N when is_integer(N) -> N;
_ -> {error, ?ERR_NOT_ACCEPTABLE} _ -> {error, ?ERR_NOT_ACCEPTABLE}
end. end.
%% Convert XForm booleans to Erlang booleans. %% Convert XForm booleans to Erlang booleans.
@ -288,19 +227,18 @@ xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
get_option_xfield(Lang, Key, Options) -> get_option_xfield(Lang, Key, Options) ->
Var = xfield_var(Key), Var = xfield_var(Key),
Label = xfield_label(Key), Label = xfield_label(Key),
{Type, OptEls} = type_and_options(xfield_type(Key), {Type, OptEls} = type_and_options(xfield_type(Key), Lang),
Lang),
Vals = case lists:keysearch(Key, 1, Options) of Vals = case lists:keysearch(Key, 1, Options) of
{value, {_, Val}} -> {value, {_, Val}} ->
[tr_xfield_values(Vals) [tr_xfield_values(Vals)
|| Vals <- xfield_val(Key, Val)]; || Vals <- xfield_val(Key, Val)];
false -> [] false -> []
end, end,
#xmlel{name = <<"field">>, #xmlel{name = <<"field">>,
attrs = attrs =
[{<<"var">>, Var}, {<<"type">>, Type}, [{<<"var">>, Var}, {<<"type">>, Type},
{<<"label">>, translate:translate(Lang, Label)}], {<<"label">>, translate:translate(Lang, Label)}],
children = OptEls ++ Vals}. children = OptEls ++ Vals}.
type_and_options({Type, Options}, Lang) -> type_and_options({Type, Options}, Lang) ->
{Type, [tr_xfield_options(O, Lang) || O <- Options]}; {Type, [tr_xfield_options(O, Lang) || O <- Options]};
@ -308,29 +246,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
tr_xfield_options({Value, Label}, Lang) -> tr_xfield_options({Value, Label}, Lang) ->
#xmlel{name = <<"option">>, #xmlel{name = <<"option">>,
attrs = attrs =
[{<<"label">>, translate:translate(Lang, Label)}], [{<<"label">>, translate:translate(Lang, Label)}],
children = children =
[#xmlel{name = <<"value">>, attrs = [], [#xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}]}. children = [{xmlcdata, Value}]}]}.
tr_xfield_values(Value) -> tr_xfield_values(Value) ->
%% Return the XForm variable name for a subscription option key. %% Return the XForm variable name for a subscription option key.
%% Return the XForm variable type for a subscription option key. %% Return the XForm variable type for a subscription option key.
#xmlel{name = <<"value">>, attrs = [], #xmlel{name = <<"value">>, attrs = [],
children = [{xmlcdata, Value}]}. children = [{xmlcdata, Value}]}.
xfield_var(deliver) -> ?PUBSUB_DELIVER; xfield_var(deliver) -> ?PUBSUB_DELIVER;
%xfield_var(digest) -> ?PUBSUB_DIGEST; %xfield_var(digest) -> ?PUBSUB_DIGEST;
%xfield_var(digest_frequency) -> %xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
% ?PUBSUB_DIGEST_FREQUENCY;
%xfield_var(expire) -> ?PUBSUB_EXPIRE; %xfield_var(expire) -> ?PUBSUB_EXPIRE;
%xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY; %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES; xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
xfield_var(subscription_type) -> xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
?PUBSUB_SUBSCRIPTION_TYPE; xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_var(subscription_depth) ->
?PUBSUB_SUBSCRIPTION_DEPTH.
xfield_type(deliver) -> <<"boolean">>; xfield_type(deliver) -> <<"boolean">>;
%xfield_type(digest) -> <<"boolean">>; %xfield_type(digest) -> <<"boolean">>;
@ -339,34 +274,31 @@ xfield_type(deliver) -> <<"boolean">>;
%xfield_type(include_body) -> <<"boolean">>; %xfield_type(include_body) -> <<"boolean">>;
xfield_type(show_values) -> xfield_type(show_values) ->
{<<"list-multi">>, {<<"list-multi">>,
[{<<"away">>, ?SHOW_VALUE_AWAY_LABEL}, [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
{<<"chat">>, ?SHOW_VALUE_CHAT_LABEL}, {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
{<<"dnd">>, ?SHOW_VALUE_DND_LABEL}, {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
{<<"online">>, ?SHOW_VALUE_ONLINE_LABEL}, {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
{<<"xa">>, ?SHOW_VALUE_XA_LABEL}]}; {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
xfield_type(subscription_type) -> xfield_type(subscription_type) ->
{<<"list-single">>, {<<"list-single">>,
[{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL}, [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
{<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]}; {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
xfield_type(subscription_depth) -> xfield_type(subscription_depth) ->
{<<"list-single">>, {<<"list-single">>,
[{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL}, [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
{<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}. {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
%% Return the XForm variable label for a subscription option key. %% Return the XForm variable label for a subscription option key.
xfield_label(deliver) -> ?DELIVER_LABEL; xfield_label(deliver) -> ?DELIVER_LABEL;
%xfield_label(digest) -> ?DIGEST_LABEL; %xfield_label(digest) -> ?DIGEST_LABEL;
%xfield_label(digest_frequency) -> %xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
% ?DIGEST_FREQUENCY_LABEL;
%xfield_label(expire) -> ?EXPIRE_LABEL; %xfield_label(expire) -> ?EXPIRE_LABEL;
%xfield_label(include_body) -> ?INCLUDE_BODY_LABEL; %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
xfield_label(show_values) -> ?SHOW_VALUES_LABEL; xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
%% Return the XForm value for a subscription option key. %% Return the XForm value for a subscription option key.
%% Convert erlang booleans to XForms. %% Convert erlang booleans to XForms.
xfield_label(subscription_type) -> xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
?SUBSCRIPTION_TYPE_LABEL; xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
xfield_label(subscription_depth) ->
?SUBSCRIPTION_DEPTH_LABEL.
xfield_val(deliver, Val) -> [bool_to_xopt(Val)]; xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
%xfield_val(digest, Val) -> [bool_to_xopt(Val)]; %xfield_val(digest, Val) -> [bool_to_xopt(Val)];

View File

@ -813,7 +813,7 @@ pubsub(Config) ->
node = Node, node = Node,
jid = my_jid(Config)}}]}), jid = my_jid(Config)}}]}),
?recv2( ?recv2(
#message{sub_els = [#pubsub_event{}, #delay{}, #legacy_delay{}]}, #message{sub_els = [#pubsub_event{}, #delay{}]},
#iq{type = result, id = I1}), #iq{type = result, id = I1}),
%% Get subscriptions %% Get subscriptions
true = lists:member(?PUBSUB("retrieve-subscriptions"), Features), true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
@ -860,8 +860,7 @@ pubsub(Config) ->
#message{sub_els = [#pubsub_event{ #message{sub_els = [#pubsub_event{
items = [#pubsub_event_items{ items = [#pubsub_event_items{
node = Node, node = Node,
retract = [ItemID]}]}, retract = [ItemID]}]}]}),
#shim{headers = [{<<"Collection">>, Node}]}]}),
%% Unsubscribe from node "presence" %% Unsubscribe from node "presence"
#iq{type = result, sub_els = []} = #iq{type = result, sub_els = []} =
send_recv(Config, send_recv(Config,

View File

@ -94,7 +94,8 @@
{mod_offline, [{db_type, odbc}]}, {mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]}, {mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]}, {mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode}, {mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true}, {ignore_pep_from_offline, true},
{last_item_cache, false}, {last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]}, {plugins, ["flat", "hometree", "pep"]}]},
@ -114,7 +115,8 @@
{mod_offline, [{db_type, odbc}]}, {mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]}, {mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]}, {mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode}, {mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true}, {ignore_pep_from_offline, true},
{last_item_cache, false}, {last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]}, {plugins, ["flat", "hometree", "pep"]}]},
@ -133,7 +135,8 @@
{mod_offline, [{db_type, odbc}]}, {mod_offline, [{db_type, odbc}]},
{mod_privacy, [{db_type, odbc}]}, {mod_privacy, [{db_type, odbc}]},
{mod_private, [{db_type, odbc}]}, {mod_private, [{db_type, odbc}]},
{mod_pubsub_odbc, [{access_createnode, pubsub_createnode}, {mod_pubsub, [{db_type, odbc},
{access_createnode, pubsub_createnode},
{ignore_pep_from_offline, true}, {ignore_pep_from_offline, true},
{last_item_cache, false}, {last_item_cache, false},
{plugins, ["flat", "hometree", "pep"]}]}, {plugins, ["flat", "hometree", "pep"]}]},

View File

@ -26,7 +26,8 @@ host_config:
db_type: odbc db_type: odbc
mod_private: mod_private:
db_type: odbc db_type: odbc
mod_pubsub_odbc: mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode access_createnode: pubsub_createnode
ignore_pep_from_offline: true ignore_pep_from_offline: true
last_item_cache: false last_item_cache: false
@ -76,7 +77,8 @@ Welcome to this XMPP server."
db_type: odbc db_type: odbc
mod_private: mod_private:
db_type: odbc db_type: odbc
mod_pubsub_odbc: mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode access_createnode: pubsub_createnode
ignore_pep_from_offline: true ignore_pep_from_offline: true
last_item_cache: false last_item_cache: false
@ -132,7 +134,8 @@ Welcome to this XMPP server."
db_type: odbc db_type: odbc
mod_private: mod_private:
db_type: odbc db_type: odbc
mod_pubsub_odbc: mod_pubsub:
db_type: odbc
access_createnode: pubsub_createnode access_createnode: pubsub_createnode
ignore_pep_from_offline: true ignore_pep_from_offline: true
last_item_cache: false last_item_cache: false