]> granicus.if.org Git - ejabberd/commitdiff
PubSub improvements
authorChristophe Romain <christophe.romain@process-one.net>
Wed, 8 Apr 2015 15:12:05 +0000 (17:12 +0200)
committerChristophe Romain <christophe.romain@process-one.net>
Tue, 21 Apr 2015 13:24:16 +0000 (15:24 +0200)
This commit contains
- code cleanup
- use of db_type instead of old mod_pubsub_odbc
- some minor optimizations
- some minor bugfixes

31 files changed:
include/pubsub.hrl
src/gen_pubsub_node.erl
src/gen_pubsub_nodetree.erl
src/mod_pubsub.erl
src/mod_pubsub_odbc.erl [deleted file]
src/node.template [deleted file]
src/node_buddy.erl
src/node_club.erl
src/node_dag.erl
src/node_dispatch.erl
src/node_flat.erl
src/node_flat_odbc.erl
src/node_hometree.erl
src/node_hometree_odbc.erl
src/node_mb.erl
src/node_pep.erl
src/node_pep_odbc.erl
src/node_private.erl
src/node_public.erl
src/nodetree_dag.erl
src/nodetree_tree.erl
src/nodetree_tree_odbc.erl
src/nodetree_virtual.erl
src/pubsub_db_odbc.erl
src/pubsub_index.erl
src/pubsub_migrate.erl [new file with mode: 0644]
src/pubsub_subscription.erl
src/pubsub_subscription_odbc.erl
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ejabberd.cfg
test/ejabberd_SUITE_data/ejabberd.yml

index 3be41e3fdfac69371b4a77cfe5b643a6b9168aca..2f85bdbfab6de5aaec714b81f7e2fc1773f623d0 100644 (file)
@@ -27,8 +27,7 @@
 
 %% -------------------------------
 %% Pubsub constants
--define(ERR_EXTENDED(E, C),
-       mod_pubsub:extended_error(E, C)).
+-define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)).
 
 %% The actual limit can be configured with mod_pubsub's option max_items_node
 -define(MAXITEMS, 10).
@@ -40,7 +39,6 @@
 %% -------------------------------
 %% Pubsub types
 
-%% @type hostPubsub() = string().
 -type(hostPubsub() :: binary()).
 %% <p><tt>hostPubsub</tt> is the name of the PubSub service. For example, it can be
 %% <tt>"pubsub.localhost"</tt>.</p>
 -type(nodeId() :: binary()).
 %% @type nodeId() = binary().
 %% <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:
 %% ```<<"/home/localhost/user">>'''</p>
 
--type(nodeIdx() :: pos_integer()).
-%% @type nodeIdx() = integer().
+-type(nodeIdx() :: pos_integer() | binary()).
+%% @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() = string().
 -type(subId() :: binary()).
 %% @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() ::
     {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}
 %%    Option = atom()
      Value::binary() | [binary()] | boolean()
 }).
 
--type(subOptions() :: [SubOption::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(subOptions() :: [mod_pubsub:subOption(),...]).
 
-%% @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'
                      | 'owner'
 ).
 %% @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'
                       | 'subscribers'
                       | 'open'
 ).
-
+%% @type publishModel() = 'publishers' | 'subscribers' | 'open'
 
 -record(pubsub_index,
 {
     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,
 {
-    nodeid               ,%:: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()},
-    id                   ,%:: mod_pubsub:nodeIdx(),
-    parents = []         ,%:: [Parent_NodeId::mod_pubsub:nodeId()],
-    type    = <<"flat">> ,%:: binary(),
-    owners  = []         ,%:: [Owner::ljid(),...],
-    options = []          %:: mod_pubsub:nodeOptions()
+    nodeid              ,% :: {mod_pubsub:host(), mod_pubsub:nodeId()},
+    id                  ,% :: mod_pubsub:nodeIdx(),
+    parents = []        ,% :: [mod_pubsub:nodeId(),...],
+    type    = <<"flat">>,% :: binary(),
+    owners  = []        ,% :: [jlib:ljid(),...],
+    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,
 {
-    stateid                ,%:: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()},
-    items         = []     ,%:: [ItemId::mod_pubsub:itemId()],
-    affiliation   = 'none' ,%:: mod_pubsub:affiliation(),
-    subscriptions = []      %:: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
+    stateid               ,% :: {jlib:ljid(), mod_pubsub:nodeIdx()},
+    items         = []    ,% :: [mod_pubsub:itemId(),...],
+    affiliation   = 'none',% :: mod_pubsub:affiliation(),
+    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,
 {
-    itemid                            ,%:: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
-    creation     = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
-    modification = {unknown, unknown} ,%:: {erlang:timestamp(), ljid()},
-    payload      = []                  %:: mod_pubsub:payload()
+    itemid                           ,% :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
+    creation     = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
+    modification = {unknown, unknown},% :: {erlang:timestamp(), jlib:ljid()},
+    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,
 {
-    subid   ,%:: mod_pubsub:subId(),
-    options  %:: [] | mod_pubsub:subOptions()
+    subid        ,% :: mod_pubsub:subId(),
+    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,
 {
-    nodeid   ,%:: mod_pubsub:nodeIdx(),
-    itemid   ,%:: mod_pubsub:itemId(),
-    creation ,%:: {erlang:timestamp(), ljid()},
-    payload   %:: mod_pubsub:payload()
+    nodeid   ,% :: mod_pubsub:nodeIdx(),
+    itemid   ,% :: mod_pubsub:itemId(),
+    creation ,% :: {erlang:timestamp(), jlib:ljid()},
+    payload  :: mod_pubsub:payload()
 }).
index da8cd6a0e58bd1b7d307cdac3a08c15d100a76f3..c608dad81240b9dd2e04a442e21b03899a751231 100644 (file)
 
 -include("jlib.hrl").
 
--type(host() :: mod_pubsub:host()
-              | mod_pubsub_odbc:host()
-).
-
--type(nodeId() :: mod_pubsub:nodeId()
-                | 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(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()
-).
+-type(host() :: mod_pubsub:host()).
+-type(nodeId() :: mod_pubsub:nodeId()).
+-type(nodeIdx() :: mod_pubsub:nodeIdx()).
+-type(itemId() :: mod_pubsub:itemId()).
+-type(pubsubNode() :: mod_pubsub:pubsubNode()).
+-type(pubsubState() :: mod_pubsub:pubsubState()).
+-type(pubsubItem() :: mod_pubsub:pubsubItem()).
+-type(subOptions() :: mod_pubsub:subOptions()).
+-type(affiliation() :: mod_pubsub:affiliation()).
+-type(subscription() :: mod_pubsub:subscription()).
+-type(subId() :: mod_pubsub:subId()).
+-type(accessModel() :: mod_pubsub:accessModel()).
+-type(publishModel() :: mod_pubsub:publishModel()).
+-type(payload() :: mod_pubsub:payload()).
 
 -callback init(Host :: binary(),
-               ServerHost :: binary(),
-               Opts :: [any()]) -> atom().
+       ServerHost :: binary(),
+       Opts :: [any()]) -> atom().
 
 -callback terminate(Host :: host(),
-                    ServerHost :: binary()) -> atom().
+       ServerHost :: binary()) -> atom().
 
 -callback options() -> [{atom(), any()}].
 
 -callback features() -> [binary()].
 
 -callback create_node_permission(Host :: host(),
-                                 ServerHost :: binary(),
-                                 Node :: nodeId(),
-                                 ParentNode :: nodeId(),
-                                 Owner :: jid(), Access :: atom()) ->
+       ServerHost :: binary(),
+       Node :: nodeId(),
+       ParentNode :: nodeId(),
+       Owner :: jid(), Access :: atom()) ->
     {result, boolean()}.
 
 -callback create_node(NodeIdx :: nodeIdx(),
-                      Owner   :: jid()) ->
+       Owner   :: jid()) ->
     {result, {default, broadcast}}.
 
 -callback delete_node(Nodes :: [pubsubNode(),...]) ->
     {result,
-     {default, broadcast,
-      [{pubsubNode(),
-        [{ljid(), [{subscription(), subId()}]},...]},...]
-     }
-    }
+       {default, broadcast,
+           [{pubsubNode(),
+                   [{ljid(), [{subscription(), subId()}]},...]},...]
+           }
+       }
     |
     {result,
-     {[],
-      [{pubsubNode(),
-        [{ljid(), [{subscription(), subId()}]},...]},...]
-     }
-    }.
+       {[],
+           [{pubsubNode(),
+                   [{ljid(), [{subscription(), subId()}]},...]},...]
+           }
+       }.
 
 -callback purge_node(NodeIdx :: nodeIdx(),
-                     Owner :: jid()) ->
+       Owner :: jid()) ->
     {result, {default, broadcast}} |
     {error, xmlel()}.
 
 -callback subscribe_node(NodeIdx :: nodeIdx(),
-                         Sender :: jid(),
-                         Subscriber :: ljid(),
-                         AccessModel :: accessModel(),
-                         SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
-                         PresenceSubscription :: boolean(),
-                         RosterGroup :: boolean(),
-                         Options :: subOptions()) ->
+       Sender :: jid(),
+       Subscriber :: jid(),
+       AccessModel :: accessModel(),
+       SendLast :: 'never' | 'on_sub' | 'on_sub_and_presence',
+       PresenceSubscription :: boolean(),
+       RosterGroup :: boolean(),
+       Options :: subOptions()) ->
     {result, {default, subscribed, subId()}} |
     {result, {default, subscribed, subId(), send_last}} |
     {result, {default, pending, subId()}} |
     {error, xmlel()}.
 
 -callback unsubscribe_node(NodeIdx :: nodeIdx(),
-                           Sender :: jid(),
-                           Subscriber :: ljid(),
-                           SubId :: subId()) ->
+       Sender :: jid(),
+       Subscriber :: jid(),
+       SubId :: subId()) ->
     {result, default} |
     {error, xmlel()}.
 
 -callback publish_item(NodeId :: nodeIdx(),
-                       Publisher :: jid(),
-                       PublishModel :: publishModel(),
-                       Max_Items :: non_neg_integer(),
-                       ItemId :: <<>> | itemId(),
-                       Payload :: payload()) ->
+       Publisher :: jid(),
+       PublishModel :: publishModel(),
+       Max_Items :: non_neg_integer(),
+       ItemId :: <<>> | itemId(),
+       Payload :: payload()) ->
     {result, {default, broadcast, [itemId()]}} |
     {error, xmlel()}.
 
 -callback delete_item(NodeIdx :: nodeIdx(),
-                      Publisher :: jid(),
-                      PublishModel :: publishModel(),
-                      ItemId :: <<>> | itemId()) ->
+       Publisher :: jid(),
+       PublishModel :: publishModel(),
+       ItemId :: <<>> | itemId()) ->
     {result, {default, broadcast}} |
     {error, xmlel()}.
 
 -callback remove_extra_items(NodeIdx :: nodeIdx(),
-                             Max_Items :: unlimited | non_neg_integer(),
-                             ItemIds :: [itemId()]) ->
+       Max_Items :: unlimited | non_neg_integer(),
+       ItemIds :: [itemId()]) ->
     {result, {[itemId()], [itemId()]}
-    }.
+       }.
 
 -callback get_node_affiliations(NodeIdx :: nodeIdx()) ->
     {result, [{ljid(), affiliation()}]}.
 
 -callback get_entity_affiliations(Host :: host(),
-                                  Owner :: jid()) ->
+       Owner :: jid()) ->
     {result, [{pubsubNode(), affiliation()}]}.
 
 -callback get_affiliation(NodeIdx :: nodeIdx(),
-                          Owner :: jid()) ->
+       Owner :: jid()) ->
     {result, affiliation()}.
 
 -callback set_affiliation(NodeIdx :: nodeIdx(),
-                          Owner :: ljid(),
-                          Affiliation :: affiliation()) ->
+       Owner :: jid(),
+       Affiliation :: affiliation()) ->
     ok |
     {error, xmlel()}.
 
 -callback get_node_subscriptions(NodeIdx :: nodeIdx()) ->
     {result,
-     [{ljid(), subscription(), subId()}] |
-     [{ljid(), none},...]
-    }.
+       [{ljid(), subscription(), subId()}] |
+       [{ljid(), none},...]
+       }.
 
 -callback get_entity_subscriptions(Host :: host(),
-                                   Owner :: jid()) ->
+       Key :: jid()) ->
     {result, [{pubsubNode(), subscription(), subId(), ljid()}]
-    }.
+       }.
 
 -callback get_subscriptions(NodeIdx :: nodeIdx(),
-                            Owner :: ljid()) ->
+       Owner :: jid()) ->
     {result, [{subscription(), subId()}]}.
 
 -callback get_pending_nodes(Host :: host(),
-                            Owner :: jid()) ->
+       Owner :: jid()) ->
     {result, [nodeId()]}.
 
 -callback get_states(NodeIdx::nodeIdx()) ->
     {result, [pubsubState()]}.
 
 -callback get_state(NodeIdx :: nodeIdx(),
-                    JID :: ljid()) ->
+       Key :: ljid()) ->
     pubsubState().
 
 -callback set_state(State::pubsubState()) ->
     {error, xmlel()}.
 
 -callback get_items(NodeIdx :: nodeIdx(),
-                    JID :: jid(),
-                    AccessModel :: accessModel(),
-                    Presence_Subscription :: boolean(),
-                    RosterGroup :: boolean(),
-                    SubId :: subId()) ->
-    {result, [pubsubItem()]} |
+       JID :: jid(),
+       AccessModel :: accessModel(),
+       Presence_Subscription :: boolean(),
+       RosterGroup :: boolean(),
+       SubId :: subId(),
+       RSM :: none | rsm_in()) ->
+    {result, {[pubsubItem()], none | rsm_out()}} |
     {error, xmlel()}.
 
 -callback get_items(NodeIdx :: nodeIdx(),
-                    From :: jid()) ->
-    {result, [pubsubItem()]}.
+       From :: jid(),
+       RSM :: none | rsm_in()) ->
+    {result, {[pubsubItem()], none | rsm_out()}}.
 
 -callback get_item(NodeIdx :: nodeIdx(),
-                   ItemId :: itemId(),
-                   JID :: jid(),
-                   AccessModel :: accessModel(),
-                   PresenceSubscription :: boolean(),
-                   RosterGroup :: boolean(),
-                   SubId :: subId()) ->
+       ItemId :: itemId(),
+       JID :: jid(),
+       AccessModel :: accessModel(),
+       PresenceSubscription :: boolean(),
+       RosterGroup :: boolean(),
+       SubId :: subId()) ->
     {result, pubsubItem()} |
     {error, xmlel()}.
 
 -callback get_item(NodeIdx :: nodeIdx(),
-                   ItemId :: itemId()) ->
+       ItemId :: itemId()) ->
     {result, pubsubItem()} |
     {error, xmlel()}.
 
 %   | {error, _}.
 
 -callback get_item_name(Host :: host(),
-                        ServerHost :: binary(),
-                        Node :: nodeId()) ->
+       ServerHost :: binary(),
+       Node :: nodeId()) ->
     itemId().
 
 -callback node_to_path(Node :: nodeId()) ->
index 8cbe1d3dfb26a99cfb7f9413a4bc820ca5ec5557..ce6750dbac80f027df2d2f39dbe3bd4c6e72af03 100644 (file)
 
 -include("jlib.hrl").
 
--type(host() :: mod_pubsub:host()
-              | mod_pubsub_odbc:host()
-).
-
--type(nodeId() :: mod_pubsub:nodeId()
-                | 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()
-).
+-type(host() :: mod_pubsub:host()).
+-type(nodeId() :: mod_pubsub:nodeId()).
+-type(nodeIdx() :: mod_pubsub:nodeIdx()).
+-type(pubsubNode() :: mod_pubsub:pubsubNode()).
+-type(nodeOptions() :: mod_pubsub:nodeOptions()).
 
 -callback init(Host :: host(),
-               ServerHost :: binary(),
-               Opts :: [any()]) -> atom().
+       ServerHost :: binary(),
+       Opts :: [any()]) -> atom().
 
 -callback terminate(Host :: host(), ServerHost :: binary()) -> atom().
 
 -callback options() -> nodeOptions().
 
 -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(),
-                   NodeId :: nodeId(),
-                   From   :: jid()) ->
+       NodeId :: nodeId(),
+       From   :: jid()) ->
     pubsubNode() |
     {error, xmlel()}.
 
 -callback get_node(Host :: host(),
-                   NodeId :: nodeId()) ->
+       NodeId :: nodeId()) ->
     pubsubNode() |
     {error, xmlel()}.
 
     {error, xmlel()}.
 
 -callback get_nodes(Host :: host(),
-                   From :: jid())->
+       From :: jid())->
     [pubsubNode()].
 
 -callback get_nodes(Host :: host())->
     [pubsubNode()].
 
 -callback get_parentnodes(Host :: host(),
-                          NodeId :: nodeId(),
-                          From :: jid()) ->
+       NodeId :: nodeId(),
+       From :: jid()) ->
     [pubsubNode()] |
     {error, xmlel()}.
 
 -callback get_parentnodes_tree(Host :: host(),
-                               NodeId :: nodeId(),
-                               From :: jid()) ->
+       NodeId :: nodeId(),
+       From :: jid()) ->
     [{0, [pubsubNode(),...]}].
 
 -callback get_subnodes(Host :: host(),
-                       NodeId :: nodeId(),
-                       From :: ljid()) ->
+       NodeId :: nodeId(),
+       From :: jid()) ->
     [pubsubNode()].
 
 -callback get_subnodes_tree(Host :: host(),
-                            NodeId :: nodeId(),
-                            From :: ljid()) ->
+       NodeId :: nodeId(),
+       From :: jid()) ->
     [pubsubNode()].
 
 -callback create_node(Host :: host(),
-                      NodeId :: nodeId(),
-                      Type :: binary(),
-                      Owner :: jid(),
-                      Options :: nodeOptions(),
-                      Parents :: [nodeId()]) ->
+       NodeId :: nodeId(),
+       Type :: binary(),
+       Owner :: jid(),
+       Options :: nodeOptions(),
+       Parents :: [nodeId()]) ->
     {ok, NodeIdx::nodeIdx()} |
-    {error, xmlel()}.
+    {error, xmlel()} |
+    {error, {virtual, {host(), nodeId()}}}.
 
 -callback delete_node(Host :: host(),
-                      NodeId :: nodeId()) ->
+       NodeId :: nodeId()) ->
     [pubsubNode()].
index 579c4775714d2d8c76bf4420d06ff3c1e55eba74..8fbdfbb6fd7158024bc833e763998735ab306844 100644 (file)
@@ -4,13 +4,13 @@
 %%% 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
 %%% XEP-0060 section 12.18.
 
 -module(mod_pubsub).
-
--author('christophe.romain@process-one.net').
-
--version('1.13-0').
-
--behaviour(gen_server).
-
 -behaviour(gen_mod).
+-behaviour(gen_server).
+-author('christophe.romain@process-one.net').
+-version('1.13-1').
 
 -include("ejabberd.hrl").
 -include("logger.hrl").
-
 -include("adhoc.hrl").
-
 -include("jlib.hrl").
-
 -include("pubsub.hrl").
 
 -define(STDTREE, <<"tree">>).
-
 -define(STDNODE, <<"flat">>).
-
 -define(PEPNODE, <<"pep">>).
 
 %% exports for hooks
 -export([presence_probe/3, caps_update/3,
-        in_subscription/6, out_subscription/4,
-        on_user_offline/3, remove_user/2,
-        disco_local_identity/5, disco_local_features/5,
-        disco_local_items/5, disco_sm_identity/5,
-        disco_sm_features/5, disco_sm_items/5]).
+    in_subscription/6, out_subscription/4,
+    on_user_offline/3, remove_user/2,
+    disco_local_identity/5, disco_local_features/5,
+    disco_local_items/5, disco_sm_identity/5,
+    disco_sm_features/5, disco_sm_items/5]).
 
 %% exported iq handlers
 -export([iq_sm/3]).
 
 %% exports for console debug manual use
--export([create_node/5,
-        delete_node/3,
-        subscribe_node/5,
-        unsubscribe_node/5,
-        publish_item/6,
-        delete_item/4,
-        send_items/7,
-        get_items/2,
-        get_item/3,
-        get_cached_item/2,
-        broadcast_stanza/9,
-        get_configure/5,
-        set_configure/5,
-        tree_action/3,
-        node_action/4
-       ]).
+-export([create_node/5, create_node/7, delete_node/3,
+    subscribe_node/5, unsubscribe_node/5, publish_item/6,
+    delete_item/4, send_items/7, get_items/2, get_item/3,
+    get_cached_item/2, get_configure/5, set_configure/5,
+    tree_action/3, node_action/4, node_call/4]).
 
 %% general helpers for plugins
 -export([subscription_to_string/1, affiliation_to_string/1,
-        string_to_subscription/1, string_to_affiliation/1,
-        extended_error/2, extended_error/3,
-        rename_default_nodeplugin/0]).
+    string_to_subscription/1, string_to_affiliation/1,
+    extended_error/2, extended_error/3, service_jid/1,
+    tree/1, tree/2, plugin/2, config/3, host/1, serverhost/1]).
 
 %% API and gen_server callbacks
 -export([start_link/2, start/2, stop/1, init/1,
-        handle_call/3, handle_cast/2, handle_info/2,
-        terminate/2, code_change/3]).
+    handle_call/3, handle_cast/2, handle_info/2,
+    terminate/2, code_change/3]).
 
 %% calls for parallel sending of last items
 -export([send_loop/1]).
 
--export([export/1]).
-
 -define(PROCNAME, ejabberd_mod_pubsub).
-
 -define(LOOPNAME, ejabberd_mod_pubsub_loop).
 
 %%====================================================================
 %% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
 %% Description: Starts the server
 %%--------------------------------------------------------------------
--define(PLUGIN_PREFIX, <<"node_">>).
 
--define(TREE_PREFIX, <<"nodetree_">>).
-
-%
 -export_type([
-    host/0,
-    hostPubsub/0,
-    hostPEP/0,
-    %%
-    nodeIdx/0,
-    nodeId/0,
-    itemId/0,
-    subId/0,
-    payload/0,
-    %%
-    nodeOption/0,
-    nodeOptions/0,
-    subOption/0,
-    subOptions/0,
-    %%
-    affiliation/0,
-    subscription/0,
-    accessModel/0,
-    publishModel/0
-]).
+       host/0,
+       hostPubsub/0,
+       hostPEP/0,
+       %%
+       nodeIdx/0,
+       nodeId/0,
+       itemId/0,
+       subId/0,
+       payload/0,
+       %%
+       nodeOption/0,
+       nodeOptions/0,
+       subOption/0,
+       subOptions/0,
+       %%
+       affiliation/0,
+       subscription/0,
+       accessModel/0,
+       publishModel/0
+       ]).
 
 %% -type payload() defined here because the -type xmlel() is not accessible
 %% from pubsub.hrl
 -type(payload() :: [] | [xmlel(),...]).
 
 -export_type([
-    pubsubNode/0,
-    pubsubState/0,
-    pubsubItem/0,
-    pubsubSubscription/0,
-    pubsubLastItem/0
-]).
+       pubsubNode/0,
+       pubsubState/0,
+       pubsubItem/0,
+       pubsubSubscription/0,
+       pubsubLastItem/0
+       ]).
 
 -type(pubsubNode() ::
     #pubsub_node{
-        nodeid  :: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()},
-        id      :: mod_pubsub:nodeIdx(),
-        parents :: [Parent_NodeId::mod_pubsub:nodeId()],
-        type    :: binary(),
-        owners  :: [Owner::ljid(),...],
-        options :: mod_pubsub:nodeOptions()
-    }
-).
+       nodeid  :: {Host::mod_pubsub:host(), Node::mod_pubsub:nodeId()},
+       id      :: Nidx::mod_pubsub:nodeIdx(),
+       parents :: [Node::mod_pubsub:nodeId()],
+       type    :: Type::binary(),
+       owners  :: [Owner::ljid(),...],
+       options :: Opts::mod_pubsub:nodeOptions()
+       }
+    ).
 
 -type(pubsubState() ::
     #pubsub_state{
-        stateid       :: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()},
-        items         :: [ItemId::mod_pubsub:itemId()],
-        affiliation   :: mod_pubsub:affiliation(),
-        subscriptions :: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
-    }
-).
+       stateid       :: {Entity::ljid(), Nidx::mod_pubsub:nodeIdx()},
+       items         :: [ItemId::mod_pubsub:itemId()],
+       affiliation   :: Affs::mod_pubsub:affiliation(),
+       subscriptions :: [{Sub::mod_pubsub:subscription(), SubId::mod_pubsub:subId()}]
+       }
+    ).
 
 -type(pubsubItem() ::
     #pubsub_item{
-        itemid       :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
-        creation     :: {erlang:timestamp(), ljid()},
-        modification :: {erlang:timestamp(), ljid()},
-        payload      :: mod_pubsub:payload()
-    }
-).
+       itemid       :: {ItemId::mod_pubsub:itemId(), Nidx::mod_pubsub:nodeIdx()},
+       creation     :: {erlang:timestamp(), ljid()},
+       modification :: {erlang:timestamp(), ljid()},
+       payload      :: mod_pubsub:payload()
+       }
+    ).
 
 -type(pubsubSubscription() ::
     #pubsub_subscription{
-        subid   :: mod_pubsub:subId(),
-        options :: [] | mod_pubsub:subOptions()
-    }
-).
+       subid   :: SubId::mod_pubsub:subId(),
+       options :: [] | mod_pubsub:subOptions()
+       }
+    ).
 
 -type(pubsubLastItem() ::
     #pubsub_last_item{
-        nodeid   :: mod_pubsub:nodeIdx(),
-        itemid   :: mod_pubsub:itemId(),
-        creation :: {erlang:timestamp(), ljid()},
-        payload  :: mod_pubsub:payload()
-    }
-).
+       nodeid   :: mod_pubsub:nodeIdx(),
+       itemid   :: mod_pubsub:itemId(),
+       creation :: {erlang:timestamp(), ljid()},
+       payload  :: mod_pubsub:payload()
+       }
+    ).
 
 -record(state,
-{
-    server_host,
-    host,
-    access,
-    pep_mapping             = [],
-    ignore_pep_from_offline = true,
-    last_item_cache         = false,
-    max_items_node          = ?MAXITEMS,
-    nodetree                = ?STDTREE,
-    plugins                 = [?STDNODE]
-}).
+    {
+       server_host,
+       host,
+       access,
+       pep_mapping             = [],
+       ignore_pep_from_offline = true,
+       last_item_cache         = false,
+       max_items_node          = ?MAXITEMS,
+       nodetree                = <<"nodetree_", (?STDTREE)/binary>>,
+       plugins                 = [?STDNODE],
+       db_type
+       }).
 
 -type(state() ::
     #state{
-        server_host             :: binary(),
-        host                    :: mod_pubsub:hostPubsub(),
-        access                  :: atom(),
-        pep_mapping             :: [{binary(), binary()}],
-        ignore_pep_from_offline :: boolean(),
-        last_item_cache         :: boolean(),
-        max_items_node          :: non_neg_integer(),
-        nodetree                :: binary(),
-        plugins                 :: [binary(),...]
-    }
+       server_host             :: binary(),
+       host                    :: mod_pubsub:hostPubsub(),
+       access                  :: atom(),
+       pep_mapping             :: [{binary(), binary()}],
+       ignore_pep_from_offline :: boolean(),
+       last_item_cache         :: boolean(),
+       max_items_node          :: non_neg_integer(),
+       nodetree                :: binary(),
+       plugins                 :: [binary(),...],
+       db_type                 :: atom()
+       }
 
-).
+    ).
 
 
 start_link(Host, Opts) ->
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:start_link({local, Proc}, ?MODULE,
-                         [Host, Opts], []).
+    gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
 
 start(Host, Opts) ->
     Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
     ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
-                transient, 1000, worker, [?MODULE]},
+           transient, 1000, worker, [?MODULE]},
     supervisor:start_child(ejabberd_sup, ChildSpec).
 
 stop(Host) ->
@@ -259,125 +233,115 @@ stop(Host) ->
 
 %%--------------------------------------------------------------------
 %% Function: init(Args) -> {ok, State} |
-%%                      {ok, State, Timeout} |
-%%                      ignore        |
-%%                      {stop, Reason}
+%%                         {ok, State, Timeout} |
+%%                         ignore               |
+%%                         {stop, Reason}
 %% Description: Initiates the server
 %%--------------------------------------------------------------------
 -spec(init/1 ::
-(
-    _:: _)
-    -> {ok, state()}
-).
+    (
+       [binary() | [{_,_}],...])
+    -> {'ok',state()}
+    ).
 
 init([ServerHost, Opts]) ->
     ?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
     Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
     Access = gen_mod:get_opt(access_createnode, Opts,
-        fun(A) when is_atom(A) -> A end, all),
+           fun(A) when is_atom(A) -> A end, all),
     PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts,
-                                fun(A) when is_boolean(A) -> A end, true),
+           fun(A) when is_boolean(A) -> A end, true),
     IQDisc = gen_mod:get_opt(iqdisc, Opts,
-                fun(A) when is_atom(A) -> A end, one_queue),
+           fun(A) when is_atom(A) -> A end, one_queue),
     LastItemCache = gen_mod:get_opt(last_item_cache, Opts,
-                                   fun(A) when is_boolean(A) -> A end, false),
+           fun(A) when is_boolean(A) -> A end, false),
     MaxItemsNode = gen_mod:get_opt(max_items_node, Opts,
-                                  fun(A) when is_integer(A) andalso A >= 0 -> A end, ?MAXITEMS),
+           fun(A) when is_integer(A) andalso A >= 0 -> A end, ?MAXITEMS),
     pubsub_index:init(Host, ServerHost, Opts),
-    ets:new(gen_mod:get_module_proc(Host, config),
-           [set, named_table]),
-    ets:new(gen_mod:get_module_proc(ServerHost, config),
-           [set, named_table]),
-    {Plugins, NodeTree, PepMapping} = init_plugins(Host,
-                                                  ServerHost, Opts),
+    ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table]),
+    {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
     mnesia:create_table(pubsub_last_item,
-                       [{ram_copies, [node()]},
-                        {attributes, record_info(fields, pubsub_last_item)}]),
+       [{ram_copies, [node()]},
+           {attributes, record_info(fields, pubsub_last_item)}]),
     mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {nodetree, NodeTree}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {plugins, Plugins}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {last_item_cache, LastItemCache}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {max_items_node, MaxItemsNode}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {nodetree, NodeTree}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {plugins, Plugins}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {last_item_cache, LastItemCache}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {max_items_node, MaxItemsNode}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {pep_mapping, PepMapping}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {ignore_pep_from_offline, PepOffline}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {host, Host}),
-    ejabberd_hooks:add(sm_remove_connection_hook,
-                      ServerHost, ?MODULE, on_user_offline, 75),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {nodetree, NodeTree}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {plugins, Plugins}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {last_item_cache, LastItemCache}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {max_items_node, MaxItemsNode}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {pep_mapping, PepMapping}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {ignore_pep_from_offline, PepOffline}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {host, Host}),
+    ets:insert(gen_mod:get_module_proc(ServerHost, config), {access, Access}),
+    ejabberd_hooks:add(sm_remove_connection_hook, ServerHost,
+       ?MODULE, on_user_offline, 75),
     ejabberd_hooks:add(disco_local_identity, ServerHost,
-                      ?MODULE, disco_local_identity, 75),
+       ?MODULE, disco_local_identity, 75),
     ejabberd_hooks:add(disco_local_features, ServerHost,
-                      ?MODULE, disco_local_features, 75),
+       ?MODULE, disco_local_features, 75),
     ejabberd_hooks:add(disco_local_items, ServerHost,
-                      ?MODULE, disco_local_items, 75),
+       ?MODULE, disco_local_items, 75),
     ejabberd_hooks:add(presence_probe_hook, ServerHost,
-                      ?MODULE, presence_probe, 80),
+       ?MODULE, presence_probe, 80),
     ejabberd_hooks:add(roster_in_subscription, ServerHost,
-                      ?MODULE, in_subscription, 50),
+       ?MODULE, in_subscription, 50),
     ejabberd_hooks:add(roster_out_subscription, ServerHost,
-                      ?MODULE, out_subscription, 50),
-    ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
-                      remove_user, 50),
+       ?MODULE, out_subscription, 50),
+    ejabberd_hooks:add(remove_user, ServerHost,
+       ?MODULE, remove_user, 50),
     ejabberd_hooks:add(anonymous_purge_hook, ServerHost,
-                      ?MODULE, remove_user, 50),
+       ?MODULE, remove_user, 50),
     case lists:member(?PEPNODE, Plugins) of
-      true ->
-         ejabberd_hooks:add(caps_update, ServerHost, ?MODULE,
-                            caps_update, 80),
-         ejabberd_hooks:add(disco_sm_identity, ServerHost,
-                            ?MODULE, disco_sm_identity, 75),
-         ejabberd_hooks:add(disco_sm_features, ServerHost,
-                            ?MODULE, disco_sm_features, 75),
-         ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE,
-                            disco_sm_items, 75),
-         gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
-                                       ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
-         gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
-                                       ?NS_PUBSUB_OWNER, ?MODULE, iq_sm,
-                                       IQDisc);
-      false -> ok
+       true ->
+           ejabberd_hooks:add(caps_update, ServerHost,
+               ?MODULE, caps_update, 80),
+           ejabberd_hooks:add(disco_sm_identity, ServerHost,
+               ?MODULE, disco_sm_identity, 75),
+           ejabberd_hooks:add(disco_sm_features, ServerHost,
+               ?MODULE, disco_sm_features, 75),
+           ejabberd_hooks:add(disco_sm_items, ServerHost,
+               ?MODULE, disco_sm_items, 75),
+           gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
+               ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
+           gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
+               ?NS_PUBSUB_OWNER, ?MODULE, iq_sm, IQDisc);
+       false ->
+           ok
     end,
     ejabberd_router:register_route(Host),
-    update_node_database(Host, ServerHost),
-    update_state_database(Host, ServerHost),
-    update_item_database_binary(),
-    put(server_host, ServerHost),
+    pubsub_migrate:update_node_database(Host, ServerHost),
+    pubsub_migrate:update_state_database(Host, ServerHost),
+    pubsub_migrate:update_lastitem_database(Host, ServerHost),
     init_nodes(Host, ServerHost, NodeTree, Plugins),
-    State = #state{host = Host, server_host = ServerHost,
-                  access = Access, pep_mapping = PepMapping,
-                  ignore_pep_from_offline = PepOffline,
-                  last_item_cache = LastItemCache,
-                  max_items_node = MaxItemsNode, nodetree = NodeTree,
-                  plugins = Plugins},
-    init_send_loop(ServerHost, State),
+    {_, State} = init_send_loop(ServerHost),
     {ok, State}.
 
-init_send_loop(ServerHost, State) ->
+init_send_loop(ServerHost) ->
+    NodeTree = config(ServerHost, nodetree),
+    Plugins = config(ServerHost, plugins),
+    LastItemCache = config(ServerHost, last_item_cache),
+    MaxItemsNode = config(ServerHost, max_items_node),
+    PepMapping = config(ServerHost, pep_mapping),
+    PepOffline = config(ServerHost, ignore_pep_from_offline),
+    Host = config(ServerHost, host),
+    Access = config(ServerHost, access),
+    DBType = gen_mod:db_type(ServerHost, ?MODULE),
+    State = #state{host = Host, server_host = ServerHost,
+           access = Access, pep_mapping = PepMapping,
+           ignore_pep_from_offline = PepOffline,
+           last_item_cache = LastItemCache,
+           max_items_node = MaxItemsNode, nodetree = NodeTree,
+           plugins = Plugins, db_type = DBType},
     Proc = gen_mod:get_module_proc(ServerHost, ?LOOPNAME),
-    SendLoop = spawn(?MODULE, send_loop, [State]),
-    register(Proc, SendLoop),
-    SendLoop.
-
-%% @spec (Host, ServerHost, Opts) -> Plugins
-%%      Host = mod_pubsub:host()   Opts = [{Key,Value}]
-%%      ServerHost = host()
-%%      Key = atom()
-%%      Value = term()
-%%      Plugins = [Plugin::string()]
+    Pid = case whereis(Proc) of
+       undefined ->
+           SendLoop = spawn(?MODULE, send_loop, [State]),
+           register(Proc, SendLoop),
+           SendLoop;
+       Loop ->
+           Loop
+    end,
+    {Pid, State}.
+
 %% @doc Call the init/1 function for each plugin declared in the config file.
 %% The default plugin module is implicit.
 %% <p>The Erlang code for the plugin is located in a module called
@@ -386,594 +350,129 @@ init_send_loop(ServerHost, State) ->
 %% and sorted to ensure that each module is initialized only once.</p>
 %% <p>See {@link node_hometree:init/1} for an example implementation.</p>
 init_plugins(Host, ServerHost, Opts) ->
-    TreePlugin =
-       jlib:binary_to_atom(<<(?TREE_PREFIX)/binary,
-                               (gen_mod:get_opt(nodetree, Opts, fun(A) when is_binary(A) -> A end,
-                                                ?STDTREE))/binary>>),
+    TreePlugin = tree(Host, gen_mod:get_opt(nodetree, Opts,
+               fun(A) when is_binary(A) -> A end,
+               ?STDTREE)),
     ?DEBUG("** tree plugin is ~p", [TreePlugin]),
     TreePlugin:init(Host, ServerHost, Opts),
     Plugins = gen_mod:get_opt(plugins, Opts,
-        fun(A) when is_list(A) -> A end, [?STDNODE]),
+           fun(A) when is_list(A) -> A end, [?STDNODE]),
     PepMapping = gen_mod:get_opt(pep_mapping, Opts,
-        fun(A) when is_list(A) -> A end, []),
+           fun(A) when is_list(A) -> A end, []),
     ?DEBUG("** PEP Mapping : ~p~n", [PepMapping]),
-    PluginsOK = lists:foldl(fun (Name, Acc) ->
-                                   Plugin =
-                                       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                                               Name/binary>>),
-                                   case catch apply(Plugin, init,
-                                                    [Host, ServerHost, Opts])
-                                       of
-                                     {'EXIT', _Error} -> Acc;
-                                     _ ->
-                                         ?DEBUG("** init ~s plugin", [Name]),
-                                         [Name | Acc]
-                                   end
-                           end,
-                           [], Plugins),
+    PluginsOK = lists:foldl(
+           fun (Name, Acc) ->
+                   Plugin = plugin(Host, Name),
+                   case catch apply(Plugin, init, [Host, ServerHost, Opts]) of
+                       {'EXIT', _Error} ->
+                           Acc;
+                       _ ->
+                           ?DEBUG("** init ~s plugin", [Name]),
+                           [Name | Acc]
+                   end
+           end,
+           [], Plugins),
     {lists:reverse(PluginsOK), TreePlugin, PepMapping}.
 
-terminate_plugins(Host, ServerHost, Plugins,
-                 TreePlugin) ->
-    lists:foreach(fun (Name) ->
-                         ?DEBUG("** terminate ~s plugin", [Name]),
-                         Plugin =
-                             jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                                     Name/binary>>),
-                         Plugin:terminate(Host, ServerHost)
-                 end,
-                 Plugins),
+terminate_plugins(Host, ServerHost, Plugins, TreePlugin) ->
+    lists:foreach(
+       fun (Name) ->
+               ?DEBUG("** terminate ~s plugin", [Name]),
+               Plugin = plugin(Host, Name),
+               Plugin:terminate(Host, ServerHost)
+       end,
+       Plugins),
     TreePlugin:terminate(Host, ServerHost),
     ok.
 
 init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
     case lists:member(<<"hometree">>, Plugins) of
-      true ->
+       true ->
            create_node(Host, ServerHost, <<"/home">>, service_jid(Host), <<"hometree">>),
-           create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, service_jid(Host),
-             <<"hometree">>);
-      false -> ok
-    end.
-
-
-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} = node_call(Type, path_to_node, [N]),
-                                                          BN1
-                                                  end,
-                                             BP = case [case P of
-                                                            Binary2 when is_binary(Binary2) -> P;
-                                                            _ -> element(2, node_call(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
+           create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, service_jid(Host), <<"hometree">>);
+       false -> ok
     end.
 
 send_loop(State) ->
     receive
-      {presence, JID, Pid} ->
-         Host = State#state.host,
-         ServerHost = State#state.server_host,
-         LJID = jlib:jid_tolower(JID),
-         BJID = jlib:jid_remove_resource(LJID),
-         lists:foreach(fun (PType) ->
-                               {result, Subscriptions} = node_action(Host,
-                                                                     PType,
-                                                                     get_entity_subscriptions,
-                                                                     [Host,
-                                                                      JID]),
-                               lists:foreach(fun ({Node, subscribed, _,
-                                                   SubJID}) ->
-                                                     if (SubJID == LJID) or
-                                                          (SubJID == BJID) ->
-                                                            #pubsub_node{nodeid
-                                                                             =
-                                                                             {H,
-                                                                              N},
-                                                                         type =
-                                                                             Type,
-                                                                         id =
-                                                                             NodeId,
-                                                                         options
-                                                                             =
-                                                                             Options} =
-                                                                Node,
-                                                            case
-                                                              get_option(Options,
-                                                                         send_last_published_item)
-                                                                of
-                                                              on_sub_and_presence ->
-                                                                  send_items(H,
-                                                                             N,
-                                                                             NodeId,
-                                                                             Type,
-                                                                             Options,
-                                                                             LJID,
-                                                                             last);
-                                                              _ -> ok
-                                                            end;
-                                                        true ->
-                                                            % resource not concerned about that subscription
-                                                            ok
-                                                     end;
-                                                 (_) -> ok
-                                             end,
-                                             lists:usort(Subscriptions))
-                       end,
-                       State#state.plugins),
-         if not State#state.ignore_pep_from_offline ->
-                {User, Server, Resource} = jlib:jid_tolower(JID),
-                case catch ejabberd_c2s:get_subscribed(Pid) of
-                  Contacts when is_list(Contacts) ->
-                      lists:foreach(fun ({U, S, R}) ->
-                                            case S of
-                                              ServerHost ->  %% local contacts
-                                                  case user_resources(U, S) of
-                                                    [] -> %% offline
-                                                        PeerJID =
-                                                            jlib:make_jid(U, S,
-                                                                          R),
-                                                        self() !
-                                                          {presence, User,
-                                                           Server, [Resource],
-                                                           PeerJID};
-                                                    _ -> %% online
-                                                        % this is already handled by presence probe
-                                                        ok
-                                                  end;
-                                              _ ->  %% remote contacts
-                                                  % we can not do anything in any cases
-                                                  ok
-                                            end
-                                    end,
-                                    Contacts);
-                  _ -> ok
-                end;
-            true -> ok
-         end,
-         send_loop(State);
-      {presence, User, Server, Resources, JID} ->
-         spawn(fun () ->
+       {presence, JID, Pid} ->
+           Host = State#state.host,
+           ServerHost = State#state.server_host,
+           DBType = State#state.db_type,
+           LJID = jlib:jid_tolower(JID),
+           BJID = jlib:jid_remove_resource(LJID),
+           lists:foreach(
+               fun(PType) ->
+                       Subs = get_subscriptions_for_send_last(Host, PType, DBType, JID, LJID, BJID),
+                       lists:foreach(
+                           fun({NodeRec, _, _, SubJID}) ->
+                                   {_, Node} = NodeRec#pubsub_node.nodeid,
+                                   Nidx = NodeRec#pubsub_node.id,
+                                   Options = NodeRec#pubsub_node.options,
+                                   send_items(Host, Node, Nidx, PType, Options, SubJID, last)
+                           end,
+                           lists:usort(Subs))
+               end,
+               State#state.plugins),
+           if not State#state.ignore_pep_from_offline ->
+                   {User, Server, Resource} = LJID,
+                   case catch ejabberd_c2s:get_subscribed(Pid) of
+                       Contacts when is_list(Contacts) ->
+                           lists:foreach(
+                               fun({U, S, R}) when S == ServerHost ->
+                                       case user_resources(U, S) of
+                                           [] -> %% offline
+                                               PeerJID = jlib:make_jid(U, S, R),
+                                               self() !  {presence, User, Server, [Resource], PeerJID};
+                                           _ -> %% online
+                                               % this is already handled by presence probe
+                                               ok
+                                       end;
+                                   (_) ->
+                                       % we can not do anything in any cases
+                                       ok
+                               end,
+                               Contacts);
+                       _ ->
+                           ok
+                   end;
+               true ->
+                   ok
+           end,
+           send_loop(State);
+       {presence, User, Server, Resources, JID} ->
+           spawn(fun() ->
                        Host = State#state.host,
                        Owner = jlib:jid_remove_resource(jlib:jid_tolower(JID)),
-                       lists:foreach(fun (#pubsub_node{nodeid = {_, Node},
-                                                       type = Type,
-                                                       id = NodeId,
-                                                       options = Options}) ->
-                                             case get_option(Options,
-                                                             send_last_published_item)
-                                                 of
-                                               on_sub_and_presence ->
-                                                   lists:foreach(fun
-                                                                   (Resource) ->
-                                                                       LJID =
-                                                                           {User,
-                                                                            Server,
-                                                                            Resource},
-                                                                       Subscribed =
-                                                                           case
-                                                                             get_option(Options,
-                                                                                        access_model)
-                                                                               of
-                                                                             open ->
-                                                                                 true;
-                                                                             presence ->
-                                                                                 true;
-                                                                             whitelist ->
-                                                                                 false; % subscribers are added manually
-                                                                             authorize ->
-                                                                                 false; % likewise
-                                                                             roster ->
-                                                                                 Grps =
-                                                                                     get_option(Options,
-                                                                                                roster_groups_allowed,
-                                                                                                []),
-                                                                                 {OU,
-                                                                                  OS,
-                                                                                  _} =
-                                                                                     Owner,
-                                                                                 element(2,
-                                                                                         get_roster_info(OU,
-                                                                                                         OS,
-                                                                                                         LJID,
-                                                                                                         Grps))
-                                                                           end,
-                                                                       if
-                                                                         Subscribed ->
-                                                                             send_items(Owner,
-                                                                                        Node,
-                                                                                        NodeId,
-                                                                                        Type,
-                                                                                        Options,
-                                                                                        LJID,
-                                                                                        last);
-                                                                         true ->
-                                                                             ok
-                                                                       end
-                                                                 end,
-                                                                 Resources);
-                                               _ -> ok
-                                             end
-                                     end,
-                                     tree_action(Host, get_nodes,
-                                                 [Owner, JID]))
+                       lists:foreach(fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) ->
+                                   case match_option(Options, send_last_published_item, on_sub_and_presence) of
+                                       true ->
+                                           lists:foreach(fun(Resource) ->
+                                                       LJID = {User, Server, Resource},
+                                                       Subscribed = case get_option(Options, access_model) of
+                                                           open -> true;
+                                                           presence -> true;
+                                                           whitelist -> false; % subscribers are added manually
+                                                           authorize -> false; % likewise
+                                                           roster ->
+                                                               Grps = get_option(Options, roster_groups_allowed, []),
+                                                               {OU, OS, _} = Owner,
+                                                               element(2, get_roster_info(OU, OS, LJID, Grps))
+                                                       end,
+                                                       if Subscribed -> send_items(Owner, Node, Nidx, Type, Options, LJID, last);
+                                                           true -> ok
+                                                       end
+                                               end,
+                                               Resources);
+                                       _ ->
+                                           ok
+                                   end
+                           end,
+                           tree_action(Host, get_nodes, [Owner, JID]))
                end),
-         send_loop(State);
-      stop -> ok
+           send_loop(State);
+       stop ->
+           ok
     end.
 
 %% -------
@@ -981,49 +480,43 @@ send_loop(State) ->
 %%
 
 -spec(disco_local_identity/5 ::
-(
-  Acc    :: [xmlel()],
-  _From  :: jid(),
-  To     :: jid(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  Lang   :: binary())
+    (
+       Acc    :: [xmlel()],
+       _From  :: jid(),
+       To     :: jid(),
+       Node   :: <<>> | mod_pubsub:nodeId(),
+       Lang   :: binary())
     -> [xmlel()]
-).
+    ).
 disco_local_identity(Acc, _From, To, <<>>, _Lang) ->
     case lists:member(?PEPNODE, plugins(To#jid.lserver)) of
-      true ->
-         [#xmlel{name = <<"identity">>,
-                 attrs =
-                     [{<<"category">>, <<"pubsub">>},
-                      {<<"type">>, <<"pep">>}],
-                 children = []}
-          | Acc];
-      false -> Acc
+       true ->
+           [#xmlel{name = <<"identity">>,
+                   attrs = [{<<"category">>, <<"pubsub">>},
+                       {<<"type">>, <<"pep">>}]}
+               | Acc];
+       false ->
+           Acc
     end;
 disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
     Acc.
 
 -spec(disco_local_features/5 ::
-(
-  Acc    :: [xmlel()],
-  _From  :: jid(),
-  To     :: jid(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  Lang   :: binary())
+    (
+       Acc    :: [xmlel()],
+       _From  :: jid(),
+       To     :: jid(),
+       Node   :: <<>> | mod_pubsub:nodeId(),
+       Lang   :: binary())
     -> [binary(),...]
-).
+    ).
 disco_local_features(Acc, _From, To, <<>>, _Lang) ->
     Host = To#jid.lserver,
     Feats = case Acc of
-             {result, I} -> I;
-             _ -> []
-           end,
-    {result,
-     Feats ++
-       lists:map(fun (Feature) ->
-                        <<(?NS_PUBSUB)/binary, "#", Feature/binary>>
-                end,
-                features(Host, <<>>))};
+       {result, I} -> I;
+       _ -> []
+    end,
+    {result, Feats ++ [feature(F) || F <- features(Host, <<>>)]};
 disco_local_features(Acc, _From, _To, _Node, _Lang) ->
     Acc.
 
@@ -1033,179 +526,156 @@ disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc.
 %disco_sm_identity(Acc, From, To, Node, Lang)
 %    when is_binary(Node) ->
 %    disco_sm_identity(Acc, From, To, iolist_to_binary(Node),
-%                    Lang);
+%                      Lang);
 -spec(disco_sm_identity/5 ::
-(
-  Acc  :: empty | [xmlel()],
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
+    (
+       Acc  :: empty | [xmlel()],
+       From :: jid(),
+       To   :: jid(),
+       Node :: mod_pubsub:nodeId(),
+       Lang :: binary())
     -> [xmlel()]
-).
+    ).
 disco_sm_identity(empty, From, To, Node, Lang) ->
     disco_sm_identity([], From, To, Node, Lang);
 disco_sm_identity(Acc, From, To, Node, _Lang) ->
     disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)
-      ++ Acc.
+    ++ Acc.
 
 disco_identity(_Host, <<>>, _From) ->
     [#xmlel{name = <<"identity">>,
-           attrs =
-               [{<<"category">>, <<"pubsub">>},
-                {<<"type">>, <<"pep">>}],
-           children = []}];
+           attrs = [{<<"category">>, <<"pubsub">>},
+               {<<"type">>, <<"pep">>}]}];
 disco_identity(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options, owners = Owners}) ->
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          {result,
-                           [#xmlel{name = <<"identity">>,
-                                   attrs =
-                                       [{<<"category">>, <<"pubsub">>},
-                                        {<<"type">>, <<"pep">>}],
-                                   children = []},
-                            #xmlel{name = <<"identity">>,
-                                   attrs =
-                                       [{<<"category">>, <<"pubsub">>},
-                                        {<<"type">>, <<"leaf">>}
-                                        | case get_option(Options, title) of
-                                            false -> [];
-                                            [Title] -> [{<<"name">>, Title}]
-                                          end],
-                                   children = []}]};
-                      _ -> {result, []}
-                    end
-            end,
+    Action = fun (#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) ->
+           Owners = node_owners_call(Host, Type, Nidx, O),
+           case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+               {result, _} ->
+                   {result, [#xmlel{name = <<"identity">>,
+                               attrs = [{<<"category">>, <<"pubsub">>},
+                                   {<<"type">>, <<"pep">>}]},
+                           #xmlel{name = <<"identity">>,
+                               attrs = [{<<"category">>, <<"pubsub">>},
+                                   {<<"type">>, <<"leaf">>}
+                                   | case get_option(Options, title) of
+                                       false -> [];
+                                       [Title] -> [{<<"name">>, Title}]
+                                   end]}]};
+               _ ->
+                   {result, []}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
+       {result, {_, Result}} -> Result;
+       _ -> []
     end.
 
 -spec(disco_sm_features/5 ::
-(
-  Acc  :: empty | {result, Features::[Feature::binary()]},
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
+    (
+       Acc  :: empty | {result, Features::[Feature::binary()]},
+       From :: jid(),
+       To   :: jid(),
+       Node :: mod_pubsub:nodeId(),
+       Lang :: binary())
     -> {result, Features::[Feature::binary()]}
-).
+    ).
 %disco_sm_features(Acc, From, To, Node, Lang)
 %    when is_binary(Node) ->
 %    disco_sm_features(Acc, From, To, iolist_to_binary(Node),
-%                    Lang);
+%                      Lang);
 disco_sm_features(empty, From, To, Node, Lang) ->
     disco_sm_features({result, []}, From, To, Node, Lang);
 disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) ->
     {result,
-     OtherFeatures ++
-       disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)};
+       OtherFeatures ++
+       disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)};
 disco_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc.
 
-disco_features(_Host, <<>>, _From) ->
-    [?NS_PUBSUB | [<<(?NS_PUBSUB)/binary, "#", Feature/binary>>
-        || Feature <- features(<<"pep">>)]];
+disco_features(Host, <<>>, _From) ->
+    [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, <<"pep">>)]];
 disco_features(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options, owners = Owners}) ->
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          {result,
-                           [?NS_PUBSUB | [<<(?NS_PUBSUB)/binary, "#",
-                                            Feature/binary>>
-                                          || Feature <- features(<<"pep">>)]]};
-                      _ -> {result, []}
-                    end
-            end,
+    Action = fun (#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) ->
+           Owners = node_owners_call(Host, Type, Nidx, O),
+           case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+               {result, _} -> {result, [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, <<"pep">>)]]};
+               _ -> {result, []}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
+       {result, {_, Result}} -> Result;
+       _ -> []
     end.
 
 -spec(disco_sm_items/5 ::
-(
-  Acc  :: empty | {result, [xmlel()]},
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
+    (
+       Acc  :: empty | {result, [xmlel()]},
+       From :: jid(),
+       To   :: jid(),
+       Node :: mod_pubsub:nodeId(),
+       Lang :: binary())
     -> {result, [xmlel()]}
-).
+    ).
 %disco_sm_items(Acc, From, To, Node, Lang)
 %    when is_binary(Node) ->
 %    disco_sm_items(Acc, From, To, iolist_to_binary(Node),
-%                 Lang);
+%                   Lang);
 disco_sm_items(empty, From, To, Node, Lang) ->
     disco_sm_items({result, []}, From, To, Node, Lang);
 disco_sm_items({result, OtherItems}, From, To, Node, _Lang) ->
-    {result,
-     lists:usort(OtherItems ++
-               disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))};
+    {result, lists:usort(OtherItems ++
+           disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))};
 disco_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc.
 
 -spec(disco_items/3 ::
-(
-  Host :: mod_pubsub:host(),
-  Node :: mod_pubsub:nodeId(),
-  From :: jid())
+    (
+       Host :: mod_pubsub:host(),
+       Node :: mod_pubsub:nodeId(),
+       From :: jid())
     -> [xmlel()]
-).
+    ).
 disco_items(Host, <<>>, From) ->
-    Action = fun (#pubsub_node{nodeid = {_, NodeID},
-                              options = Options, type = Type, id = Idx,
-                              owners = Owners},
-                 Acc) ->
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          [#xmlel{name = <<"item">>,
-                                  attrs =
-                                      [{<<"node">>, (NodeID)},
-                                       {<<"jid">>,
-                                        case Host of
-                                          {_, _, _} ->
-                                              jlib:jid_to_string(Host);
-                                          _Host -> Host
-                                        end}
-                                       | case get_option(Options, title) of
-                                           false -> [];
-                                           [Title] -> [{<<"name">>, Title}]
-                                         end],
-                                  children = []}
-                           | Acc];
-                      _ -> Acc
-                    end
-            end,
-    case transaction(Host, Action, sync_dirty) of
-      {result, Items} -> Items;
-      _ -> []
+    Action = fun (#pubsub_node{nodeid = {_, Node},
+                       options = Options, type = Type, id = Nidx, owners = O},
+                   Acc) ->
+           Owners = node_owners_call(Host, Type, Nidx, O),
+           case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+               {result, _} ->
+                   [#xmlel{name = <<"item">>,
+                           attrs = [{<<"node">>, (Node)},
+                               {<<"jid">>, jlib:jid_to_string(Host)}
+                               | case get_option(Options, title) of
+                                   false -> [];
+                                   [Title] -> [{<<"name">>, Title}]
+                               end]}
+                       | Acc];
+               _ ->
+                   Acc
+           end
+    end,
+    NodeBloc = fun() ->
+           {result,
+               lists:foldl(Action, [], tree_call(Host, get_nodes, [Host]))}
+    end,
+    case transaction(Host, NodeBloc, sync_dirty) of
+       {result, Items} -> Items;
+       _ -> []
     end;
 disco_items(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options, owners = Owners}) ->
-                    case get_allowed_items_call(Host, Idx, From, Type,
-                                                Options, Owners)
-                        of
-                      {result, Items} ->
-                          {result,
-                           [#xmlel{name = <<"item">>,
-                                   attrs =
-                                       [{<<"jid">>,
-                                         case Host of
-                                           {_, _, _} ->
-                                               jlib:jid_to_string(Host);
-                                           _Host -> Host
-                                         end},
-                                        {<<"name">>, ItemID}],
-                                   children = []}
-                            || #pubsub_item{itemid = {ItemID, _}} <- Items]};
-                      _ -> {result, []}
-                    end
-            end,
+    Action = fun (#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) ->
+           Owners = node_owners_call(Host, Type, Nidx, O),
+           case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) of
+               {result, Items} ->
+                   {result, [#xmlel{name = <<"item">>,
+                               attrs = [{<<"jid">>, jlib:jid_to_string(Host)},
+                                   {<<"name">>, ItemId}]}
+                           || #pubsub_item{itemid = {ItemId, _}} <- Items]};
+               _ ->
+                   {result, []}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
+       {result, {_, Result}} -> Result;
+       _ -> []
     end.
 
 %% -------
@@ -1233,26 +703,10 @@ presence_probe(_Host, _JID, _Pid) ->
     ok.
 
 presence(ServerHost, Presence) ->
-    SendLoop = case
-                whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME))
-                  of
-                undefined ->
-                    Host = host(ServerHost),
-                    Plugins = plugins(Host),
-                    PepOffline = case catch
-                                        ets:lookup(gen_mod:get_module_proc(ServerHost,
-                                                                           config),
-                                                   ignore_pep_from_offline)
-                                     of
-                                   [{ignore_pep_from_offline, PO}] -> PO;
-                                   _ -> true
-                                 end,
-                    State = #state{host = Host, server_host = ServerHost,
-                                   ignore_pep_from_offline = PepOffline,
-                                   plugins = Plugins},
-                    init_send_loop(ServerHost, State);
-                Pid -> Pid
-              end,
+    {SendLoop, _} = case whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME)) of
+       undefined -> init_send_loop(ServerHost);
+       Pid -> {Pid, undefined}
+    end,
     SendLoop ! Presence.
 
 %% -------
@@ -1260,73 +714,57 @@ presence(ServerHost, Presence) ->
 %%
 
 out_subscription(User, Server, JID, subscribed) ->
-    Owner = jlib:make_jid(User, Server, <<"">>),
+    Owner = jlib:make_jid(User, Server, <<>>),
     {PUser, PServer, PResource} = jlib:jid_tolower(JID),
     PResources = case PResource of
-                  <<>> -> user_resources(PUser, PServer);
-                  _ -> [PResource]
-                end,
-    presence(Server,
-            {presence, PUser, PServer, PResources, Owner}),
+       <<>> -> user_resources(PUser, PServer);
+       _ -> [PResource]
+    end,
+    presence(Server, {presence, PUser, PServer, PResources, Owner}),
     true;
-out_subscription(_, _, _, _) -> true.
+out_subscription(_, _, _, _) ->
+    true.
 
-in_subscription(_, User, Server, Owner, unsubscribed,
-               _) ->
-    unsubscribe_user(jlib:make_jid(User, Server, <<"">>),
-                    Owner),
+in_subscription(_, User, Server, Owner, unsubscribed, _) ->
+    unsubscribe_user(jlib:make_jid(User, Server, <<>>), Owner),
     true;
-in_subscription(_, _, _, _, _, _) -> true.
+in_subscription(_, _, _, _, _, _) ->
+    true.
 
 unsubscribe_user(Entity, Owner) ->
     BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
     Host = host(element(2, BJID)),
     spawn(fun () ->
-                 lists:foreach(fun (PType) ->
-                                       {result, Subscriptions} =
-                                           node_action(Host, PType,
-                                                       get_entity_subscriptions,
-                                                       [Host, Entity]),
-                                       lists:foreach(fun ({#pubsub_node{options
-                                                                            =
-                                                                            Options,
-                                                                        owners
-                                                                            =
-                                                                            Owners,
-                                                                        id =
-                                                                            NodeId},
-                                                           subscribed, _,
-                                                           JID}) ->
-                                                             case
-                                                               get_option(Options,
-                                                                          access_model)
-                                                                 of
-                                                               presence ->
-                                                                   case
-                                                                     lists:member(BJID,
-                                                                                  Owners)
-                                                                       of
-                                                                     true ->
-                                                                         node_action(Host,
-                                                                                     PType,
-                                                                                     unsubscribe_node,
-                                                                                     [NodeId,
-                                                                                      Entity,
-                                                                                      JID,
-                                                                                      all]);
-                                                                     false ->
-                                                                         {result,
-                                                                          ok}
-                                                                   end;
-                                                               _ ->
-                                                                   {result, ok}
-                                                             end;
-                                                         (_) -> ok
-                                                     end,
-                                                     Subscriptions)
+               lists:foreach(fun (PType) ->
+                           {result, Subs} = node_action(Host, PType,
+                                   get_entity_subscriptions,
+                                   [Host, Entity]),
+                           lists:foreach(fun
+                                   ({#pubsub_node{options = Options,
+                                                       owners = O,
+                                                       id = Nidx},
+                                                   subscribed, _, JID}) ->
+                                       case match_option(Options, access_model, presence) of
+                                           true ->
+                                               Owners = node_owners_action(Host, PType, Nidx, O),
+                                               case lists:member(BJID, Owners) of
+                                                   true ->
+                                                       node_action(Host, PType,
+                                                           unsubscribe_node,
+                                                           [Nidx, Entity, JID, all]);
+                                                   false ->
+                                                       {result, ok}
+                                               end;
+                                           _ ->
+                                               {result, ok}
+                                       end;
+                                   (_) ->
+                                       ok
                                end,
-                               plugins(Host))
-         end).
+                               Subs)
+                   end,
+                   plugins(Host))
+       end).
 
 %% -------
 %% user remove hook handling function
@@ -1335,68 +773,43 @@ unsubscribe_user(Entity, Owner) ->
 remove_user(User, Server) ->
     LUser = jlib:nodeprep(User),
     LServer = jlib:nameprep(Server),
-    Entity = jlib:make_jid(LUser, LServer, <<"">>),
+    Entity = jlib:make_jid(LUser, LServer, <<>>),
     Host = host(LServer),
     HomeTreeBase = <<"/home/", LServer/binary, "/", LUser/binary>>,
     spawn(fun () ->
-                 lists:foreach(fun (PType) ->
-                                       {result, Subscriptions} =
-                                           node_action(Host, PType,
-                                                       get_entity_subscriptions,
-                                                       [Host, Entity]),
-                                       lists:foreach(fun ({#pubsub_node{id =
-                                                                            NodeId},
-                                                           _, _, JID}) ->
-                                                             node_action(Host,
-                                                                         PType,
-                                                                         unsubscribe_node,
-                                                                         [NodeId,
-                                                                          Entity,
-                                                                          JID,
-                                                                          all]);
-                                                         (_) -> ok
-                                                     end,
-                                                     Subscriptions),
-                                       {result, Affiliations} =
-                                           node_action(Host, PType,
-                                                       get_entity_affiliations,
-                                                       [Host, Entity]),
-                                       lists:foreach(fun ({#pubsub_node{nodeid
-                                                                            =
-                                                                            {H,
-                                                                             N},
-                                                                        parents
-                                                                            =
-                                                                            []},
-                                                           owner}) ->
-                                                             delete_node(H, N,
-                                                                         Entity);
-                                                         ({#pubsub_node{nodeid
-                                                                            =
-                                                                            {H,
-                                                                             N},
-                                                                        type =
-                                                                            <<"hometree">>},
-                                                           owner})
-                                                             when N ==
-                                                                    HomeTreeBase ->
-                                                             delete_node(H, N,
-                                                                         Entity);
-                                                         ({#pubsub_node{id =
-                                                                            NodeId},
-                                                           publisher}) ->
-                                                             node_action(Host,
-                                                                         PType,
-                                                                         set_affiliation,
-                                                                         [NodeId,
-                                                                          Entity,
-                                                                          none]);
-                                                         (_) -> ok
-                                                     end,
-                                                     Affiliations)
+               lists:foreach(fun (PType) ->
+                           {result, Subs} = node_action(Host, PType,
+                                   get_entity_subscriptions,
+                                   [Host, Entity]),
+                           lists:foreach(fun
+                                   ({#pubsub_node{id = Nidx}, _, _, JID}) ->
+                                       node_action(Host, PType,
+                                           unsubscribe_node,
+                                           [Nidx, Entity, JID, all]);
+                                   (_) ->
+                                       ok
+                               end,
+                               Subs),
+                           {result, Affs} = node_action(Host, PType,
+                                   get_entity_affiliations,
+                                   [Host, Entity]),
+                           lists:foreach(fun
+                                   ({#pubsub_node{nodeid = {H, N}, parents = []}, owner}) ->
+                                       delete_node(H, N, Entity);
+                                   ({#pubsub_node{nodeid = {H, N}, type = Type}, owner})
+                                           when N == HomeTreeBase, Type == <<"hometree">> ->
+                                       delete_node(H, N, Entity);
+                                   ({#pubsub_node{id = Nidx}, publisher}) ->
+                                       node_action(Host, PType,
+                                           set_affiliation,
+                                           [Nidx, Entity, none]);
+                                   (_) ->
+                                       ok
                                end,
-                               plugins(Host))
-         end).
+                               Affs)
+                   end,
+                   plugins(Host))
+       end).
 
 handle_call(server_host, _From, State) ->
     {reply, State#state.server_host, State};
@@ -1411,36 +824,32 @@ handle_call(stop, _From, State) ->
 
 %%--------------------------------------------------------------------
 %% Function: handle_cast(Msg, State) -> {noreply, State} |
-%%                                   {noreply, State, Timeout} |
-%%                                   {stop, Reason, State}
+%%                                      {noreply, State, Timeout} |
+%%                                      {stop, Reason, State}
 %% Description: Handling cast messages
 %%--------------------------------------------------------------------
 %% @private
 handle_cast(_Msg, State) -> {noreply, State}.
 
 -spec(handle_info/2 ::
-(
-  _     :: {route, From::jid(), To::jid(), Packet::xmlel()},
-  State :: state())
+    (
+       _     :: {route, From::jid(), To::jid(), Packet::xmlel()},
+       State :: state())
     -> {noreply, state()}
-).
+    ).
 
 %%--------------------------------------------------------------------
 %% Function: handle_info(Info, State) -> {noreply, State} |
-%%                                    {noreply, State, Timeout} |
-%%                                    {stop, Reason, State}
+%%                                       {noreply, State, Timeout} |
+%%                                       {stop, Reason, State}
 %% Description: Handling all non call/cast messages
 %%--------------------------------------------------------------------
 %% @private
 handle_info({route, From, To, Packet},
-           #state{server_host = ServerHost, access = Access,
-                  plugins = Plugins} =
-               State) ->
-    case catch do_route(ServerHost, Access, Plugins,
-                       To#jid.lserver, From, To, Packet)
-       of
-      {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
-      _ -> ok
+           #state{server_host = ServerHost, access = Access, plugins = Plugins} = State) ->
+    case catch do_route(ServerHost, Access, Plugins, To#jid.lserver, From, To, Packet) of
+       {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
+       _ -> ok
     end,
     {noreply, State};
 handle_info(_Info, State) ->
@@ -1455,47 +864,51 @@ handle_info(_Info, State) ->
 %%--------------------------------------------------------------------
 %% @private
 terminate(_Reason,
-         #state{host = Host, server_host = ServerHost,
-                nodetree = TreePlugin, plugins = Plugins}) ->
+           #state{host = Host, server_host = ServerHost, nodetree = TreePlugin, plugins = Plugins}) ->
     ejabberd_router:unregister_route(Host),
     case lists:member(?PEPNODE, Plugins) of
-      true ->
-         ejabberd_hooks:delete(caps_update, ServerHost, ?MODULE,
-                               caps_update, 80),
-         ejabberd_hooks:delete(disco_sm_identity, ServerHost,
-                               ?MODULE, disco_sm_identity, 75),
-         ejabberd_hooks:delete(disco_sm_features, ServerHost,
-                               ?MODULE, disco_sm_features, 75),
-         ejabberd_hooks:delete(disco_sm_items, ServerHost,
-                               ?MODULE, disco_sm_items, 75),
-         gen_iq_handler:remove_iq_handler(ejabberd_sm,
-                                          ServerHost, ?NS_PUBSUB),
-         gen_iq_handler:remove_iq_handler(ejabberd_sm,
-                                          ServerHost, ?NS_PUBSUB_OWNER);
-      false -> ok
+       true ->
+           ejabberd_hooks:delete(caps_update, ServerHost,
+               ?MODULE, caps_update, 80),
+           ejabberd_hooks:delete(disco_sm_identity, ServerHost,
+               ?MODULE, disco_sm_identity, 75),
+           ejabberd_hooks:delete(disco_sm_features, ServerHost,
+               ?MODULE, disco_sm_features, 75),
+           ejabberd_hooks:delete(disco_sm_items, ServerHost,
+               ?MODULE, disco_sm_items, 75),
+           gen_iq_handler:remove_iq_handler(ejabberd_sm,
+               ServerHost, ?NS_PUBSUB),
+           gen_iq_handler:remove_iq_handler(ejabberd_sm,
+               ServerHost, ?NS_PUBSUB_OWNER);
+       false ->
+           ok
     end,
-    ejabberd_hooks:delete(sm_remove_connection_hook,
-                         ServerHost, ?MODULE, on_user_offline, 75),
+    ejabberd_hooks:delete(sm_remove_connection_hook, ServerHost,
+       ?MODULE, on_user_offline, 75),
     ejabberd_hooks:delete(disco_local_identity, ServerHost,
-                         ?MODULE, disco_local_identity, 75),
+       ?MODULE, disco_local_identity, 75),
     ejabberd_hooks:delete(disco_local_features, ServerHost,
-                         ?MODULE, disco_local_features, 75),
+       ?MODULE, disco_local_features, 75),
     ejabberd_hooks:delete(disco_local_items, ServerHost,
-                         ?MODULE, disco_local_items, 75),
+       ?MODULE, disco_local_items, 75),
     ejabberd_hooks:delete(presence_probe_hook, ServerHost,
-                         ?MODULE, presence_probe, 80),
-    ejabberd_hooks:delete(roster_in_subscription,
-                         ServerHost, ?MODULE, in_subscription, 50),
-    ejabberd_hooks:delete(roster_out_subscription,
-                         ServerHost, ?MODULE, out_subscription, 50),
-    ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
-                         remove_user, 50),
+       ?MODULE, presence_probe, 80),
+    ejabberd_hooks:delete(roster_in_subscription, ServerHost,
+       ?MODULE, in_subscription, 50),
+    ejabberd_hooks:delete(roster_out_subscription, ServerHost,
+       ?MODULE, out_subscription, 50),
+    ejabberd_hooks:delete(remove_user, ServerHost,
+       ?MODULE, remove_user, 50),
     ejabberd_hooks:delete(anonymous_purge_hook, ServerHost,
-                         ?MODULE, remove_user, 50),
+       ?MODULE, remove_user, 50),
     mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
-    gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
-    terminate_plugins(Host, ServerHost, Plugins,
-                     TreePlugin).
+    case whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME)) of
+       undefined ->
+           ?ERROR_MSG("~s process is dead, pubsub was broken", [?LOOPNAME]);
+       Pid ->
+           Pid ! stop
+    end,
+    terminate_plugins(Host, ServerHost, Plugins, TreePlugin).
 
 %%--------------------------------------------------------------------
 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -1505,16 +918,16 @@ terminate(_Reason,
 code_change(_OldVsn, State, _Extra) -> {ok, State}.
 
 -spec(do_route/7 ::
-(
-  ServerHost :: binary(),
-  Access     :: atom(),
-  Plugins    :: [binary(),...],
-  Host       :: mod_pubsub:hostPubsub(),
-  From       :: jid(),
-  To         :: jid(),
-  Packet     :: xmlel())
+    (
+       ServerHost :: binary(),
+       Access     :: atom(),
+       Plugins    :: [binary(),...],
+       Host       :: mod_pubsub:hostPubsub(),
+       From       :: jid(),
+       To         :: jid(),
+       Packet     :: xmlel())
     -> ok
-).
+    ).
 
 %%--------------------------------------------------------------------
 %%% Internal functions
@@ -1522,1029 +935,833 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
 do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
     #xmlel{name = Name, attrs = Attrs} = Packet,
     case To of
-      #jid{luser = <<"">>, lresource = <<"">>} ->
-         case Name of
-           <<"iq">> ->
-               case jlib:iq_query_info(Packet) of
-                 #iq{type = get, xmlns = ?NS_DISCO_INFO, sub_el = SubEl,
-                     lang = Lang} =
-                     IQ ->
-                     #xmlel{attrs = QAttrs} = SubEl,
-                     Node = xml:get_attr_s(<<"node">>, QAttrs),
-                     Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
-                                                    [],
-                                                    [ServerHost, ?MODULE,
-                                                     <<"">>, <<"">>]),
-                     Res = case iq_disco_info(Host, Node, From, Lang) of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el =
-                                                          [#xmlel{name =
-                                                                      <<"query">>,
-                                                                  attrs =
-                                                                      QAttrs,
-                                                                  children =
-                                                                      IQRes ++
-                                                                        Info}]});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
+       #jid{luser = <<>>, lresource = <<>>} ->
+           case Name of
+               <<"iq">> ->
+                   case jlib:iq_query_info(Packet) of
+                       #iq{type = get, xmlns = ?NS_DISCO_INFO, sub_el = SubEl, lang = Lang} = IQ ->
+                           #xmlel{attrs = QAttrs} = SubEl,
+                           Node = xml:get_attr_s(<<"node">>, QAttrs),
+                           Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
+                                   [],
+                                   [ServerHost, ?MODULE, <<>>, <<>>]),
+                           Res = case iq_disco_info(Host, Node, From, Lang) of
+                               {result, IQRes} ->
+                                   jlib:iq_to_xml(IQ#iq{type = result,
+                                           sub_el =
+                                           [#xmlel{name = <<"query">>,
+                                                   attrs = QAttrs,
+                                                   children = IQRes ++ Info}]});
+                               {error, Error} ->
+                                   jlib:make_error_reply(Packet, Error)
                            end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = get, xmlns = ?NS_DISCO_ITEMS,
-                     sub_el = SubEl} =
-                     IQ ->
-                     #xmlel{attrs = QAttrs} = SubEl,
-                     Node = xml:get_attr_s(<<"node">>, QAttrs),
-                     Res = case iq_disco_items(Host, Node, From) of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el =
-                                                          [#xmlel{name =
-                                                                      <<"query">>,
-                                                                  attrs =
-                                                                      QAttrs,
-                                                                  children =
-                                                                      IQRes}]})
-%                            {error, Error} ->
-%                                jlib:make_error_reply(Packet, Error)
+                           ejabberd_router:route(To, From, Res);
+                       #iq{type = get, xmlns = ?NS_DISCO_ITEMS, sub_el = SubEl} = IQ ->
+                           #xmlel{attrs = QAttrs} = SubEl,
+                           Node = xml:get_attr_s(<<"node">>, QAttrs),
+                           Res = case iq_disco_items(Host, Node, From, jlib:rsm_decode(IQ)) of
+                               {result, IQRes} ->
+                                   jlib:iq_to_xml(IQ#iq{type = result,
+                                           sub_el =
+                                           [#xmlel{name = <<"query">>,
+                                                   attrs = QAttrs,
+                                                   children = IQRes}]})
+                                   %{error, Error} ->
+                                   %     jlib:make_error_reply(Packet, Error)
                            end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = IQType, xmlns = ?NS_PUBSUB, lang = Lang,
-                     sub_el = SubEl} =
-                     IQ ->
-                     Res = case iq_pubsub(Host, ServerHost, From, IQType,
-                                          SubEl, Lang, Access, Plugins)
-                               of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
+                           ejabberd_router:route(To, From, Res);
+                       #iq{type = IQType, xmlns = ?NS_PUBSUB, lang = Lang, sub_el = SubEl} = IQ ->
+                           Res = case iq_pubsub(Host, ServerHost, From, IQType,
+                                   SubEl, Lang, Access, Plugins)
+                           of
+                               {result, IQRes} ->
+                                   jlib:iq_to_xml(IQ#iq{type = result, sub_el = IQRes});
+                               {error, Error} ->
+                                   jlib:make_error_reply(Packet, Error)
                            end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = IQType, xmlns = ?NS_PUBSUB_OWNER,
-                     lang = Lang, sub_el = SubEl} =
-                     IQ ->
-                     Res = case iq_pubsub_owner(Host, ServerHost, From,
-                                                IQType, SubEl, Lang)
-                               of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
+                           ejabberd_router:route(To, From, Res);
+                       #iq{type = IQType, xmlns = ?NS_PUBSUB_OWNER, lang = Lang, sub_el = SubEl} = IQ ->
+                           Res = case iq_pubsub_owner(Host, ServerHost, From,
+                                   IQType, SubEl, Lang)
+                           of
+                               {result, IQRes} ->
+                                   jlib:iq_to_xml(IQ#iq{type = result, sub_el = IQRes});
+                               {error, Error} ->
+                                   jlib:make_error_reply(Packet, Error)
                            end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = get, xmlns = (?NS_VCARD) = XMLNS,
-                     lang = Lang, sub_el = _SubEl} =
-                     IQ ->
-                     Res = IQ#iq{type = result,
-                                 sub_el =
-                                     [#xmlel{name = <<"vCard">>,
-                                             attrs = [{<<"xmlns">>, XMLNS}],
-                                             children = iq_get_vcard(Lang)}]},
-                     ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
-                 #iq{type = set, xmlns = ?NS_COMMANDS} = IQ ->
-                     Res = case iq_command(Host, ServerHost, From, IQ,
-                                           Access, Plugins)
-                               of
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error);
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes})
+                           ejabberd_router:route(To, From, Res);
+                       #iq{type = get, xmlns = (?NS_VCARD) = XMLNS, lang = Lang, sub_el = _SubEl} = IQ ->
+                           Res = IQ#iq{type = result,
+                                   sub_el =
+                                   [#xmlel{name = <<"vCard">>,
+                                           attrs = [{<<"xmlns">>, XMLNS}],
+                                           children = iq_get_vcard(Lang)}]},
+                           ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
+                       #iq{type = set, xmlns = ?NS_COMMANDS} = IQ ->
+                           Res = case iq_command(Host, ServerHost, From, IQ, Access, Plugins) of
+                               {error, Error} ->
+                                   jlib:make_error_reply(Packet, Error);
+                               {result, IQRes} ->
+                                   jlib:iq_to_xml(IQ#iq{type = result, sub_el = IQRes})
                            end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{} ->
-                     Err = jlib:make_error_reply(Packet,
-                                                 ?ERR_FEATURE_NOT_IMPLEMENTED),
-                     ejabberd_router:route(To, From, Err);
-                 _ -> ok
-               end;
-           <<"message">> ->
-               case xml:get_attr_s(<<"type">>, Attrs) of
-                 <<"error">> -> ok;
-                 _ ->
-                     case find_authorization_response(Packet) of
-                       none -> ok;
-                       invalid ->
-                           ejabberd_router:route(To, From,
-                                                 jlib:make_error_reply(Packet,
-                                                                       ?ERR_BAD_REQUEST));
-                       XFields ->
-                           handle_authorization_response(Host, From, To,
-                                                         Packet, XFields)
-                     end
-               end;
-           _ -> ok
-         end;
-      _ ->
-         case xml:get_attr_s(<<"type">>, Attrs) of
-           <<"error">> -> ok;
-           <<"result">> -> ok;
-           _ ->
-               Err = jlib:make_error_reply(Packet,
-                                           ?ERR_ITEM_NOT_FOUND),
-               ejabberd_router:route(To, From, Err)
-         end
+                           ejabberd_router:route(To, From, Res);
+                       #iq{} ->
+                           Err = jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
+                           ejabberd_router:route(To, From, Err);
+                       _ ->
+                           ok
+                   end;
+               <<"message">> ->
+                   case xml:get_attr_s(<<"type">>, Attrs) of
+                       <<"error">> ->
+                           ok;
+                       _ ->
+                           case find_authorization_response(Packet) of
+                               none ->
+                                   ok;
+                               invalid ->
+                                   Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
+                                   ejabberd_router:route(To, From, Err);
+                               XFields ->
+                                   handle_authorization_response(Host, From, To, Packet, XFields)
+                           end
+                   end;
+               _ ->
+                   ok
+           end;
+       _ ->
+           case xml:get_attr_s(<<"type">>, Attrs) of
+               <<"error">> ->
+                   ok;
+               <<"result">> ->
+                   ok;
+               _ ->
+                   Err = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND),
+                   ejabberd_router:route(To, From, Err)
+           end
     end.
 
 command_disco_info(_Host, ?NS_COMMANDS, _From) ->
     IdentityEl = #xmlel{name = <<"identity">>,
-                       attrs =
-                           [{<<"category">>, <<"automation">>},
-                            {<<"type">>, <<"command-list">>}],
-                       children = []},
+           attrs = [{<<"category">>, <<"automation">>},
+               {<<"type">>, <<"command-list">>}]},
     {result, [IdentityEl]};
-command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING,
-                  _From) ->
+command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING, _From) ->
     IdentityEl = #xmlel{name = <<"identity">>,
-                       attrs =
-                           [{<<"category">>, <<"automation">>},
-                            {<<"type">>, <<"command-node">>}],
-                       children = []},
+           attrs = [{<<"category">>, <<"automation">>},
+               {<<"type">>, <<"command-node">>}]},
     FeaturesEl = #xmlel{name = <<"feature">>,
-                       attrs = [{<<"var">>, ?NS_COMMANDS}], children = []},
+           attrs = [{<<"var">>, ?NS_COMMANDS}]},
     {result, [IdentityEl, FeaturesEl]}.
 
 node_disco_info(Host, Node, From) ->
     node_disco_info(Host, Node, From, true, true).
 
 node_disco_info(Host, Node, From, _Identity, _Features) ->
-%    Action =
-%      fun(#pubsub_node{type = Type, id = NodeId}) ->
-%              I = case Identity of
-%                      false ->
-%                          [];
-%                      true ->
-%                          Types =
-%                              case tree_call(Host, get_subnodes, [Host, Node, From]) of
-%                                  [] ->
-%                                      [<<"leaf">>]; %% No sub-nodes: it's a leaf node
-%                                  _ ->
-%                                      case node_call(Type, get_items, [NodeId, From]) of
-%                                          {result, []} -> [<<"collection">>];
-%                                          {result, _} -> [<<"leaf">>, <<"collection">>];
-%                                          _ -> []
-%                                      end
-%                              end,
-%                          lists:map(fun(T) ->
-%                                               #xmlel{name = <<"identity">>,
-%                                                      attrs =
-%                                                          [{<<"category">>,
-%                                                            <<"pubsub">>},
-%                                                           {<<"type">>, T}],
-%                                                      children = []}
-%                                    end, Types)
-%                  end,
-%              F = case Features of
-%                      false ->
-%                          [];
-%                      true ->
-%                          [#xmlel{name = <<"feature">>,
-%                                     attrs = [{<<"var">>, ?NS_PUBSUB}],
-%                                     children = []}
-%                              | lists:map(fun (T) ->
-%                                                  #xmlel{name = <<"feature">>,
-%                                                         attrs =
-%                                                             [{<<"var">>,
-%                                                               <<(?NS_PUBSUB)/binary,
-%                                                                 "#",
-%                                                                 T/binary>>}],
-%                                                         children = []}
-%                                          end,
-%                                          features(Type))]
-%                  end,
-%              %% TODO: add meta-data info (spec section 5.4)
-%              {result, I ++ F}
-%      end,
-%    case transaction(Host, Node, Action, sync_dirty) of
-%      {result, {_, Result}} -> {result, Result};
-%      Other -> Other
-%    end.
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Types = case tree_call(Host, get_subnodes,
-                                                     [Host, Node, From])
-                                          of
-                                        [] -> [<<"leaf">>];
-                                        _ ->
-                                            case node_call(Type, get_items,
-                                                           [NodeId, From])
-                                                of
-                                              {result, []} ->
-                                                  [<<"collection">>];
-                                              {result, _} ->
-                                                  [<<"leaf">>,
-                                                   <<"collection">>];
-                                              _ -> []
-                                            end
-                                      end,
-                   I = lists:map(fun (T) ->
-                                                #xmlel{name = <<"identity">>,
-                                                       attrs =
-                                                           [{<<"category">>,
-                                                             <<"pubsub">>},
-                                                            {<<"type">>, T}],
-                                                       children = []}
-                                        end,
-                                        Types),
-                    F = [#xmlel{name = <<"feature">>,
-                                      attrs = [{<<"var">>, ?NS_PUBSUB}],
-                                      children = []}
-                               | lists:map(fun (T) ->
-                                                   #xmlel{name = <<"feature">>,
-                                                          attrs =
-                                                              [{<<"var">>,
-                                                                <<(?NS_PUBSUB)/binary,
-                                                                  "#",
-                                                                  T/binary>>}],
-                                                          children = []}
-                                           end,
-                                           features(Type))],
-                    {result, I ++ F}
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           Types = case tree_call(Host, get_subnodes, [Host, Node, From]) of
+               [] ->
+                   [<<"leaf">>];
+               _ ->
+                   case node_call(Host, Type, get_items, [Nidx, From, none]) of
+                       {result, {[], _}} -> [<<"collection">>];
+                       {result, _} -> [<<"leaf">>, <<"collection">>];
+                       _ -> []
+                   end
+           end,
+           I = [#xmlel{name = <<"identity">>,
+                       attrs = [{<<"category">>, <<"pubsub">>}, {<<"type">>, T}]}
+                   || T <- Types],
+           F = [#xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_PUBSUB}]}
+                   | [#xmlel{name = <<"feature">>,
+                           attrs = [{<<"var">>, feature(F)}]}
+                       || F <- plugin_features(Host, Type)]],
+           {result, I ++ F}
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> {result, Result};
-      Other -> Other
+       {result, {_, Result}} -> {result, Result};
+       Other -> Other
     end.
 
 iq_disco_info(Host, SNode, From, Lang) ->
     [Node | _] = case SNode of
-                       <<>> -> [<<>>];
-                       _ -> str:tokens(SNode, <<"!">>)
-                     end,
- %   Node = string_to_node(RealSNode),
+       <<>> -> [<<>>];
+       _ -> str:tokens(SNode, <<"!">>)
+    end,
   %   Node = string_to_node(RealSNode),
     case Node of
-      <<>> ->
-         {result,
-          [#xmlel{name = <<"identity">>,
-                  attrs =
-                      [{<<"category">>, <<"pubsub">>},
-                       {<<"type">>, <<"service">>},
-                       {<<"name">>,
-                        translate:translate(Lang, <<"Publish-Subscribe">>)}],
-                  children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_PUBSUB}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_COMMANDS}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_VCARD}], children = []}]
-            ++
-            lists:map(fun (Feature) ->
-                              #xmlel{name = <<"feature">>,
-                                     attrs =
-                                         [{<<"var">>, <<(?NS_PUBSUB)/binary, "#", Feature/binary>>}],
-                                     children = []}
-                      end,
-                      features(Host, Node))};
-      ?NS_COMMANDS -> command_disco_info(Host, Node, From);
-      ?NS_PUBSUB_GET_PENDING ->
-         command_disco_info(Host, Node, From);
-      _ -> node_disco_info(Host, Node, From)
+       <<>> ->
+           {result, [#xmlel{name = <<"identity">>,
+                       attrs = [{<<"category">>, <<"pubsub">>},
+                           {<<"type">>, <<"service">>},
+                           {<<"name">>, translate:translate(Lang, <<"Publish-Subscribe">>)}]},
+                   #xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_DISCO_INFO}]},
+                   #xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_DISCO_ITEMS}]},
+                   #xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_PUBSUB}]},
+                   #xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_COMMANDS}]},
+                   #xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, ?NS_VCARD}]}]
+               ++ [#xmlel{name = <<"feature">>,
+                       attrs = [{<<"var">>, feature(F)}]}
+                   || F <- features(Host, Node)]};
+       ?NS_COMMANDS ->
+           command_disco_info(Host, Node, From);
+       ?NS_PUBSUB_GET_PENDING ->
+           command_disco_info(Host, Node, From);
+       _ ->
+           node_disco_info(Host, Node, From)
     end.
 
--spec(iq_disco_items/3 ::
-(
-  Host   :: mod_pubsub:host(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  From   :: jid())
+-spec(iq_disco_items/4 ::
+    (
+       Host   :: mod_pubsub:host(),
+       Node   :: <<>> | mod_pubsub:nodeId(),
+       From   :: jid(),
+       Rsm    :: none | rsm_in())
     -> {result, [xmlel()]}
-).
-iq_disco_items(Host, <<>>, From) ->
-         {result,
-          lists:map(fun (#pubsub_node{nodeid = {_, SubNode},
-                                      options = Options}) ->
-                            Attrs = case get_option(Options, title) of
-                                      false ->
-                                          [{<<"jid">>, Host}
-                                           | nodeAttr(SubNode)];
-                                      Title ->
-                                          [{<<"jid">>, Host},
-                                           {<<"name">>, Title}
-                                           | nodeAttr(SubNode)]
-                                    end,
-                            #xmlel{name = <<"item">>, attrs = Attrs,
-                                   children = []}
-                    end,
-                    tree_action(Host, get_subnodes, [Host, <<>>, From]))};
-%    case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
-%      Nodes when is_list(Nodes) ->
-%        {result,
-%         lists:map(fun (#pubsub_node{nodeid = {_, SubNode},
-%                                     options = Options}) ->
-%                           Attrs = case get_option(Options, title) of
-%                                     false ->
-%                                         [{<<"jid">>, Host}
-%                                          | nodeAttr(SubNode)];
-%                                     Title ->
-%                                         [{<<"jid">>, Host},
-%                                          {<<"name">>, Title}
-%                                          | nodeAttr(SubNode)]
-%                                   end,
-%                           #xmlel{name = <<"item">>, attrs = Attrs,
-%                                  children = []}
-%                   end,
-%                   Nodes)};
-%      Other -> Other
-%    end;
-iq_disco_items(Host, ?NS_COMMANDS, _From) ->
-    CommandItems = [#xmlel{name = <<"item">>,
-                          attrs =
-                              [{<<"jid">>, Host},
-                               {<<"node">>, ?NS_PUBSUB_GET_PENDING},
-                               {<<"name">>, <<"Get Pending">>}],
-                          children = []}],
-    {result, CommandItems};
-iq_disco_items(_Host, ?NS_PUBSUB_GET_PENDING, _From) ->
-    CommandItems = [], {result, CommandItems};
-iq_disco_items(Host, Item, From) ->
+    ).
+iq_disco_items(Host, <<>>, From, _RSM) ->
+    {result,
+       lists:map(fun (#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
+                   Attrs = case get_option(Options, title) of
+                       false ->
+                           [{<<"jid">>, Host}
+                               | nodeAttr(SubNode)];
+                       Title ->
+                           [{<<"jid">>, Host},
+                               {<<"name">>, Title}
+                               | nodeAttr(SubNode)]
+                   end,
+                   #xmlel{name = <<"item">>, attrs = Attrs}
+           end,
+           tree_action(Host, get_subnodes, [Host, <<>>, From]))};
+iq_disco_items(Host, ?NS_COMMANDS, _From, _RSM) ->
+    {result, [#xmlel{name = <<"item">>,
+               attrs = [{<<"jid">>, Host},
+                   {<<"node">>, ?NS_PUBSUB_GET_PENDING},
+                   {<<"name">>, <<"Get Pending">>}]}]};
+iq_disco_items(_Host, ?NS_PUBSUB_GET_PENDING, _From, _RSM) ->
+    {result, []};
+iq_disco_items(Host, Item, From, RSM) ->
     case str:tokens(Item, <<"!">>) of
-      [_Node, _ItemID] -> {result, []};
-      [Node] ->
-%        Node = string_to_node(SNode),
-         Action = fun (#pubsub_node{id = Idx, type = Type,
-                                    options = Options, owners = Owners}) ->
-                          NodeItems = case get_allowed_items_call(Host, Idx,
-                                                                  From, Type,
-                                                                  Options,
-                                                                  Owners)
-                                          of
-                                        {result, R} -> R;
-                                        _ -> []
-                                      end,
-                          Nodes = lists:map(fun (#pubsub_node{nodeid =
-                                                                  {_, SubNode},
-                                                              options =
-                                                                  SubOptions}) ->
-                                                    Attrs = case
-                                                              get_option(SubOptions,
-                                                                         title)
-                                                                of
-                                                              false ->
-                                                                  [{<<"jid">>,
-                                                                    Host}
-                                                                   | nodeAttr(SubNode)];
-                                                              Title ->
-                                                                  [{<<"jid">>,
-                                                                    Host},
-                                                                   {<<"name">>,
-                                                                    Title}
-                                                                   | nodeAttr(SubNode)]
-                                                            end,
-                                                    #xmlel{name = <<"item">>,
-                                                           attrs = Attrs,
-                                                           children = []}
-                                            end,
-                                            tree_call(Host, get_subnodes,
-                                                      [Host, Node, From])),
-                          Items = lists:map(fun (#pubsub_item{itemid =
-                                                                  {RN, _}}) ->
-                                                    {result, Name} =
-                                                        node_call(Type,
-                                                                  get_item_name,
-                                                                  [Host, Node,
-                                                                   RN]),
-                                                    #xmlel{name = <<"item">>,
-                                                           attrs =
-                                                               [{<<"jid">>,
-                                                                 Host},
-                                                                {<<"name">>,
-                                                                 Name}],
-                                                           children = []}
-                                            end,
-                                            NodeItems),
-                          {result, Nodes ++ Items}
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
+       [_Node, _ItemId] ->
+           {result, []};
+       [Node] ->
+           Action = fun (#pubsub_node{id = Nidx, type = Type, options = Options, owners = O}) ->
+                   Owners = node_owners_call(Host, Type, Nidx, O),
+                   {NodeItems, RsmOut} = case get_allowed_items_call(Host, Nidx,
+                           From, Type, Options, Owners, RSM)
+                   of
+                       {result, R} -> R;
+                       _ -> {[], none}
+                   end,
+                   Nodes = lists:map(fun (#pubsub_node{nodeid = {_, SubNode}, options = SubOptions}) ->
+                                   Attrs = case get_option(SubOptions, title) of
+                                       false ->
+                                           [{<<"jid">>, Host}
+                                               | nodeAttr(SubNode)];
+                                       Title ->
+                                           [{<<"jid">>, Host},
+                                               {<<"name">>, Title}
+                                               | nodeAttr(SubNode)]
+                                   end,
+                                   #xmlel{name = <<"item">>, attrs = Attrs}
+                           end,
+                           tree_call(Host, get_subnodes, [Host, Node, From])),
+                   Items = lists:map(fun (#pubsub_item{itemid = {RN, _}}) ->
+                                   {result, Name} = node_call(Host, Type, get_item_name, [Host, Node, RN]),
+                                   #xmlel{name = <<"item">>,
+                                       attrs = [{<<"jid">>, Host}, {<<"name">>, Name}]}
+                           end,
+                           NodeItems),
+                   {result, Nodes ++ Items ++ jlib:rsm_encode(RsmOut)}
+           end,
+           case transaction(Host, Node, Action, sync_dirty) of
+               {result, {_, Result}} -> {result, Result};
+               Other -> Other
+           end
     end.
 
 -spec(iq_sm/3 ::
-(
-  From :: jid(),
-  To   :: jid(),
-  IQ   :: iq_request())
+    (
+       From :: jid(),
+       To   :: jid(),
+       IQ   :: iq_request())
     -> iq_result() | iq_error()
-).
+    ).
 iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
     ServerHost = To#jid.lserver,
     LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
     Res = case XMLNS of
-           ?NS_PUBSUB ->
-               iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang);
-           ?NS_PUBSUB_OWNER ->
-               iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl,
-                               Lang)
-         end,
+       ?NS_PUBSUB ->
+           iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang);
+       ?NS_PUBSUB_OWNER ->
+           iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl, Lang)
+    end,
     case Res of
-      {result, IQRes} -> IQ#iq{type = result, sub_el = IQRes};
-      {error, Error} ->
-         IQ#iq{type = error, sub_el = [Error, SubEl]}
+       {result, IQRes} -> IQ#iq{type = result, sub_el = IQRes};
+       {error, Error} -> IQ#iq{type = error, sub_el = [Error, SubEl]}
     end.
 
 iq_get_vcard(Lang) ->
     [#xmlel{name = <<"FN">>, attrs = [],
            children = [{xmlcdata, <<"ejabberd/mod_pubsub">>}]},
-     #xmlel{name = <<"URL">>, attrs = [],
+       #xmlel{name = <<"URL">>, attrs = [],
            children = [{xmlcdata, ?EJABBERD_URI}]},
-     #xmlel{name = <<"DESC">>, attrs = [],
-           children =
-               [{xmlcdata,
-                 <<(translate:translate(Lang,
-                                        <<"ejabberd Publish-Subscribe module">>))/binary,
-                   "\nCopyright (c) 2004-2015 ProcessOne">>}]}].
+       #xmlel{name = <<"DESC">>, attrs = [],
+           children = [{xmlcdata,
+                   <<(translate:translate(Lang, <<"ejabberd Publish-Subscribe module">>))/binary,
+                       "\nCopyright (c) 2004-2015 ProcessOne">>}]}].
 
 -spec(iq_pubsub/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary())
+    (
+       Host       :: mod_pubsub:host(),
+       ServerHost :: binary(),
+       From       :: jid(),
+       IQType     :: 'get' | 'set',
+       SubEl      :: xmlel(),
+       Lang       :: binary())
     -> {result, [xmlel()]}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 
 iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang) ->
     iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, all, plugins(ServerHost)).
 
 -spec(iq_pubsub/8 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary(),
-  Access     :: atom(),
-  Plugins    :: [binary(),...])
+    (
+       Host       :: mod_pubsub:host(),
+       ServerHost :: binary(),
+       From       :: jid(),
+       IQType     :: 'get' | 'set',
+       SubEl      :: xmlel(),
+       Lang       :: binary(),
+       Access     :: atom(),
+       Plugins    :: [binary(),...])
     -> {result, [xmlel()]}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 
 iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
     #xmlel{children = SubEls} = SubEl,
     case xml:remove_cdata(SubEls) of
-      [#xmlel{name = Name, attrs = Attrs, children = Els} | Rest] ->
-         Node = xml:get_attr_s(<<"node">>, Attrs),
-         case {IQType, Name} of
-           {set, <<"create">>} ->
-               Config = case Rest of
-                          [#xmlel{name = <<"configure">>, children = C}] -> C;
-                          _ -> []
-                        end,
-               Type = case xml:get_attr_s(<<"type">>, Attrs) of
-                        <<>> -> hd(Plugins);
-                        T -> T
-                      end,
-               case lists:member(Type, Plugins) of
-                 false ->
-                     {error,
-                      extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                     unsupported, <<"create-nodes">>)};
-                 true ->
-                     create_node(Host, ServerHost, Node, From, Type, Access, Config)
-               end;
-           {set, <<"publish">>} ->
-               case xml:remove_cdata(Els) of
-                 [#xmlel{name = <<"item">>, attrs = ItemAttrs,
-                         children = Payload}] ->
-                     ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
-                     publish_item(Host, ServerHost, Node, From, ItemId, Payload, Access);
-                 [] ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
-                 _ ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)}
-               end;
-           {set, <<"retract">>} ->
-               ForceNotify = case xml:get_attr_s(<<"notify">>, Attrs)
-                                 of
-                               <<"1">> -> true;
-                               <<"true">> -> true;
-                               _ -> false
-                             end,
-               case xml:remove_cdata(Els) of
-                 [#xmlel{name = <<"item">>, attrs = ItemAttrs}] ->
-                     ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
-                     delete_item(Host, Node, From, ItemId, ForceNotify);
-                 _ ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"item-required">>)}
-               end;
-           {set, <<"subscribe">>} ->
-               Config = case Rest of
-                          [#xmlel{name = <<"options">>, children = C}] -> C;
-                          _ -> []
-                        end,
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               subscribe_node(Host, Node, From, JID, Config);
-           {set, <<"unsubscribe">>} ->
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               SubId = xml:get_attr_s(<<"subid">>, Attrs),
-               unsubscribe_node(Host, Node, From, JID, SubId);
-           {get, <<"items">>} ->
-               MaxItems = xml:get_attr_s(<<"max_items">>, Attrs),
-               SubId = xml:get_attr_s(<<"subid">>, Attrs),
-               ItemIDs = lists:foldl(fun (#xmlel{name = <<"item">>,
-                                                 attrs = ItemAttrs},
-                                          Acc) ->
-                                             case xml:get_attr_s(<<"id">>,
-                                                                 ItemAttrs)
-                                                 of
-                                               <<"">> -> Acc;
-                                               ItemID -> [ItemID | Acc]
-                                             end;
-                                         (_, Acc) -> Acc
-                                     end,
-                                     [], xml:remove_cdata(Els)),
-               get_items(Host, Node, From, SubId, MaxItems, ItemIDs);
-           {get, <<"subscriptions">>} ->
-               get_subscriptions(Host, Node, From, Plugins);
-           {get, <<"affiliations">>} ->
-               get_affiliations(Host, Node, From, Plugins);
-           {get, <<"options">>} ->
-               SubID = xml:get_attr_s(<<"subid">>, Attrs),
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               get_options(Host, Node, JID, SubID, Lang);
-           {set, <<"options">>} ->
-               SubID = xml:get_attr_s(<<"subid">>, Attrs),
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               set_options(Host, Node, JID, SubID, Els);
-           _ -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-         end;
-      Other ->
-         ?INFO_MSG("Too many actions: ~p", [Other]),
-         {error, ?ERR_BAD_REQUEST}
+       [#xmlel{name = Name, attrs = Attrs, children = Els} | Rest] ->
+           Node = xml:get_attr_s(<<"node">>, Attrs),
+           case {IQType, Name} of
+               {set, <<"create">>} ->
+                   Config = case Rest of
+                       [#xmlel{name = <<"configure">>, children = C}] -> C;
+                       _ -> []
+                   end,
+                   Type = case xml:get_attr_s(<<"type">>, Attrs) of
+                       <<>> -> hd(Plugins);
+                       T -> T
+                   end,
+                   case lists:member(Type, Plugins) of
+                       false ->
+                           {error,
+                               extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"create-nodes">>)};
+                       true ->
+                           create_node(Host, ServerHost, Node, From, Type, Access, Config)
+                   end;
+               {set, <<"publish">>} ->
+                   case xml:remove_cdata(Els) of
+                       [#xmlel{name = <<"item">>, attrs = ItemAttrs,
+                                       children = Payload}] ->
+                           ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
+                           publish_item(Host, ServerHost, Node, From, ItemId, Payload, Access);
+                       [] ->
+                           {error,
+                               extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
+                       _ ->
+                           {error,
+                               extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)}
+                   end;
+               {set, <<"retract">>} ->
+                   ForceNotify = case xml:get_attr_s(<<"notify">>, Attrs) of
+                       <<"1">> -> true;
+                       <<"true">> -> true;
+                       _ -> false
+                   end,
+                   case xml:remove_cdata(Els) of
+                       [#xmlel{name = <<"item">>, attrs = ItemAttrs}] ->
+                           ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
+                           delete_item(Host, Node, From, ItemId, ForceNotify);
+                       _ ->
+                           {error,
+                               extended_error(?ERR_BAD_REQUEST, <<"item-required">>)}
+                   end;
+               {set, <<"subscribe">>} ->
+                   Config = case Rest of
+                       [#xmlel{name = <<"options">>, children = C}] -> C;
+                       _ -> []
+                   end,
+                   JID = xml:get_attr_s(<<"jid">>, Attrs),
+                   subscribe_node(Host, Node, From, JID, Config);
+               {set, <<"unsubscribe">>} ->
+                   JID = xml:get_attr_s(<<"jid">>, Attrs),
+                   SubId = xml:get_attr_s(<<"subid">>, Attrs),
+                   unsubscribe_node(Host, Node, From, JID, SubId);
+               {get, <<"items">>} ->
+                   MaxItems = xml:get_attr_s(<<"max_items">>, Attrs),
+                   SubId = xml:get_attr_s(<<"subid">>, Attrs),
+                   ItemIds = lists:foldl(fun
+                               (#xmlel{name = <<"item">>, attrs = ItemAttrs}, Acc) ->
+                                   case xml:get_attr_s(<<"id">>, ItemAttrs) of
+                                       <<>> -> Acc;
+                                       ItemId -> [ItemId | Acc]
+                                   end;
+                               (_, Acc) ->
+                                   Acc
+                           end,
+                           [], xml:remove_cdata(Els)),
+                   get_items(Host, Node, From, SubId, MaxItems, ItemIds, jlib:rsm_decode(SubEl));
+               {get, <<"subscriptions">>} ->
+                   get_subscriptions(Host, Node, From, Plugins);
+               {get, <<"affiliations">>} ->
+                   get_affiliations(Host, Node, From, Plugins);
+               {get, <<"options">>} ->
+                   SubId = xml:get_attr_s(<<"subid">>, Attrs),
+                   JID = xml:get_attr_s(<<"jid">>, Attrs),
+                   get_options(Host, Node, JID, SubId, Lang);
+               {set, <<"options">>} ->
+                   SubId = xml:get_attr_s(<<"subid">>, Attrs),
+                   JID = xml:get_attr_s(<<"jid">>, Attrs),
+                   set_options(Host, Node, JID, SubId, Els);
+               _ ->
+                   {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
+           end;
+       Other ->
+           ?INFO_MSG("Too many actions: ~p", [Other]),
+           {error, ?ERR_BAD_REQUEST}
     end.
 
 
 -spec(iq_pubsub_owner/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary())
+    (
+       Host       :: mod_pubsub:host(),
+       ServerHost :: binary(),
+       From       :: jid(),
+       IQType     :: 'get' | 'set',
+       SubEl      :: xmlel(),
+       Lang       :: binary())
     -> {result, [xmlel()]}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
     #xmlel{children = SubEls} = SubEl,
     Action = xml:remove_cdata(SubEls),
     case Action of
-      [#xmlel{name = Name, attrs = Attrs, children = Els}] ->
-         Node = xml:get_attr_s(<<"node">>, Attrs),
-         case {IQType, Name} of
-           {get, <<"configure">>} ->
-               get_configure(Host, ServerHost, Node, From, Lang);
-           {set, <<"configure">>} ->
-               set_configure(Host, Node, From, Els, Lang);
-           {get, <<"default">>} ->
-               get_default(Host, Node, From, Lang);
-           {set, <<"delete">>} -> delete_node(Host, Node, From);
-           {set, <<"purge">>} -> purge_node(Host, Node, From);
-           {get, <<"subscriptions">>} ->
-               get_subscriptions(Host, Node, From);
-           {set, <<"subscriptions">>} ->
-               set_subscriptions(Host, Node, From,
-                                 xml:remove_cdata(Els));
-           {get, <<"affiliations">>} ->
-               get_affiliations(Host, Node, From);
-           {set, <<"affiliations">>} ->
-               set_affiliations(Host, Node, From, xml:remove_cdata(Els));
-           _ -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-         end;
-      _ ->
-         ?INFO_MSG("Too many actions: ~p", [Action]),
-         {error, ?ERR_BAD_REQUEST}
+       [#xmlel{name = Name, attrs = Attrs, children = Els}] ->
+           Node = xml:get_attr_s(<<"node">>, Attrs),
+           case {IQType, Name} of
+               {get, <<"configure">>} ->
+                   get_configure(Host, ServerHost, Node, From, Lang);
+               {set, <<"configure">>} ->
+                   set_configure(Host, Node, From, Els, Lang);
+               {get, <<"default">>} ->
+                   get_default(Host, Node, From, Lang);
+               {set, <<"delete">>} ->
+                   delete_node(Host, Node, From);
+               {set, <<"purge">>} ->
+                   purge_node(Host, Node, From);
+               {get, <<"subscriptions">>} ->
+                   get_subscriptions(Host, Node, From);
+               {set, <<"subscriptions">>} ->
+                   set_subscriptions(Host, Node, From, xml:remove_cdata(Els));
+               {get, <<"affiliations">>} ->
+                   get_affiliations(Host, Node, From);
+               {set, <<"affiliations">>} ->
+                   set_affiliations(Host, Node, From, xml:remove_cdata(Els));
+               _ ->
+                   {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
+           end;
+       _ ->
+           ?INFO_MSG("Too many actions: ~p", [Action]),
+           {error, ?ERR_BAD_REQUEST}
     end.
 
 iq_command(Host, ServerHost, From, IQ, Access, Plugins) ->
     case adhoc:parse_request(IQ) of
-      Req when is_record(Req, adhoc_request) ->
-         case adhoc_request(Host, ServerHost, From, Req, Access,
-                            Plugins)
-             of
-           Resp when is_record(Resp, adhoc_response) ->
-               {result, [adhoc:produce_response(Req, Resp)]};
-           Error -> Error
-         end;
-      Err -> Err
+       Req when is_record(Req, adhoc_request) ->
+           case adhoc_request(Host, ServerHost, From, Req, Access, Plugins) of
+               Resp when is_record(Resp, adhoc_response) ->
+                   {result, [adhoc:produce_response(Req, Resp)]};
+               Error ->
+                   Error
+           end;
+       Err -> Err
     end.
 
 %% @doc <p>Processes an Ad Hoc Command.</p>
 adhoc_request(Host, _ServerHost, Owner,
-             #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
-                            lang = Lang, action = <<"execute">>,
-                            xdata = false},
-             _Access, Plugins) ->
+           #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
+               lang = Lang, action = <<"execute">>,
+               xdata = false},
+           _Access, Plugins) ->
     send_pending_node_form(Host, Owner, Lang, Plugins);
 adhoc_request(Host, _ServerHost, Owner,
-             #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
-                            action = <<"execute">>, xdata = XData},
-             _Access, _Plugins) ->
+           #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
+               action = <<"execute">>, xdata = XData},
+           _Access, _Plugins) ->
     ParseOptions = case XData of
-                    #xmlel{name = <<"x">>} = XEl ->
-                        case jlib:parse_xdata_submit(XEl) of
-                          invalid -> {error, ?ERR_BAD_REQUEST};
-                          XData2 ->
-                              case set_xoption(Host, XData2, []) of
-                                NewOpts when is_list(NewOpts) ->
-                                    {result, NewOpts};
-                                Err -> Err
-                              end
-                        end;
-                    _ ->
-                        ?INFO_MSG("Bad XForm: ~p", [XData]),
-                        {error, ?ERR_BAD_REQUEST}
-                  end,
+       #xmlel{name = <<"x">>} = XEl ->
+           case jlib:parse_xdata_submit(XEl) of
+               invalid ->
+                   {error, ?ERR_BAD_REQUEST};
+               XData2 ->
+                   case set_xoption(Host, XData2, []) of
+                       NewOpts when is_list(NewOpts) -> {result, NewOpts};
+                       Err -> Err
+                   end
+           end;
+       _ ->
+           ?INFO_MSG("Bad XForm: ~p", [XData]),
+           {error, ?ERR_BAD_REQUEST}
+    end,
     case ParseOptions of
-      {result, XForm} ->
-         case lists:keysearch(node, 1, XForm) of
-           {value, {_, Node}} ->
-               send_pending_auth_events(Host, Node, Owner);
-           false ->
-               {error,
-                extended_error(?ERR_BAD_REQUEST, <<"bad-payload">>)}
-         end;
-      Error -> Error
+       {result, XForm} ->
+           case lists:keysearch(node, 1, XForm) of
+               {value, {_, Node}} -> send_pending_auth_events(Host, Node, Owner);
+               false -> {error, extended_error(?ERR_BAD_REQUEST, <<"bad-payload">>)}
+           end;
+       Error -> Error
     end;
 adhoc_request(_Host, _ServerHost, _Owner,
-             #adhoc_request{action = <<"cancel">>}, _Access,
-             _Plugins) ->
+           #adhoc_request{action = <<"cancel">>}, _Access,
+           _Plugins) ->
     #adhoc_response{status = canceled};
 adhoc_request(Host, ServerHost, Owner,
-             #adhoc_request{action = <<>>} = R, Access, Plugins) ->
+           #adhoc_request{action = <<>>} = R, Access, Plugins) ->
     adhoc_request(Host, ServerHost, Owner,
-                 R#adhoc_request{action = <<"execute">>}, Access,
-                 Plugins);
-adhoc_request(_Host, _ServerHost, _Owner, Other,
-             _Access, _Plugins) ->
+       R#adhoc_request{action = <<"execute">>}, Access,
+       Plugins);
+adhoc_request(_Host, _ServerHost, _Owner, Other, _Access, _Plugins) ->
     ?DEBUG("Couldn't process ad hoc command:~n~p", [Other]),
     {error, ?ERR_ITEM_NOT_FOUND}.
 
-%% @spec (Host, Owner, Lang, Plugins) -> iqRes()
-%% @doc <p>Sends the process pending subscriptions XForm for Host to
-%% Owner.</p>
+%% @doc <p>Sends the process pending subscriptions XForm for Host to Owner.</p>
 send_pending_node_form(Host, Owner, _Lang, Plugins) ->
-    Filter = fun (Plugin) ->
-                    lists:member(<<"get-pending">>, features(Plugin))
-            end,
+    Filter = fun (Type) ->
+           lists:member(<<"get-pending">>, plugin_features(Host, Type))
+    end,
     case lists:filter(Filter, Plugins) of
-      [] -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
-      Ps ->
-         XOpts = lists:map(fun (Node) ->
-                                   #xmlel{name = <<"option">>, attrs = [],
-                                          children =
-                                              [#xmlel{name = <<"value">>,
-                                                      attrs = [],
-                                                      children =
-                                                          [{xmlcdata, Node}]}]}
-                           end,
-                           get_pending_nodes(Host, Owner, Ps)),
-         XForm = #xmlel{name = <<"x">>,
-                        attrs =
-                            [{<<"xmlns">>, ?NS_XDATA},
-                             {<<"type">>, <<"form">>}],
-                        children =
-                            [#xmlel{name = <<"field">>,
-                                    attrs =
-                                        [{<<"type">>, <<"list-single">>},
-                                         {<<"var">>, <<"pubsub#node">>}],
-                                    children = lists:usort(XOpts)}]},
-         #adhoc_response{status = executing,
-                         defaultaction = <<"execute">>, elements = [XForm]}
+       [] ->
+           {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
+       Ps ->
+           XOpts = [#xmlel{name = <<"option">>, attrs = [],
+                       children = [#xmlel{name = <<"value">>,
+                               attrs = [],
+                               children = [{xmlcdata, Node}]}]}
+                   || Node <- get_pending_nodes(Host, Owner, Ps)],
+           XForm = #xmlel{name = <<"x">>,
+                   attrs = [{<<"xmlns">>, ?NS_XDATA},
+                       {<<"type">>, <<"form">>}],
+                   children = [#xmlel{name = <<"field">>,
+                           attrs = [{<<"type">>, <<"list-single">>},
+                               {<<"var">>, <<"pubsub#node">>}],
+                           children = lists:usort(XOpts)}]},
+           #adhoc_response{status = executing,
+               defaultaction = <<"execute">>, elements = [XForm]}
     end.
 
 get_pending_nodes(Host, Owner, Plugins) ->
     Tr = fun (Type) ->
-                case node_call(Type, get_pending_nodes, [Host, Owner])
-                    of
-                  {result, Nodes} -> Nodes;
-                  _ -> []
-                end
-        end,
-    case transaction(fun () ->
-                            {result, lists:flatmap(Tr, Plugins)}
-                    end,
-                    sync_dirty)
-       of
-      {result, Res} -> Res;
-      Err -> Err
+           case node_call(Host, Type, get_pending_nodes, [Host, Owner]) of
+               {result, Nodes} -> Nodes;
+               _ -> []
+           end
+    end,
+    Action = fun() -> {result, lists:flatmap(Tr, Plugins)} end,
+    case transaction(Host, Action, sync_dirty) of
+       {result, Res} -> Res;
+       Err -> Err
     end.
 
-%% @spec (Host, Node, Owner) -> iqRes()
 %% @doc <p>Send a subscription approval form to Owner for all pending
 %% subscriptions on Host and Node.</p>
 send_pending_auth_events(Host, Node, Owner) ->
-    ?DEBUG("Sending pending auth events for ~s on "
-          "~s:~s",
-          [jlib:jid_to_string(Owner), Host, Node]),
-    Action = fun (#pubsub_node{id = NodeID, type = Type}) ->
-                    case lists:member(<<"get-pending">>, features(Type)) of
-                      true ->
-                          case node_call(Type, get_affiliation,
-                                         [NodeID, Owner])
-                              of
-                            {result, owner} ->
-                                node_call(Type, get_node_subscriptions,
-                                          [NodeID]);
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end;
-                      false -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-                    end
-            end,
+    ?DEBUG("Sending pending auth events for ~s on ~s:~s",
+       [jlib:jid_to_string(Owner), Host, Node]),
+    Action = fun (#pubsub_node{id = Nidx, type = Type}) ->
+           case lists:member(<<"get-pending">>, plugin_features(Host, Type)) of
+               true ->
+                   case node_call(Host, Type, get_affiliation, [Nidx, Owner]) of
+                       {result, owner} -> node_call(Host, Type, get_node_subscriptions, [Nidx]);
+                       _ -> {error, ?ERR_FORBIDDEN}
+                   end;
+               false ->
+                   {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {N, Subscriptions}} ->
-         lists:foreach(fun ({J, pending, _SubID}) ->
-                               send_authorization_request(N, jlib:make_jid(J));
-                           ({J, pending}) ->
-                               send_authorization_request(N, jlib:make_jid(J));
-                           (_) -> ok
-                       end,
-                       Subscriptions),
-         #adhoc_response{};
-      Err -> Err
+       {result, {N, Subs}} ->
+           lists:foreach(fun
+                   ({J, pending, _SubId}) -> send_authorization_request(N, jlib:make_jid(J));
+                   ({J, pending}) -> send_authorization_request(N, jlib:make_jid(J));
+                   (_) -> ok
+               end,
+               Subs),
+           #adhoc_response{};
+       Err ->
+           Err
     end.
 
 %%% authorization handling
 
-send_authorization_request(#pubsub_node{owners = Owners, nodeid = {Host, Node}},
-       Subscriber) ->
+send_authorization_request(#pubsub_node{nodeid = {Host, Node}, type = Type, id = Nidx, owners = O},
+           Subscriber) ->
     Lang = <<"en">>,
     Stanza = #xmlel{name = <<"message">>, attrs = [],
+           children =
+           [#xmlel{name = <<"x">>,
+                   attrs =
+                   [{<<"xmlns">>, ?NS_XDATA},
+                       {<<"type">>, <<"form">>}],
                    children =
-                       [#xmlel{name = <<"x">>,
-                               attrs =
-                                   [{<<"xmlns">>, ?NS_XDATA},
-                                    {<<"type">>, <<"form">>}],
-                               children =
-                                   [#xmlel{name = <<"title">>, attrs = [],
-                                           children =
-                                               [{xmlcdata,
-                                                 translate:translate(Lang,
-                                                                     <<"PubSub subscriber request">>)}]},
-                                    #xmlel{name = <<"instructions">>,
-                                           attrs = [],
-                                           children =
-                                               [{xmlcdata,
-                                                 translate:translate(Lang,
-                                                                     <<"Choose whether to approve this entity's "
-                                                                       "subscription.">>)}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>, <<"FORM_TYPE">>},
-                                                {<<"type">>, <<"hidden">>}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             ?NS_PUBSUB_SUB_AUTH}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>, <<"pubsub#node">>},
-                                                {<<"type">>,
-                                                 <<"text-single">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Node ID">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Node}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>,
-                                                 <<"pubsub#subscriber_jid">>},
-                                                {<<"type">>, <<"jid-single">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Subscriber Address">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             jlib:jid_to_string(Subscriber)}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>,
-                                                 <<"pubsub#allow">>},
-                                                {<<"type">>, <<"boolean">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Allow this Jabber ID to subscribe to "
-                                                                       "this pubsub node?">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             <<"false">>}]}]}]}]},
+                   [#xmlel{name = <<"title">>, attrs = [],
+                           children =
+                           [{xmlcdata,
+                                   translate:translate(Lang, <<"PubSub subscriber request">>)}]},
+                       #xmlel{name = <<"instructions">>,
+                           attrs = [],
+                           children =
+                           [{xmlcdata,
+                                   translate:translate(Lang,
+                                       <<"Choose whether to approve this entity's "
+                                           "subscription.">>)}]},
+                       #xmlel{name = <<"field">>,
+                           attrs =
+                           [{<<"var">>, <<"FORM_TYPE">>},
+                               {<<"type">>, <<"hidden">>}],
+                           children =
+                           [#xmlel{name = <<"value">>,
+                                   attrs = [],
+                                   children =
+                                   [{xmlcdata, ?NS_PUBSUB_SUB_AUTH}]}]},
+                       #xmlel{name = <<"field">>,
+                           attrs =
+                           [{<<"var">>, <<"pubsub#node">>},
+                               {<<"type">>,
+                                   <<"text-single">>},
+                               {<<"label">>, translate:translate(Lang, <<"Node ID">>)}],
+                           children =
+                           [#xmlel{name = <<"value">>,
+                                   attrs = [],
+                                   children =
+                                   [{xmlcdata, Node}]}]},
+                       #xmlel{name = <<"field">>,
+                           attrs =
+                           [{<<"var">>,
+                                   <<"pubsub#subscriber_jid">>},
+                               {<<"type">>, <<"jid-single">>},
+                               {<<"label">>,
+                                   translate:translate(Lang, <<"Subscriber Address">>)}],
+                           children =
+                           [#xmlel{name = <<"value">>,
+                                   attrs = [],
+                                   children =
+                                   [{xmlcdata, jlib:jid_to_string(Subscriber)}]}]},
+                       #xmlel{name = <<"field">>,
+                           attrs =
+                           [{<<"var">>,
+                                   <<"pubsub#allow">>},
+                               {<<"type">>, <<"boolean">>},
+                               {<<"label">>,
+                                   translate:translate(Lang,
+                                       <<"Allow this Jabber ID to subscribe to "
+                                           "this pubsub node?">>)}],
+                           children =
+                           [#xmlel{name = <<"value">>,
+                                   attrs = [],
+                                   children =
+                                   [{xmlcdata, <<"false">>}]}]}]}]},
     lists:foreach(fun (Owner) ->
-                         ejabberd_router:route(service_jid(Host),
-                                               jlib:make_jid(Owner), Stanza)
-                 end,
-                 Owners).
+               ejabberd_router:route(service_jid(Host), jlib:make_jid(Owner), Stanza)
+       end,
+       node_owners_action(Host, Type, Nidx, O)).
 
 find_authorization_response(Packet) ->
     #xmlel{children = Els} = Packet,
-    XData1 = lists:map(fun (#xmlel{name = <<"x">>,
-                                  attrs = XAttrs} =
-                               XEl) ->
-                              case xml:get_attr_s(<<"xmlns">>, XAttrs) of
-                                ?NS_XDATA ->
-                                    case xml:get_attr_s(<<"type">>, XAttrs) of
-                                      <<"cancel">> -> none;
-                                      _ -> jlib:parse_xdata_submit(XEl)
-                                    end;
-                                _ -> none
-                              end;
-                          (_) -> none
-                      end,
-                      xml:remove_cdata(Els)),
+    XData1 = lists:map(fun
+               (#xmlel{name = <<"x">>, attrs = XAttrs} = XEl) ->
+                   case xml:get_attr_s(<<"xmlns">>, XAttrs) of
+                       ?NS_XDATA ->
+                           case xml:get_attr_s(<<"type">>, XAttrs) of
+                               <<"cancel">> -> none;
+                               _ -> jlib:parse_xdata_submit(XEl)
+                           end;
+                       _ ->
+                           none
+                   end;
+               (_) ->
+                   none
+           end,
+           xml:remove_cdata(Els)),
     XData = lists:filter(fun (E) -> E /= none end, XData1),
     case XData of
-      [invalid] -> invalid;
-      [] -> none;
-      [XFields] when is_list(XFields) ->
-         ?DEBUG("XFields: ~p", [XFields]),
-         case lists:keysearch(<<"FORM_TYPE">>, 1, XFields) of
-           {value, {_, [?NS_PUBSUB_SUB_AUTH]}} -> XFields;
-           _ -> invalid
-         end
+       [invalid] ->
+           invalid;
+       [] ->
+           none;
+       [XFields] when is_list(XFields) ->
+           ?DEBUG("XFields: ~p", [XFields]),
+           case lists:keysearch(<<"FORM_TYPE">>, 1, XFields) of
+               {value, {_, [?NS_PUBSUB_SUB_AUTH]}} -> XFields;
+               _ -> invalid
+           end
     end.
-%% @spec (Host, JID, Node, Subscription) -> void
-%%      Host = mod_pubsub:host()
-%%      JID = jlib:jid()
-%%      SNode = string()
-%%      Subscription = atom() | {atom(), mod_pubsub:subid()}
+
 %% @doc Send a message to JID with the supplied Subscription
-%% TODO : ask Christophe's opinion
 send_authorization_approval(Host, JID, SNode, Subscription) ->
     SubAttrs = case Subscription of
-%               {S, SID} ->
-%                   [{<<"subscription">>, subscription_to_string(S)},
-%                    {<<"subid">>, SID}];
-                S -> [{<<"subscription">>, subscription_to_string(S)}]
-              end,
-    Stanza = event_stanza([#xmlel{name = <<"subscription">>,
-                                 attrs =
-                                     [{<<"jid">>, jlib:jid_to_string(JID)}
-                                      | nodeAttr(SNode)]
-                                       ++ SubAttrs,
-                                 children = []}]),
+       %{S, SID} ->
+       %    [{<<"subscription">>, subscription_to_string(S)},
+       %     {<<"subid">>, SID}];
+       S -> 
+           [{<<"subscription">>, subscription_to_string(S)}]
+    end,
+    Stanza = event_stanza(<<"subscription">>, 
+           [{<<"jid">>, jlib:jid_to_string(JID)}
+               | nodeAttr(SNode)]
+           ++ SubAttrs),
     ejabberd_router:route(service_jid(Host), JID, Stanza).
 
 handle_authorization_response(Host, From, To, Packet, XFields) ->
     case {lists:keysearch(<<"pubsub#node">>, 1, XFields),
-         lists:keysearch(<<"pubsub#subscriber_jid">>, 1, XFields),
-         lists:keysearch(<<"pubsub#allow">>, 1, XFields)}
-       of
-      {{value, {_, [Node]}}, {value, {_, [SSubscriber]}},
-       {value, {_, [SAllow]}}} ->
-%        Node = string_to_node(SNode),
-         Subscriber = jlib:string_to_jid(SSubscriber),
-         Allow = case SAllow of
-                   <<"1">> -> true;
-                   <<"true">> -> true;
-                   _ -> false
-                 end,
-         Action = fun (#pubsub_node{type = Type, owners = Owners,
-                                    id = NodeId}) ->
-                          IsApprover =
-                              lists:member(jlib:jid_tolower(jlib:jid_remove_resource(From)),
-                                           Owners),
-                          {result, Subscriptions} = node_call(Type,
-                                                              get_subscriptions,
-                                                              [NodeId,
-                                                               Subscriber]),
-                          if not IsApprover -> {error, ?ERR_FORBIDDEN};
-                             true ->
-                                 update_auth(Host, Node, Type, NodeId,
-                                             Subscriber, Allow, Subscriptions)
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {error, Error} ->
-               ejabberd_router:route(To, From,
-                                     jlib:make_error_reply(Packet, Error));
-           {result, {_, _NewSubscription}} ->
-               %% XXX: notify about subscription state change, section 12.11
-               ok;
-           _ ->
-               ejabberd_router:route(To, From,
-                                     jlib:make_error_reply(Packet,
-                                                           ?ERR_INTERNAL_SERVER_ERROR))
-         end;
-      _ ->
-         ejabberd_router:route(To, From,
-                               jlib:make_error_reply(Packet,
-                                                     ?ERR_NOT_ACCEPTABLE))
+           lists:keysearch(<<"pubsub#subscriber_jid">>, 1, XFields),
+           lists:keysearch(<<"pubsub#allow">>, 1, XFields)}
+    of
+       {{value, {_, [Node]}},
+                   {value, {_, [SSubscriber]}},
+                   {value, {_, [SAllow]}}} ->
+           FromLJID = jlib:jid_tolower(jlib:jid_remove_resource(From)),
+           Subscriber = jlib:string_to_jid(SSubscriber),
+           Allow = case SAllow of
+               <<"1">> -> true;
+               <<"true">> -> true;
+               _ -> false
+           end,
+           Action = fun (#pubsub_node{type = Type, id = Nidx, owners = O}) ->
+                   Owners = node_owners_call(Host, Type, Nidx, O),
+                   case lists:member(FromLJID, Owners) of
+                       true ->
+                           {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]),
+                           update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs);
+                       false ->
+                           {error, ?ERR_FORBIDDEN}
+                   end
+           end,
+           case transaction(Host, Node, Action, sync_dirty) of
+               {error, Error} ->
+                   Err = jlib:make_error_reply(Packet, Error),
+                   ejabberd_router:route(To, From, Err);
+               {result, {_, _NewSubscription}} ->
+                   %% XXX: notify about subscription state change, section 12.11
+                   ok;
+               _ ->
+                   Err = jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR),
+                   ejabberd_router:route(To, From, Err)
+           end;
+       _ ->
+           Err = jlib:make_error_reply(Packet, ?ERR_NOT_ACCEPTABLE),
+           ejabberd_router:route(To, From, Err)
     end.
 
-update_auth(Host, Node, Type, NodeId, Subscriber, Allow,
-           Subscriptions) ->
-    Subscription = lists:filter(fun ({pending, _}) -> true;
-                                   (_) -> false
-                               end,
-                               Subscriptions),
-    case Subscription of
-      [{pending, SubID}] ->
-         NewSubscription = case Allow of
-                             true -> subscribed;
-                             false -> none
-                           end,
-         node_call(Type, set_subscriptions,
-                   [NodeId, Subscriber, NewSubscription, SubID]),
-         send_authorization_approval(Host, Subscriber, Node,
-                                     NewSubscription),
-         {result, ok};
-      _ -> {error, ?ERR_UNEXPECTED_REQUEST}
+update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
+    Sub= lists:filter(fun
+               ({pending, _}) -> true;
+               (_) -> false
+           end,
+           Subs),
+    case Sub of
+       [{pending, SubId}] ->
+           NewSub = case Allow of
+               true -> subscribed;
+               false -> none
+           end,
+           node_call(Host, Type, set_subscriptions, [Nidx, Subscriber, NewSub, SubId]),
+           send_authorization_approval(Host, Subscriber, Node, NewSub),
+           {result, ok};
+       _ ->
+           {error, ?ERR_UNEXPECTED_REQUEST}
     end.
 
 -define(XFIELD(Type, Label, Var, Val),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, Type},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, Val}]}]}).
+    #xmlel{name = <<"field">>,
+       attrs = [{<<"type">>, Type},
+           {<<"label">>, translate:translate(Lang, Label)},
+           {<<"var">>, Var}],
+       children = [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, Val}]}]}).
 
 -define(BOOLXFIELD(Label, Var, Val),
-       ?XFIELD(<<"boolean">>, Label, Var,
-               case Val of
-                 true -> <<"1">>;
-                 _ -> <<"0">>
-               end)).
+    ?XFIELD(<<"boolean">>, Label, Var,
+       case Val of
+           true -> <<"1">>;
+           _ -> <<"0">>
+       end)).
 
 -define(STRINGXFIELD(Label, Var, Val),
-       ?XFIELD(<<"text-single">>, Label, Var, Val)).
+    ?XFIELD(<<"text-single">>, Label, Var, Val)).
 
 -define(STRINGMXFIELD(Label, Var, Vals),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, <<"text-multi">>},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, V}]}
-                   || V <- Vals]}).
+    #xmlel{name = <<"field">>,
+       attrs = [{<<"type">>, <<"text-multi">>},
+           {<<"label">>, translate:translate(Lang, Label)},
+           {<<"var">>, Var}],
+       children = [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, V}]}
+           || V <- Vals]}).
 
 -define(XFIELDOPT(Type, Label, Var, Val, Opts),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, Type},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  lists:map(fun (Opt) ->
-                                    #xmlel{name = <<"option">>, attrs = [],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Opt}]}]}
-                            end,
-                            Opts)
-                    ++
-                    [#xmlel{name = <<"value">>, attrs = [],
-                            children = [{xmlcdata, Val}]}]}).
+    #xmlel{name = <<"field">>,
+       attrs = [{<<"type">>, Type},
+           {<<"label">>, translate:translate(Lang, Label)},
+           {<<"var">>, Var}],
+       children = [#xmlel{name = <<"option">>, attrs = [],
+               children = [#xmlel{name = <<"value">>,
+                       attrs = [],
+                       children = [{xmlcdata, Opt}]}]}
+           || Opt <- Opts]
+       ++
+       [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, Val}]}]}).
 
 -define(LISTXFIELD(Label, Var, Val, Opts),
-       ?XFIELDOPT(<<"list-single">>, Label, Var, Val, Opts)).
+    ?XFIELDOPT(<<"list-single">>, Label, Var, Val, Opts)).
 
 -define(LISTMXFIELD(Label, Var, Vals, Opts),
-%% @spec (Host::host(), ServerHost::host(), Node::pubsubNode(), Owner::jid(), NodeType::nodeType()) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
+    #xmlel{name = <<"field">>,
+       attrs = [{<<"type">>, <<"list-multi">>},
+           {<<"label">>, translate:translate(Lang, Label)},
+           {<<"var">>, Var}],
+       children = [#xmlel{name = <<"option">>, attrs = [],
+               children = [#xmlel{name = <<"value">>,
+                       attrs = [],
+                       children = [{xmlcdata, Opt}]}]}
+           || Opt <- Opts]
+       ++
+       [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, Val}]}
+           || Val <- Vals]}).
+
 %% @doc <p>Create new pubsub nodes</p>
 %%<p>In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:</p>
 %%<ul>
 %%<li>The service does not support node creation.</li>
 %%<li>Only entities that are registered with the service are allowed to create nodes but the requesting entity is not registered.</li>
 %%<li>The requesting entity does not have sufficient privileges to create nodes.</li>
-%%<li>The requested NodeID already exists.</li>
-%%<li>The request did not include a NodeID and "instant nodes" are not supported.</li>
+%%<li>The requested Node already exists.</li>
+%%<li>The request did not include a Node and "instant nodes" are not supported.</li>
 %%</ul>
 %%<p>ote: node creation is a particular case, error return code is evaluated at many places:</p>
 %%<ul>
@@ -2554,176 +1771,124 @@ update_auth(Host, Node, Type, NodeId, Subscriber, Allow,
 %%<li>nodetree create_node checks if nodeid already exists</li>
 %%<li>node plugin create_node just sets default affiliation/subscription</li>
 %%</ul>
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, <<"list-multi">>},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  lists:map(fun (Opt) ->
-                                    #xmlel{name = <<"option">>, attrs = [],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Opt}]}]}
-                            end,
-                            Opts)
-                    ++
-                    lists:map(fun (Val) ->
-                                      #xmlel{name = <<"value">>, attrs = [],
-                                             children = [{xmlcdata, Val}]}
-                              end,
-                              Vals)}).
-
--spec(create_node/5 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  Node       :: <<>> | mod_pubsub:nodeId(),
-  Owner      :: jid(),
-  Type       :: binary())
+-spec(create_node/7 ::
+    (
+       Host          :: mod_pubsub:host(),
+       ServerHost    :: binary(),
+       Node        :: <<>> | mod_pubsub:nodeId(),
+       Owner         :: jid(),
+       Type          :: binary(),
+       Access        :: atom(),
+       Configuration :: [xmlel()])
     -> {result, [xmlel(),...]}
     %%%
-     | {error, xmlel()}
-).
-
+    | {error, xmlel()}
+    ).
 create_node(Host, ServerHost, Node, Owner, Type) ->
     create_node(Host, ServerHost, Node, Owner, Type, all, []).
-
--spec(create_node/7 ::
-(
-  Host          :: mod_pubsub:host(),
-  ServerHost    :: binary(),
-  Node          :: <<>> | mod_pubsub:nodeId(),
-  Owner         :: jid(),
-  Type          :: binary(),
-  Access        :: atom(),
-  Configuration :: [xmlel()])
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
 create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
-    case lists:member(<<"instant-nodes">>, features(Type)) of
-      true ->
-         NewNode = randoms:get_string(),
-         case create_node(Host, ServerHost, NewNode, Owner, Type,
-                        Access, Configuration)
-             of
-           {result, _} ->
-               {result,
-                [#xmlel{name = <<"pubsub">>,
-                        attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                        children =
-                            [#xmlel{name = <<"create">>,
-                                    attrs = nodeAttr(NewNode),
-                                    children = []}]}]};
-           Error -> Error
-         end;
-      false ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"nodeid-required">>)}
+    case lists:member(<<"instant-nodes">>, plugin_features(Host, Type)) of
+       true ->
+           Node = randoms:get_string(),
+           case create_node(Host, ServerHost, Node, Owner, Type, Access, Configuration) of
+               {result, _} ->
+                   {result, [#xmlel{name = <<"pubsub">>,
+                               attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                               children = [#xmlel{name = <<"create">>,
+                                       attrs = nodeAttr(Node)}]}]};
+               Error ->
+                   Error
+           end;
+       false ->
+           {error, extended_error(?ERR_NOT_ACCEPTABLE, <<"nodeid-required">>)}
     end;
 create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
     Type = select_type(ServerHost, Host, Node, GivenType),
     ParseOptions = case xml:remove_cdata(Configuration) of
-                    [] -> {result, node_options(Type)};
-                    [#xmlel{name = <<"x">>} = XEl] ->
-                        case jlib:parse_xdata_submit(XEl) of
-                          invalid -> {error, ?ERR_BAD_REQUEST};
-                          XData ->
-                              case set_xoption(Host, XData, node_options(Type))
-                                  of
-                                NewOpts when is_list(NewOpts) ->
-                                    {result, NewOpts};
-                                Err -> Err
-                              end
-                        end;
-                    _ ->
-                        ?INFO_MSG("Node ~p; bad configuration: ~p",
-                                  [Node, Configuration]),
-                        {error, ?ERR_BAD_REQUEST}
-                  end,
+       [] ->
+           {result, node_options(Host, Type)};
+       [#xmlel{name = <<"x">>} = XEl] ->
+           case jlib:parse_xdata_submit(XEl) of
+               invalid ->
+                   {error, ?ERR_BAD_REQUEST};
+               XData ->
+                   case set_xoption(Host, XData, node_options(Host, Type)) of
+                       NewOpts when is_list(NewOpts) -> {result, NewOpts};
+                       Err -> Err
+                   end
+           end;
+       _ ->
+           ?INFO_MSG("Node ~p; bad configuration: ~p", [Node, Configuration]),
+           {error, ?ERR_BAD_REQUEST}
+    end,
     case ParseOptions of
        {result, NodeOptions} ->
-           CreateNode =
-               fun() ->
-                       Parent = case node_call(Type, node_to_path, [Node]) of
-                           {result, [Node]} -> <<>>;
-                           {result, Path} -> element(2, node_call(Type, path_to_node, [lists:sublist(Path, length(Path)-1)]))
-                       end,
-                       Parents = case Parent of
-                           <<>> -> [];
-                           _ -> [Parent]
-                       end,
-                       case node_call(Type, create_node_permission, [Host, ServerHost, Node, Parent, Owner, Access]) of
-                           {result, true} ->
-                               case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
-                                   {ok, NodeId} ->
-                                       ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, Owner]),
-                                       SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
-                                       case node_call(Type, create_node, [NodeId, Owner]) of
-                                           {result, Result} -> {result, {NodeId, SubsByDepth, Result}};
-                                           Error -> Error
-                                       end;
-                                   {error, {virtual, NodeId}} ->
-                                       case node_call(Type, create_node, [NodeId, Owner]) of
-                                           {result, Result} -> {result, {NodeId, [], Result}};
-                                           Error -> Error
-                                       end;
-                                   Error ->
-                                       Error
-                               end;
-                           _ ->
-                               {error, ?ERR_FORBIDDEN}
-                       end
-               end,
+           CreateNode = fun () ->
+                   Parent = case node_call(Host, Type, node_to_path, [Node]) of
+                       {result, [Node]} ->
+                           <<>>;
+                       {result, Path} ->
+                           element(2, node_call(Host, Type, path_to_node, [lists:sublist(Path, length(Path)-1)]))
+                   end,
+                   Parents = case Parent of
+                       <<>> -> [];
+                       _ -> [Parent]
+                   end,
+                   case node_call(Host, Type, create_node_permission,
+                           [Host, ServerHost, Node, Parent, Owner, Access])
+                   of
+                       {result, true} ->
+                           case tree_call(Host, create_node,
+                                   [Host, Node, Type, Owner, NodeOptions, Parents])
+                           of
+                               {ok, Nidx} ->
+                                   SubsByDepth = get_node_subs_by_depth(Host, Node, Owner),
+                                   case node_call(Host, Type, create_node, [Nidx, Owner]) of
+                                       {result, Result} -> {result, {Nidx, SubsByDepth, Result}};
+                                       Error -> Error
+                                   end;
+                               {error, {virtual, Nidx}} ->
+                                   case node_call(Host, Type, create_node, [Nidx, Owner]) of
+                                       {result, Result} -> {result, {Nidx, [], Result}};
+                                       Error -> Error
+                                   end;
+                               Error ->
+                                   Error
+                           end;
+                       _ ->
+                           {error, ?ERR_FORBIDDEN}
+                   end
+           end,
            Reply = [#xmlel{name = <<"pubsub">>,
-                           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                           children = [#xmlel{name = <<"create">>,
-                                   attrs = nodeAttr(Node),
-                                   children = []}]}],
-           case transaction(CreateNode, transaction) of
-               {result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
-                   broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
+                       attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                       children = [#xmlel{name = <<"create">>,
+                               attrs = nodeAttr(Node)}]}],
+           case transaction(Host, CreateNode, transaction) of
+               {result, {Nidx, SubsByDepth, {Result, broadcast}}} ->
+                   broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth),
+                   ejabberd_hooks:run(pubsub_create_node, ServerHost,
+                       [ServerHost, Host, Node, Nidx, NodeOptions]),
                    case Result of
                        default -> {result, Reply};
                        _ -> {result, Result}
                    end;
-               {result, {NodeId, _SubsByDepth, default}} ->
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
-                   {result, Reply};
-               {result, {NodeId, _SubsByDepth, Result}} ->
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
-                   {result, Result};
-               Error ->
-                   %% in case we change transaction to sync_dirty...
-                   %%  node_call(Type, delete_node, [Host, Node]),
-                   %%  tree_call(Host, delete_node, [Host, Node]),
+               {result, {Nidx, _SubsByDepth, Result}} ->
+                   ejabberd_hooks:run(pubsub_create_node, ServerHost,
+                       [ServerHost, Host, Node, Nidx, NodeOptions]),
+                   case Result of
+                       default -> {result, Reply};
+                       _ -> {result, Result}
+                   end;
+               Error ->
+                   %% in case we change transaction to sync_dirty...
+                   %%  node_call(Host, Type, delete_node, [Host, Node]),
+                   %%  tree_call(Host, delete_node, [Host, Node]),
                    Error
            end;
        Error ->
            Error
     end.
 
-%% @spec (Host, Node, Owner) ->
-%%                     {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      Owner = jid()
-%%      Reason = stanzaError()
--spec(delete_node/3 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  Owner :: jid())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
 %% @doc <p>Delete specified node and all childs.</p>
 %%<p>There are several reasons why the node deletion request might fail:</p>
 %%<ul>
@@ -2731,80 +1896,75 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
 %%<li>The node is the root collection node, which cannot be deleted.</li>
 %%<li>The specified node does not exist.</li>
 %%</ul>
+-spec(delete_node/3 ::
+    (
+       Host  :: mod_pubsub:host(),
+       Node  :: mod_pubsub:nodeId(),
+       Owner :: jid())
+    -> {result, [xmlel(),...]}
+    %%%
+    | {error, xmlel()}
+    ).
 delete_node(_Host, <<>>, _Owner) ->
     {error, ?ERR_NOT_ALLOWED};
 delete_node(Host, Node, Owner) ->
-    Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
-                   case node_call(Type, get_affiliation, [NodeId, Owner]) of
-                       {result, owner} ->
-                           ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]),
-                           SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
-                           Removed = tree_call(Host, delete_node, [Host, Node]),
-                           case node_call(Type, delete_node, [Removed]) of
-                               {result, Res} -> {result, {SubsByDepth, Res}};
-                               Error -> Error
-                           end;
-                       _ ->
-                           %% Entity is not an owner
-                           {error, ?ERR_FORBIDDEN}
-                   end
-           end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           case node_call(Host, Type, get_affiliation, [Nidx, Owner]) of
+               {result, owner} ->
+                   SubsByDepth = get_node_subs_by_depth(Host, Node, service_jid(Host)),
+                   Removed = tree_call(Host, delete_node, [Host, Node]),
+                   case node_call(Host, Type, delete_node, [Removed]) of
+                       {result, Res} -> {result, {SubsByDepth, Res}};
+                       Error -> Error
+                   end;
+               _ ->
+                   {error, ?ERR_FORBIDDEN}
+           end
+    end,
     Reply = [],
-    ServerHost = get(server_host),
+    ServerHost = serverhost(Host),
     case transaction(Host, Node, Action, transaction) of
-       {result, {_TNode, {SubsByDepth, {Result, broadcast, Removed}}}} ->
-           lists:foreach(fun({RNode, _RSubscriptions}) ->
-               {RH, RN} = RNode#pubsub_node.nodeid,
-               NodeId = RNode#pubsub_node.id,
-               Type = RNode#pubsub_node.type,
-               Options = RNode#pubsub_node.options,
-               broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth),
-               ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, NodeId])
-           end, Removed),
+       {result, {_, {SubsByDepth, {Result, broadcast, Removed}}}} ->
+           lists:foreach(fun ({RNode, _RSubs}) ->
+                       {RH, RN} = RNode#pubsub_node.nodeid,
+                       RNidx = RNode#pubsub_node.id,
+                       RType = RNode#pubsub_node.type,
+                       ROptions = RNode#pubsub_node.options,
+                       broadcast_removed_node(RH, RN, RNidx, RType, ROptions, SubsByDepth),
+                       ejabberd_hooks:run(pubsub_delete_node,
+                           ServerHost,
+                           [ServerHost, RH, RN, RNidx])
+               end,
+               Removed),
            case Result of
                default -> {result, Reply};
                _ -> {result, Result}
            end;
-       {result, {_TNode, {_, {Result, Removed}}}} ->
-           lists:foreach(fun({RNode, _RSubscriptions}) ->
-               {RH, RN} = RNode#pubsub_node.nodeid,
-               NodeId = RNode#pubsub_node.id,
-               ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, NodeId])
-           end, Removed),
+       {result, {_, {_, {Result, Removed}}}} ->
+           lists:foreach(fun ({RNode, _RSubs}) ->
+                       {RH, RN} = RNode#pubsub_node.nodeid,
+                       RNidx = RNode#pubsub_node.id,
+                       ejabberd_hooks:run(pubsub_delete_node,
+                           ServerHost,
+                           [ServerHost, RH, RN, RNidx])
+               end,
+               Removed),
            case Result of
                default -> {result, Reply};
                _ -> {result, Result}
            end;
-       {result, {TNode, {_, default}}} ->
-           NodeId = TNode#pubsub_node.id,
-           ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, NodeId]),
-           {result, Reply};
        {result, {TNode, {_, Result}}} ->
-           NodeId = TNode#pubsub_node.id,
-           ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, NodeId]),
-           {result, Result};
+           Nidx = TNode#pubsub_node.id,
+           ejabberd_hooks:run(pubsub_delete_node, ServerHost,
+               [ServerHost, Host, Node, Nidx]),
+           case Result of
+               default -> {result, Reply};
+               _ -> {result, Result}
+           end;
        Error ->
            Error
     end.
 
-%% @spec (Host, Node, From, JID, Configuration) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      From = jid()
-%%      JID = jid()
--spec(subscribe_node/5 ::
-(
-  Host          :: mod_pubsub:host(),
-  Node          :: mod_pubsub:nodeId(),
-  From          :: jid(),
-  JID           :: binary(),
-  Configuration :: [xmlel()])
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
 %% @see node_hometree:subscribe_node/5
 %% @doc <p>Accepts or rejects subcription requests on a PubSub node.</p>
 %%<p>There are several reasons why the subscription request might fail:</p>
@@ -2820,109 +1980,94 @@ delete_node(Host, Node, Owner) ->
 %%<li>The node does not support subscriptions.</li>
 %%<li>The node does not exist.</li>
 %%</ul>
+-spec(subscribe_node/5 ::
+    (
+       Host          :: mod_pubsub:host(),
+       Node          :: mod_pubsub:nodeId(),
+       From          :: jid(),
+       JID           :: binary(),
+       Configuration :: [xmlel()])
+    -> {result, [xmlel(),...]}
+    %%%
+    | {error, xmlel()}
+    ).
 subscribe_node(Host, Node, From, JID, Configuration) ->
-    SubOpts = case
-               pubsub_subscription:parse_options_xform(Configuration)
-                 of
-               {result, GoodSubOpts} -> GoodSubOpts;
-               _ -> invalid
-             end,
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J ->
-                   case jlib:jid_tolower(J) of
-                     error -> {<<"">>, <<"">>, <<"">>};
-                     J1 -> J1
-                   end
-                end,
-    Action = fun (#pubsub_node{options = Options,
-                              owners = Owners, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    SubscribeFeature = lists:member(<<"subscribe">>, Features),
-                    OptionsFeature = lists:member(<<"subscription-options">>, Features),
-                    HasOptions = not (SubOpts == []),
-                    SubscribeConfig = get_option(Options, subscribe),
-                    AccessModel = get_option(Options, access_model),
-                    SendLast = get_option(Options, send_last_published_item),
-                    AllowedGroups = get_option(Options, roster_groups_allowed, []),
-                    {PresenceSubscription, RosterGroup} =
-                           get_presence_and_roster_permissions(Host, Subscriber,
-                                       Owners, AccessModel, AllowedGroups),
-                    if not SubscribeFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"subscribe">>)};
-                       not SubscribeConfig ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"subscribe">>)};
-                       HasOptions andalso not OptionsFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"subscription-options">>)};
-                       SubOpts == invalid ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST,
-                                           <<"invalid-options">>)};
-                       true ->
-                           node_call(Type, subscribe_node,
-                                     [NodeId, From, Subscriber, AccessModel,
-                                      SendLast, PresenceSubscription,
-                                      RosterGroup, SubOpts])
-                    end
-            end,
+    SubModule = subscription_plugin(Host),
+    SubOpts = case SubModule:parse_options_xform(Configuration) of
+       {result, GoodSubOpts} -> GoodSubOpts;
+       _ -> invalid
+    end,
+    Subscriber = string_to_ljid(JID),
+    Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx, owners = O}) ->
+           Features = plugin_features(Host, Type),
+           SubscribeFeature = lists:member(<<"subscribe">>, Features),
+           OptionsFeature = lists:member(<<"subscription-options">>, Features),
+           HasOptions = not (SubOpts == []),
+           SubscribeConfig = get_option(Options, subscribe),
+           AccessModel = get_option(Options, access_model),
+           SendLast = get_option(Options, send_last_published_item),
+           AllowedGroups = get_option(Options, roster_groups_allowed, []),
+           if not SubscribeFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscribe">>)};
+               not SubscribeConfig ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscribe">>)};
+               HasOptions andalso not OptionsFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscription-options">>)};
+               SubOpts == invalid ->
+                   {error,
+                       extended_error(?ERR_BAD_REQUEST, <<"invalid-options">>)};
+               true ->
+                   Owners = node_owners_call(Host, Type, Nidx, O),
+                   {PS, RG} = get_presence_and_roster_permissions(Host, Subscriber,
+                           Owners, AccessModel, AllowedGroups),
+                   node_call(Host, Type, subscribe_node,
+                       [Nidx, From, Subscriber, AccessModel,
+                           SendLast, PS, RG, SubOpts])
+           end
+    end,
     Reply = fun (Subscription) ->
-                   SubAttrs = case Subscription of
-                                {subscribed, SubId} ->
-                                    [{<<"subscription">>,
-                                      subscription_to_string(subscribed)},
-                                     {<<"subid">>, SubId}, {<<"node">>, Node}];
-                                Other ->
-                                    [{<<"subscription">>,
-                                      subscription_to_string(Other)},
-                                     {<<"node">>, Node}]
-                              end,
-                   Fields = [{<<"jid">>, jlib:jid_to_string(Subscriber)}
-                             | SubAttrs],
-                   [#xmlel{name = <<"pubsub">>,
-                           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                           children =
-                               [#xmlel{name = <<"subscription">>,
-                                       attrs = Fields, children = []}]}]
+           SubAttrs = case Subscription of
+               {subscribed, SubId} ->
+                   [{<<"subscription">>, subscription_to_string(subscribed)},
+                       {<<"subid">>, SubId}, {<<"node">>, Node}];
+               Other ->
+                   [{<<"subscription">>, subscription_to_string(Other)},
+                       {<<"node">>, Node}]
            end,
+           [#xmlel{name = <<"pubsub">>,
+                   attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                   children = [#xmlel{name = <<"subscription">>,
+                           attrs = [{<<"jid">>, jlib:jid_to_string(Subscriber)}
+                               | SubAttrs]}]}]
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result,
-       {TNode, {Result, subscribed, SubId, send_last}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         send_items(Host, Node, NodeId, Type, Options, Subscriber, last),
-         case Result of
-           default -> {result, Reply({subscribed, SubId})};
-           _ -> {result, Result}
-         end;
-      {result, {_TNode, {default, subscribed, SubId}}} ->
-         {result, Reply({subscribed, SubId})};
-      {result, {_TNode, {Result, subscribed, _SubId}}} ->
-         {result, Result};
-      {result, {TNode, {default, pending, _SubId}}} ->
-         send_authorization_request(TNode, Subscriber),
-         {result, Reply(pending)};
-      {result, {TNode, {Result, pending}}} ->
-         send_authorization_request(TNode, Subscriber),
-         {result, Result};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
+       {result, {TNode, {Result, subscribed, SubId, send_last}}} ->
+           Nidx = TNode#pubsub_node.id,
+           Type = TNode#pubsub_node.type,
+           Options = TNode#pubsub_node.options,
+           send_items(Host, Node, Nidx, Type, Options, Subscriber, last),
+           case Result of
+               default -> {result, Reply({subscribed, SubId})};
+               _ -> {result, Result}
+           end;
+       {result, {_TNode, {default, subscribed, SubId}}} ->
+           {result, Reply({subscribed, SubId})};
+       {result, {_TNode, {Result, subscribed, _SubId}}} ->
+           {result, Result};
+       {result, {TNode, {default, pending, _SubId}}} ->
+           send_authorization_request(TNode, Subscriber),
+           {result, Reply(pending)};
+       {result, {TNode, {Result, pending}}} ->
+           send_authorization_request(TNode, Subscriber),
+           {result, Result};
+       {result, {_, Result}} ->
+           {result, Result};
+       Error -> Error
     end.
 
-%% @spec (Host, Noce, From, JID, SubId) -> {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      From = jid()
-%%      JID = string()
-%%      SubId = string()
-%%      Reason = stanzaError()
 %% @doc <p>Unsubscribe <tt>JID</tt> from the <tt>Node</tt>.</p>
 %%<p>There are several reasons why the unsubscribe request might fail:</p>
 %%<ul>
@@ -2933,41 +2078,28 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
 %%<li>The request specifies a subscription ID that is not valid or current.</li>
 %%</ul>
 -spec(unsubscribe_node/5 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  From  :: jid(),
-  JID   :: binary() | ljid(),
-  SubId :: mod_pubsub:subId())
+    (
+       Host  :: mod_pubsub:host(),
+       Node  :: mod_pubsub:nodeId(),
+       From  :: jid(),
+       JID   :: binary() | ljid(),
+       SubId :: mod_pubsub:subId())
     -> {result, []}
     %%%
-     | {error, xmlel()}
-).
-unsubscribe_node(Host, Node, From, JID, SubId)
-    when is_binary(JID) ->
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J ->
-                   case jlib:jid_tolower(J) of
-                       error -> {<<"">>, <<"">>, <<"">>};
-                       J1 -> J1
-                   end
-                end,
-    unsubscribe_node(Host, Node, From, Subscriber, SubId);
+    | {error, xmlel()}
+    ).
+unsubscribe_node(Host, Node, From, JID, SubId) when is_binary(JID) ->
+    unsubscribe_node(Host, Node, From, string_to_ljid(JID), SubId);
 unsubscribe_node(Host, Node, From, Subscriber, SubId) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, unsubscribe_node,
-                              [NodeId, From, Subscriber, SubId])
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           node_call(Host, Type, unsubscribe_node, [Nidx, From, Subscriber, SubId])
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, default}} -> {result, []};
-%      {result, {_, Result}} -> {result, Result};
-      Error -> Error
+       {result, {_, default}} -> {result, []};
+       %      {result, {_, Result}} -> {result, Result};
+       Error -> Error
     end.
 
-%% @spec (Host::host(), ServerHost::host(), JID::jid(), Node::pubsubNode(), ItemId::string(), Payload::term())  ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
 %% @doc <p>Publish item to a PubSub node.</p>
 %% <p>The permission to publish an item must be verified by the plugin implementation.</p>
 %%<p>There are several reasons why the publish request might fail:</p>
@@ -2980,123 +2112,113 @@ unsubscribe_node(Host, Node, From, Subscriber, SubId) ->
 %%<li>The request does not match the node configuration.</li>
 %%</ul>
 -spec(publish_item/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  Node       :: mod_pubsub:nodeId(),
-  Publisher  :: jid(),
-  ItemId     :: <<>> | mod_pubsub:itemId(),
-  Payload    :: mod_pubsub:payload())
+    (
+       Host       :: mod_pubsub:host(),
+       ServerHost :: binary(),
+       Node       :: mod_pubsub:nodeId(),
+       Publisher  :: jid(),
+       ItemId     :: <<>> | mod_pubsub:itemId(),
+       Payload    :: mod_pubsub:payload())
     -> {result, [xmlel(),...]}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
-       publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, all).
+    publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, all).
 publish_item(Host, ServerHost, Node, Publisher, <<>>, Payload, Access) ->
     publish_item(Host, ServerHost, Node, Publisher, uniqid(), Payload, Access);
 publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, Access) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PublishFeature = lists:member(<<"publish">>, Features),
-                    PublishModel = get_option(Options, publish_model),
-                    DeliverPayloads = get_option(Options, deliver_payloads),
-                    PersistItems = get_option(Options, persist_items),
-                    MaxItems = max_items(Host, Options),
-                    PayloadCount = payload_xmlelements(Payload),
-                    PayloadSize = byte_size(term_to_binary(Payload)) - 2,
-                    PayloadMaxSize = get_option(Options, max_payload_size),
-                    if not PublishFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                       unsupported, <<"publish">>)};
-                       PayloadSize > PayloadMaxSize ->
-                           {error,
-                            extended_error(?ERR_NOT_ACCEPTABLE, <<"payload-too-big">>)};
-                       (PayloadCount == 0) and (Payload == []) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"payload-required">>)};
-                       (PayloadCount > 1) or (PayloadCount == 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)};
-                       (DeliverPayloads == false) and (PersistItems == false) and
-                         (PayloadSize > 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"item-forbidden">>)};
-                       ((DeliverPayloads == true) or (PersistItems == true)) and
-                         (PayloadSize == 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
-                       true ->
-                           node_call(Type, publish_item, [NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload])
-                   end
-           end,
-    ejabberd_hooks:run(pubsub_publish_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId, Payload]),
+    Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
+           Features = plugin_features(Host, Type),
+           PublishFeature = lists:member(<<"publish">>, Features),
+           PublishModel = get_option(Options, publish_model),
+           DeliverPayloads = get_option(Options, deliver_payloads),
+           PersistItems = get_option(Options, persist_items),
+           MaxItems = max_items(Host, Options),
+           PayloadCount = payload_xmlelements(Payload),
+           PayloadSize = byte_size(term_to_binary(Payload)) - 2,
+           PayloadMaxSize = get_option(Options, max_payload_size),
+           if not PublishFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"publish">>)};
+               PayloadSize > PayloadMaxSize ->
+                   {error,
+                       extended_error(?ERR_NOT_ACCEPTABLE, <<"payload-too-big">>)};
+               (PayloadCount == 0) and (Payload == []) ->
+                   {error,
+                       extended_error(?ERR_BAD_REQUEST, <<"payload-required">>)};
+               (PayloadCount > 1) or (PayloadCount == 0) ->
+                   {error,
+                       extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)};
+               (DeliverPayloads == false) and (PersistItems == false) and
+                       (PayloadSize > 0) ->
+                   {error,
+                       extended_error(?ERR_BAD_REQUEST, <<"item-forbidden">>)};
+               ((DeliverPayloads == true) or (PersistItems == true)) and (PayloadSize == 0) ->
+                   {error,
+                       extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
+               true ->
+                   node_call(Host, Type, publish_item,
+                       [Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload])
+           end
+    end,
     Reply = [#xmlel{name = <<"pubsub">>,
-                   attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                   children =
-                       [#xmlel{name = <<"publish">>, attrs = nodeAttr(Node),
-                               children =
-                                   [#xmlel{name = <<"item">>,
-                                           attrs = itemAttr(ItemId),
-                                           children = []}]}]}],
+               attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+               children = [#xmlel{name = <<"publish">>, attrs = nodeAttr(Node),
+                       children = [#xmlel{name = <<"item">>,
+                               attrs = itemAttr(ItemId)}]}]}],
     case transaction(Host, Node, Action, sync_dirty) of
        {result, {TNode, {Result, Broadcast, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
+           Nidx = TNode#pubsub_node.id,
            Type = TNode#pubsub_node.type,
            Options = TNode#pubsub_node.options,
+           BrPayload = case Broadcast of
+               broadcast -> Payload;
+               PluginPayload -> PluginPayload
+           end,
+           ejabberd_hooks:run(pubsub_publish_item, ServerHost,
+               [ServerHost, Node, Publisher, service_jid(Host), ItemId, BrPayload]),
+           set_cached_item(Host, Nidx, ItemId, Publisher, BrPayload),
            case get_option(Options, deliver_notifications) of
-                       true ->
-                               BroadcastPayload = case Broadcast of
-                                       default -> Payload;
-                                       broadcast -> Payload;
-                                       PluginPayload -> PluginPayload
-                               end,
-                               broadcast_publish_item(Host, Node, NodeId, Type, Options,
-                                       Removed, ItemId, jlib:jid_tolower(Publisher),
-                                       BroadcastPayload);
-                       false ->
-                               ok
-               end,
-               set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
+               true ->
+                   broadcast_publish_item(Host, Node, Nidx, Type, Options, ItemId,
+                       Publisher, BrPayload, Removed);
+               false ->
+                   ok
+           end,
            case Result of
                default -> {result, Reply};
                _ -> {result, Result}
            end;
        {result, {TNode, {default, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
+           Nidx = TNode#pubsub_node.id,
            Type = TNode#pubsub_node.type,
            Options = TNode#pubsub_node.options,
-           broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
-           set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
+           broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed),
+           set_cached_item(Host, Nidx, ItemId, Publisher, Payload),
            {result, Reply};
        {result, {TNode, {Result, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
+           Nidx = TNode#pubsub_node.id,
            Type = TNode#pubsub_node.type,
            Options = TNode#pubsub_node.options,
-           broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
-           set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
+           broadcast_retract_items(Host, Node, Nidx, Type, Options, Removed),
+           set_cached_item(Host, Nidx, ItemId, Publisher, Payload),
            {result, Result};
        {result, {_, default}} ->
            {result, Reply};
        {result, {_, Result}} ->
            {result, Result};
        {error, ?ERR_ITEM_NOT_FOUND} ->
-           %% handles auto-create feature
-           %% for automatic node creation. we'll take the default node type:
-           %% first listed into the plugins configuration option, or pep
            Type = select_type(ServerHost, Host, Node),
-           case lists:member(<<"auto-create">>, features(Type)) of
+           case lists:member(<<"auto-create">>, plugin_features(Host, Type)) of
                true ->
                    case create_node(Host, ServerHost, Node, Publisher, Type, Access, []) of
-                       {result, [#xmlel{name = <<"pubsub">>,
-                          attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                          children =
-                              [#xmlel{name = <<"create">>,
-                                      attrs = [{<<"node">>, NewNode}],
-                                      children = []}]}]} ->
-                           publish_item(Host, ServerHost,  NewNode,
-                                   Publisher, ItemId, Payload);
+                       {result,
+                                   [#xmlel{name = <<"pubsub">>,
+                                           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                                           children = [#xmlel{name = <<"create">>,
+                                                   attrs = [{<<"node">>, NewNode}]}]}]} ->
+                           publish_item(Host, ServerHost, NewNode, Publisher, ItemId, Payload);
                        _ ->
                            {error, ?ERR_ITEM_NOT_FOUND}
                    end;
@@ -3107,19 +2229,6 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, Access) ->
            Error
     end.
 
-%% @spec (Host::host(), JID::jid(), Node::pubsubNode(), ItemId::string()) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
--spec(delete_item/4 ::
-(
-  Host      :: mod_pubsub:host(),
-  Node      :: mod_pubsub:nodeId(),
-  Publisher :: jid(),
-  ItemId    :: mod_pubsub:itemId())
-    -> {result, []}
-    %%%
-     | {error, xmlel()}
-).
 %% @doc <p>Delete item from a PubSub node.</p>
 %% <p>The permission to delete an item must be verified by the plugin implementation.</p>
 %%<p>There are several reasons why the item retraction request might fail:</p>
@@ -3131,64 +2240,62 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, Access) ->
 %%<li>The node does not support persistent items.</li>
 %%<li>The service does not support the deletion of items.</li>
 %%</ul>
+-spec(delete_item/4 ::
+    (
+       Host      :: mod_pubsub:host(),
+       Node      :: mod_pubsub:nodeId(),
+       Publisher :: jid(),
+       ItemId    :: mod_pubsub:itemId())
+    -> {result, []}
+    %%%
+    | {error, xmlel()}
+    ).
 delete_item(Host, Node, Publisher, ItemId) ->
     delete_item(Host, Node, Publisher, ItemId, false).
-
-
-delete_item(_, <<"">>, _, _, _) ->
-    {error,
-     extended_error(?ERR_BAD_REQUEST, <<"node-required">>)};
+delete_item(_, <<>>, _, _, _) ->
+    {error, extended_error(?ERR_BAD_REQUEST, <<"node-required">>)};
 delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                    DeleteFeature = lists:member(<<"delete-items">>, Features),
-                    PublishModel = get_option(Options, publish_model),
-                    if %%->   iq_pubsub just does that matchs
-                       %%      %% Request does not specify an item
-                       %%      {error, extended_error(?ERR_BAD_REQUEST, "item-required")};
-                       not PersistentFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       not DeleteFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"delete-items">>)};
-                       true ->
-                           node_call(Type, delete_item,
-                                     [NodeId, Publisher, PublishModel, ItemId])
-                    end
-            end,
+    Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
+           Features = plugin_features(Host, Type),
+           PersistentFeature = lists:member(<<"persistent-items">>, Features),
+           DeleteFeature = lists:member(<<"delete-items">>, Features),
+           PublishModel = get_option(Options, publish_model),
+           if %%->   iq_pubsub just does that matchs
+               %%        %% Request does not specify an item
+               %%        {error, extended_error(?ERR_BAD_REQUEST, "item-required")};
+               not PersistentFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"persistent-items">>)};
+               not DeleteFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"delete-items">>)};
+               true ->
+                   node_call(Host, Type, delete_item, [Nidx, Publisher, PublishModel, ItemId])
+           end
+    end,
     Reply = [],
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {TNode, {Result, broadcast}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         broadcast_retract_items(Host, Node, NodeId, Type,
-                                 Options, [ItemId], ForceNotify),
-         case get_cached_item(Host, NodeId) of
-           #pubsub_item{itemid = {ItemId, NodeId}} ->
-               unset_cached_item(Host, NodeId);
-           _ -> ok
-         end,
-         case Result of
-           default -> {result, Reply};
-           _ -> {result, Result}
-         end;
-      {result, {_, default}} -> {result, Reply};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
+       {result, {TNode, {Result, broadcast}}} ->
+           Nidx = TNode#pubsub_node.id,
+           Type = TNode#pubsub_node.type,
+           Options = TNode#pubsub_node.options,
+           broadcast_retract_items(Host, Node, Nidx, Type, Options, [ItemId], ForceNotify),
+           case get_cached_item(Host, Nidx) of
+               #pubsub_item{itemid = {ItemId, Nidx}} -> unset_cached_item(Host, Nidx);
+               _ -> ok
+           end,
+           case Result of
+               default -> {result, Reply};
+               _ -> {result, Result}
+           end;
+       {result, {_, default}} ->
+           {result, Reply};
+       {result, {_, Result}} ->
+           {result, Result};
+       Error ->
+           Error
     end.
 
-%% @spec (Host, JID, Node) ->
-%%                     {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      JID = jid()
-%%      Reason = stanzaError()
 %% @doc <p>Delete all items of specified node owned by JID.</p>
 %%<p>There are several reasons why the node purge request might fail:</p>
 %%<ul>
@@ -3198,52 +2305,50 @@ delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
 %%<li>The specified node does not exist.</li>
 %%</ul>
 -spec(purge_node/3 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  Owner :: jid())
+    (
+       Host  :: mod_pubsub:host(),
+       Node  :: mod_pubsub:nodeId(),
+       Owner :: jid())
     -> {result, []}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 purge_node(Host, Node, Owner) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PurgeFeature = lists:member(<<"purge-nodes">>, Features),
-                    PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                    PersistentConfig = get_option(Options, persist_items),
-                    if not PurgeFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"purge-nodes">>)};
-                       not PersistentFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       not PersistentConfig ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       true -> node_call(Type, purge_node, [NodeId, Owner])
-                    end
-            end,
+    Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
+           Features = plugin_features(Host, Type),
+           PurgeFeature = lists:member(<<"purge-nodes">>, Features),
+           PersistentFeature = lists:member(<<"persistent-items">>, Features),
+           PersistentConfig = get_option(Options, persist_items),
+           if not PurgeFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"purge-nodes">>)};
+               not PersistentFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"persistent-items">>)};
+               not PersistentConfig ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"persistent-items">>)};
+               true -> node_call(Host, Type, purge_node, [Nidx, Owner])
+           end
+    end,
     Reply = [],
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {TNode, {Result, broadcast}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         broadcast_purge_node(Host, Node, NodeId, Type, Options),
-         unset_cached_item(Host, NodeId),
-         case Result of
-           default -> {result, Reply};
-           _ -> {result, Result}
-         end;
-      {result, {_, default}} -> {result, Reply};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
+       {result, {TNode, {Result, broadcast}}} ->
+           Nidx = TNode#pubsub_node.id,
+           Type = TNode#pubsub_node.type,
+           Options = TNode#pubsub_node.options,
+           broadcast_purge_node(Host, Node, Nidx, Type, Options),
+           unset_cached_item(Host, Nidx),
+           case Result of
+               default -> {result, Reply};
+               _ -> {result, Result}
+           end;
+       {result, {_, default}} ->
+           {result, Reply};
+       {result, {_, Result}} ->
+           {result, Result};
+       Error ->
+           Error
     end.
 
 %% @doc <p>Return the items of a given node.</p>
@@ -3251,886 +2356,722 @@ purge_node(Host, Node, Owner) ->
 %% <p>The permission are not checked in this function.</p>
 %% @todo We probably need to check that the user doing the query has the right
 %% to read the items.
--spec(get_items/6 ::
-(
-  Host      :: mod_pubsub:host(),
-  Node      :: mod_pubsub:nodeId(),
-  From      :: jid(),
-  SubId     :: mod_pubsub:subId(),
-  SMaxItems :: binary(),
-  ItemIDs   :: [mod_pubsub:itemId()])
+-spec(get_items/7 ::
+    (
+       Host      :: mod_pubsub:host(),
+       Node      :: mod_pubsub:nodeId(),
+       From      :: jid(),
+       SubId     :: mod_pubsub:subId(),
+       SMaxItems :: binary(),
+       ItemIds   :: [mod_pubsub:itemId()],
+       Rsm       :: none | rsm_in())
     -> {result, [xmlel(),...]}
     %%%
-     | {error, xmlel()}
-).
-get_items(Host, Node, From, SubId, SMaxItems, ItemIDs) ->
-    MaxItems = if SMaxItems == <<"">> ->
-                     get_max_items_node(Host);
-                 true ->
-                     case catch jlib:binary_to_integer(SMaxItems) of
-                       {'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
-                       Val -> Val
-                     end
-              end,
+    | {error, xmlel()}
+    ).
+get_items(Host, Node, From, SubId, SMaxItems, ItemIds, RSM) ->
+    MaxItems = if SMaxItems == <<>> ->
+           get_max_items_node(Host);
+       true ->
+           case catch jlib:binary_to_integer(SMaxItems) of
+               {'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
+               Val -> Val
+           end
+    end,
     case MaxItems of
-      {error, Error} -> {error, Error};
-      _ ->
-         Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId,
-                                    owners = Owners}) ->
-                          Features = features(Type),
-                          RetreiveFeature = lists:member(<<"retrieve-items">>, Features),
-                          PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                          AccessModel = get_option(Options, access_model),
-                          AllowedGroups = get_option(Options, roster_groups_allowed, []),
-                          {PresenceSubscription, RosterGroup} =
-                              get_presence_and_roster_permissions(Host, From, Owners,
-                                           AccessModel, AllowedGroups),
-                          if not RetreiveFeature ->
-                                 {error,
-                                  extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                 unsupported,
-                                                 <<"retrieve-items">>)};
-                             not PersistentFeature ->
-                                 {error,
-                                  extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                 unsupported,
-                                                 <<"persistent-items">>)};
-                             true ->
-                                 node_call(Type, get_items,
-                                           [NodeId, From, AccessModel,
-                                            PresenceSubscription, RosterGroup,
-                                            SubId])
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Items}} ->
-               SendItems = case ItemIDs of
-                             [] -> Items;
-                             _ ->
-                                 lists:filter(fun (#pubsub_item{itemid =
-                                                                    {ItemId,
-                                                                     _}}) ->
-                                                      lists:member(ItemId,
-                                                                   ItemIDs)
-                                              end,
-                                              Items)
-                           end,
-               {result,
-                [#xmlel{name = <<"pubsub">>,
-                        attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                        children =
-                            [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                                    children =
-                                        itemsEls(lists:sublist(SendItems,
-                                                               MaxItems))}]}]};
-           Error -> Error
-         end
+       {error, Error} ->
+           {error, Error};
+       _ ->
+           Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx, owners = O}) ->
+                   Features = plugin_features(Host, Type),
+                   RetreiveFeature = lists:member(<<"retrieve-items">>, Features),
+                   PersistentFeature = lists:member(<<"persistent-items">>, Features),
+                   AccessModel = get_option(Options, access_model),
+                   AllowedGroups = get_option(Options, roster_groups_allowed, []),
+                   if not RetreiveFeature ->
+                           {error,
+                               extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"retrieve-items">>)};
+                       not PersistentFeature ->
+                           {error,
+                               extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"persistent-items">>)};
+                       true ->
+                           Owners = node_owners_call(Host, Type, Nidx, O),
+                           {PS, RG} = get_presence_and_roster_permissions(Host, From, Owners,
+                                   AccessModel, AllowedGroups),
+                           node_call(Host, Type, get_items,
+                               [Nidx, From, AccessModel, PS, RG, SubId, RSM])
+                   end
+           end,
+           case transaction(Host, Node, Action, sync_dirty) of
+               {result, {_, {Items, RsmOut}}} ->
+                   SendItems = case ItemIds of
+                       [] ->
+                           Items;
+                       _ ->
+                           lists:filter(fun (#pubsub_item{itemid = {ItemId, _}}) ->
+                                       lists:member(ItemId, ItemIds)
+                               end,
+                               Items)
+                   end,
+                   {result,
+                       [#xmlel{name = <<"pubsub">>,
+                               attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                               children =
+                               [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
+                                       children = itemsEls(lists:sublist(SendItems, MaxItems))}
+                                   | jlib:rsm_encode(RsmOut)]}]};
+               Error ->
+                   Error
+           end
     end.
 
 get_items(Host, Node) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, get_items, [NodeId, service_jid(Host)])
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           node_call(Host, Type, get_items, [Nidx, service_jid(Host), none]) 
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Items}} -> Items;
-      Error -> Error
+       {result, {_, {Items, _}}} -> Items;
+       Error -> Error
     end.
 
 get_item(Host, Node, ItemId) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, get_item, [NodeId, ItemId])
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           node_call(Host, Type, get_item, [Nidx, ItemId])
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Items}} -> Items;
-      Error -> Error
+       {result, {_, Items}} -> Items;
+       Error -> Error
     end.
 
-get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
+get_allowed_items_call(Host, Nidx, From, Type, Options, Owners) ->
+    case get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, none) of
+       {result, {I, none}} -> {result, I};
+       Error -> Error
+    end.
+get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) ->
     AccessModel = get_option(Options, access_model),
     AllowedGroups = get_option(Options, roster_groups_allowed, []),
-    {PresenceSubscription, RosterGroup} =
-           get_presence_and_roster_permissions(Host, From, Owners, AccessModel,
-               AllowedGroups),
-    node_call(Type, get_items,
-             [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined]).
-
-%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
-%%      Host = pubsubHost()
-%%      Node = pubsubNode()
-%%      NodeId = pubsubNodeId()
-%%      Type = pubsubNodeType()
-%%      Options = mod_pubsub:nodeOptions()
-%%      LJID = {U, S, []}
-%%      Number = last | integer()
+    {PS, RG} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
+    node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, undefined, RSM]).
+
+get_last_item(Host, Type, Nidx, LJID) ->
+    case get_cached_item(Host, Nidx) of
+       undefined -> get_last_item(Host, Type, Nidx, LJID, gen_mod:db_type(serverhost(Host), ?MODULE));
+       LastItem -> LastItem
+    end.
+get_last_item(Host, Type, Nidx, LJID, mnesia) ->
+    case node_action(Host, Type, get_items, [Nidx, LJID, none]) of
+       {result, {[LastItem|_], _}} -> LastItem;
+       _ -> undefined
+    end;
+get_last_item(Host, Type, Nidx, LJID, odbc) ->
+    case node_action(Host, Type, get_last_items, [Nidx, LJID, 1]) of
+       {result, [LastItem]} -> LastItem;
+       _ -> undefined
+    end;
+get_last_item(_Host, _Type, _Nidx, _LJID, _) ->
+    undefined.
+
+get_last_items(Host, Type, Nidx, LJID, Number) ->
+    get_last_items(Host, Type, Nidx, LJID, Number, gen_mod:db_type(serverhost(Host), ?MODULE)).
+get_last_items(Host, Type, Nidx, LJID, Number, mnesia) ->
+    case node_action(Host, Type, get_items, [Nidx, LJID, none]) of
+       {result, {Items, _}} -> lists:sublist(Items, Number);
+       _ -> []
+    end;
+get_last_items(Host, Type, Nidx, LJID, Number, odbc) ->
+    case node_action(Host, Type, get_last_items, [Nidx, LJID, Number]) of
+       {result, Items} -> Items;
+       _ -> []
+    end;
+get_last_items(_Host, _Type, _Nidx, _LJID, _Number, _) ->
+    [].
+
 %% @doc <p>Resend the items of a node to the user.</p>
 %% @todo use cache-last-item feature
-send_items(Host, Node, NodeId, Type, Options, LJID, last) ->
-    case get_cached_item(Host, NodeId) of
-      undefined ->
-         send_items(Host, Node, NodeId, Type, Options, LJID, 1);
-      LastItem ->
-         {ModifNow, ModifUSR} =
-             LastItem#pubsub_item.modification,
-         Stanza = event_stanza_with_delay([#xmlel{name =
-                                                      <<"items">>,
-                                                  attrs = nodeAttr(Node),
-                                                  children =
-                                                      itemsEls([LastItem])}],
-                                          ModifNow, ModifUSR),
-         dispatch_items(Host, LJID, Node, Options, Stanza)
+send_items(Host, Node, Nidx, Type, Options, LJID, last) ->
+    case get_last_item(Host, Type, Nidx, LJID) of
+       undefined ->
+           ok;
+       LastItem ->
+           Stanza = items_event_stanza(Node, [LastItem]),
+           dispatch_items(Host, LJID, Node, Options, Stanza)
     end;
-send_items(Host, Node, NodeId, Type, Options, LJID, Number) ->
-    ToSend = case node_action(Host, Type, get_items,
-                             [NodeId, LJID])
-                of
-              {result, []} -> [];
-              {result, Items} ->
-                  case Number of
-                    N when N > 0 -> lists:sublist(Items, N);
-                    _ -> Items
-                  end;
-              _ -> []
-            end,
-    Stanza = case ToSend of
-              [] ->
-                  undefined;
-              [LastItem] ->
-                  {ModifNow, ModifUSR} =
-                      LastItem#pubsub_item.modification,
-                  event_stanza_with_delay([#xmlel{name = <<"items">>,
-                                                  attrs = nodeAttr(Node),
-                                                  children =
-                                                      itemsEls(ToSend)}],
-                                          ModifNow, ModifUSR);
-              _ ->
-                  event_stanza([#xmlel{name = <<"items">>,
-                                       attrs = nodeAttr(Node),
-                                       children = itemsEls(ToSend)}])
-            end,
+send_items(Host, Node, Nidx, Type, Options, LJID, Number) when Number > 0 ->
+    Stanza = items_event_stanza(Node, get_last_items(Host, Type, Nidx, Number, LJID)),
+    dispatch_items(Host, LJID, Node, Options, Stanza);
+send_items(Host, Node, _Nidx, _Type, Options, LJID, _) ->
+    Stanza = items_event_stanza(Node, []),
     dispatch_items(Host, LJID, Node, Options, Stanza).
 
--spec(dispatch_items/5 ::
-(
-  From    :: mod_pubsub:host(),
-  To      :: jid(),
-  Node    :: mod_pubsub:nodeId(),
-  Options :: mod_pubsub:nodeOptions(),
-  Stanza  :: xmlel() | undefined)
-    -> any()
-).
-
-dispatch_items(_From, _To, _Node, _Options, _Stanza = undefined) -> ok;
 dispatch_items({FromU, FromS, FromR} = From, {ToU, ToS, ToR} = To, Node,
-              Options, BaseStanza) ->
-    NotificationType = get_option(Options, notification_type, headline),
-    Stanza = add_message_type(BaseStanza, NotificationType),
+           Options, Stanza) ->
     C2SPid = case ejabberd_sm:get_session_pid(ToU, ToS, ToR) of
-              ToPid when is_pid(ToPid) -> ToPid;
-              _ ->
-                  R = user_resource(FromU, FromS, FromR),
-                  case ejabberd_sm:get_session_pid(FromU, FromS, R) of
-                    FromPid when is_pid(FromPid) -> FromPid;
-                    _ -> undefined
-                  end
-            end,
+       ToPid when is_pid(ToPid) -> ToPid;
+       _ ->
+           R = user_resource(FromU, FromS, FromR),
+           case ejabberd_sm:get_session_pid(FromU, FromS, R) of
+               FromPid when is_pid(FromPid) -> FromPid;
+               _ -> undefined
+           end
+    end,
     if C2SPid == undefined -> ok;
-       true ->
-          ejabberd_c2s:send_filtered(C2SPid,
-                                     {pep_message, <<Node/binary, "+notify">>},
-                                     service_jid(From), jlib:make_jid(To),
-                                     Stanza)
+       true ->
+           NotificationType = get_option(Options, notification_type, headline),
+           Message = add_message_type(Stanza, NotificationType),
+           ejabberd_c2s:send_filtered(C2SPid,
+               {pep_message, <<Node/binary, "+notify">>},
+               service_jid(From), jlib:make_jid(To),
+               Message)
     end;
-dispatch_items(From, To, _Node, Options, BaseStanza) ->
+dispatch_items(From, To, _Node, Options, Stanza) ->
     NotificationType = get_option(Options, notification_type, headline),
-    Stanza = add_message_type(BaseStanza, NotificationType),
-    ejabberd_router:route(service_jid(From), jlib:make_jid(To), Stanza).
-
-%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
-%%      Host = host()
-%%      JID = jid()
-%%      Plugins = [Plugin::string()]
-%%      Reason = stanzaError()
-%%      Response = [pubsubIQResponse()]
+    Message = add_message_type(Stanza, NotificationType),
+    ejabberd_router:route(service_jid(From), jlib:make_jid(To), Message).
+
 %% @doc <p>Return the list of affiliations as an XMPP response.</p>
 -spec(get_affiliations/4 ::
-(
-  Host    :: mod_pubsub:host(),
-  Node    :: mod_pubsub:nodeId(),
-  JID     :: jid(),
-  Plugins :: [binary()])
+    (
+       Host    :: mod_pubsub:host(),
+       Node    :: mod_pubsub:nodeId(),
+       JID     :: jid(),
+       Plugins :: [binary()])
     -> {result, [xmlel(),...]}
     %%%
-     | {error, xmlel()}
-).
-get_affiliations(Host, <<>>, JID, Plugins)
-    when is_list(Plugins) ->
-    Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                Features = features(Type),
-                                RetrieveFeature =
-                                    lists:member(<<"retrieve-affiliations">>, Features),
-                                if not RetrieveFeature ->
-                                       {{error,
-                                         extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                        unsupported,
-                                                        <<"retrieve-affiliations">>)},
-                                        Acc};
-                                   true ->
-                                       {result, Affiliations} =
-                                           node_action(Host, Type,
-                                                       get_entity_affiliations,
-                                                       [Host, JID]),
-                                       {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
-    case Result of
-      {ok, Affiliations} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, Node}},
-                                        Affiliation}) ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}
-                                                       | nodeAttr(Node)],
-                                                  children = []}]
-                                  end,
-                                  lists:usort(lists:flatten(Affiliations))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
-    end;
-get_affiliations(Host, NodeId, JID, Plugins)
-    when is_list(Plugins) ->
-    Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                Features = features(Type),
-                                RetrieveFeature =
-                                    lists:member(<<"retrieve-affiliations">>,
-                                                 Features),
-                                if not RetrieveFeature ->
-                                       {{error,
-                                         extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                        unsupported,
-                                                        <<"retrieve-affiliations">>)},
-                                        Acc};
-                                   true ->
-                                       {result, Affiliations} =
-                                           node_action(Host, Type,
-                                                       get_entity_affiliations,
-                                                       [Host, JID]),
-                                       {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
+    | {error, xmlel()}
+    ).
+get_affiliations(Host, Node, JID, Plugins) when is_list(Plugins) ->
+    Result = lists:foldl( fun (Type, {Status, Acc}) ->
+                   Features = plugin_features(Host, Type),
+                   RetrieveFeature = lists:member(<<"retrieve-affiliations">>, Features),
+                   if not RetrieveFeature ->
+                           {{error,
+                                   extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
+                                       unsupported, <<"retrieve-affiliations">>)},
+                               Acc};
+                       true ->
+                           {result, Affs} = node_action(Host, Type,
+                                   get_entity_affiliations,
+                                   [Host, JID]),
+                           {Status, [Affs | Acc]}
+                   end
+           end,
+           {ok, []}, Plugins),
     case Result of
-      {ok, Affiliations} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, Node}},
-                                        Affiliation})
-                                          when NodeId == Node ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}
-                                                       | nodeAttr(Node)],
-                                                  children = []}];
-                                      (_) -> []
-                                  end,
-                                  lists:usort(lists:flatten(Affiliations))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
+       {ok, Affs} ->
+           Entities = lists:flatmap(fun
+                       ({_, none}) ->
+                           [];
+                       ({#pubsub_node{nodeid = {_, NodeId}}, Aff}) ->
+                           if (Node == <<>>) or (Node == NodeId) ->
+                                   [#xmlel{name = <<"affiliation">>,
+                                           attrs = [{<<"affiliation">>, affiliation_to_string(Aff)}
+                                               | nodeAttr(NodeId)]}];
+                               true ->
+                                   []
+                           end;
+                       (_) ->
+                           []
+                   end,
+                   lists:usort(lists:flatten(Affs))),
+           {result,
+               [#xmlel{name = <<"pubsub">>,
+                       attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                       children = [#xmlel{name = <<"affiliations">>, attrs = [],
+                               children = Entities}]}]};
+       {Error, _} ->
+           Error
     end.
 
 -spec(get_affiliations/3 ::
-(
-  Host :: mod_pubsub:host(),
-  Node :: mod_pubsub:nodeId(),
-  JID  :: jid())
+    (
+       Host :: mod_pubsub:host(),
+       Node :: mod_pubsub:nodeId(),
+       JID  :: jid())
     -> {result, [xmlel(),...]}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 get_affiliations(Host, Node, JID) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    RetrieveFeature =
-                        lists:member(<<"modify-affiliations">>, Features),
-                    {result, Affiliation} = node_call(Type, get_affiliation,
-                                                      [NodeId, JID]),
-                    if not RetrieveFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"modify-affiliations">>)};
-                       Affiliation /= owner -> {error, ?ERR_FORBIDDEN};
-                       true -> node_call(Type, get_node_affiliations, [NodeId])
-                    end
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           Features = plugin_features(Host, Type),
+           RetrieveFeature = lists:member(<<"modify-affiliations">>, Features),
+           {result, Affiliation} = node_call(Host, Type, get_affiliation, [Nidx, JID]),
+           if not RetrieveFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"modify-affiliations">>)};
+               Affiliation /= owner ->
+                   {error, ?ERR_FORBIDDEN};
+               true ->
+                   node_call(Host, Type, get_node_affiliations, [Nidx])
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, []}} -> {error, ?ERR_ITEM_NOT_FOUND};
-      {result, {_, Affiliations}} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({AJID, Affiliation}) ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}],
-                                                  children = []}]
-                                  end,
-                                  Affiliations),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>,
-                              attrs = nodeAttr(Node), children = Entities}]}]};
-      Error -> Error
+       {result, {_, []}} ->
+           {error, ?ERR_ITEM_NOT_FOUND};
+       {result, {_, Affs}} ->
+           Entities = lists:flatmap(fun
+                       ({_, none}) ->
+                           [];
+                       ({AJID, Aff}) ->
+                           [#xmlel{name = <<"affiliation">>,
+                                   attrs = [{<<"jid">>, jlib:jid_to_string(AJID)},
+                                       {<<"affiliation">>, affiliation_to_string(Aff)}]}]
+                   end,
+                   Affs),
+           {result,
+               [#xmlel{name = <<"pubsub">>,
+                       attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
+                       children = [#xmlel{name = <<"affiliations">>,
+                               attrs = nodeAttr(Node), children = Entities}]}]};
+       Error ->
+           Error
     end.
 
 -spec(set_affiliations/4 ::
-(
-  Host        :: mod_pubsub:host(),
-  Node        :: mod_pubsub:nodeId(),
-  From        :: jid(),
-  EntitiesEls :: [xmlel()])
+    (
+       Host        :: mod_pubsub:host(),
+       Node        :: mod_pubsub:nodeId(),
+       From        :: jid(),
+       EntitiesEls :: [xmlel()])
     -> {result, []}
     %%%
-     | {error, xmlel()}
-).
+    | {error, xmlel()}
+    ).
 set_affiliations(Host, Node, From, EntitiesEls) ->
     Owner = jlib:jid_tolower(jlib:jid_remove_resource(From)),
-    Entities = lists:foldl(fun (El, Acc) ->
-                                  case Acc of
-                                    error -> error;
-                                    _ ->
-                                        case El of
-                                          #xmlel{name = <<"affiliation">>,
-                                                 attrs = Attrs} ->
-                                              JID =
-                                                  jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
-                                                                                    Attrs)),
-                                              Affiliation =
-                                                  string_to_affiliation(xml:get_attr_s(<<"affiliation">>,
-                                                                                       Attrs)),
-                                              if (JID == error) or
-                                                   (Affiliation == false) ->
-                                                     error;
-                                                 true ->
-                                                     [{jlib:jid_tolower(JID),
-                                                       Affiliation}
-                                                      | Acc]
-                                              end
-                                        end
-                                  end
-                          end,
-                          [], EntitiesEls),
+    Entities = lists:foldl(fun
+               (_, error) ->
+                   error;
+               (El, Acc) ->
+                   case El of
+                       #xmlel{name = <<"affiliation">>, attrs = Attrs} ->
+                           JID = jlib:string_to_jid(xml:get_attr_s(<<"jid">>, Attrs)),
+                           Affiliation = string_to_affiliation(xml:get_attr_s(<<"affiliation">>, Attrs)),
+                           if (JID == error) or (Affiliation == false) -> error;
+                               true -> [{jlib:jid_tolower(JID), Affiliation} | Acc]
+                           end
+                   end
+           end,
+           [], EntitiesEls),
     case Entities of
-      error -> {error, ?ERR_BAD_REQUEST};
-      _ ->
-         Action = fun (#pubsub_node{owners = Owners, type = Type,
-                                    id = NodeId} =
-                           N) ->
-                          case lists:member(Owner, Owners) of
-                            true ->
-                                OwnerJID = jlib:make_jid(Owner),
-                                FilteredEntities = case Owners of
-                                                     [Owner] ->
-                                                         [E
-                                                          || E <- Entities,
-                                                             element(1, E) =/=
-                                                               OwnerJID];
-                                                     _ -> Entities
-                                                   end,
-                                lists:foreach(fun ({JID, Affiliation}) ->
-                                                      node_call(Type,
-                                                                set_affiliation,
-                                                                [NodeId, JID,
-                                                                 Affiliation]),
-                                                      case Affiliation of
-                                                        owner ->
-                                                            NewOwner =
-                                                                jlib:jid_tolower(jlib:jid_remove_resource(JID)),
-                                                            NewOwners =
-                                                                [NewOwner
-                                                                 | Owners],
-                                                            tree_call(Host,
-                                                                      set_node,
-                                                                      [N#pubsub_node{owners
-                                                                                         =
-                                                                                         NewOwners}]);
-                                                        none ->
-                                                            OldOwner =
-                                                                jlib:jid_tolower(jlib:jid_remove_resource(JID)),
-                                                            case
-                                                              lists:member(OldOwner,
-                                                                           Owners)
-                                                                of
-                                                              true ->
-                                                                  NewOwners =
-                                                                      Owners --
-                                                                        [OldOwner],
-                                                                  tree_call(Host,
-                                                                            set_node,
-                                                                            [N#pubsub_node{owners
-                                                                                               =
-                                                                                               NewOwners}]);
-                                                              _ -> ok
-                                                            end;
-                                                        _ -> ok
-                                                      end
-                                              end,
-                                              FilteredEntities),
-                                {result, []};
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
+       error ->
+           {error, ?ERR_BAD_REQUEST};
+       _ ->
+           Action = fun (#pubsub_node{type = Type, id = Nidx, owners = O} = N) ->
+                   Owners = node_owners_call(Host, Type, Nidx, O),
+                   case lists:member(Owner, Owners) of
+                       true ->
+                           OwnerJID = jlib:make_jid(Owner),
+                           FilteredEntities = case Owners of
+                               [Owner] -> [E || E <- Entities, element(1, E) =/= OwnerJID];
+                               _ -> Entities
+                           end,
+                           lists:foreach(fun ({JID, Affiliation}) ->
+                                       node_call(Host, Type, set_affiliation, [Nidx, JID, Affiliation]),
+                                       case Affiliation of
+                                           owner ->
+                                               NewOwner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+                                               NewOwners = [NewOwner | Owners],
+                                               tree_call(Host,
+                                                   set_node,
+                                                   [N#pubsub_node{owners = NewOwners}]);
+                                           none ->
+                                               OldOwner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
+                                               case lists:member(OldOwner, Owners) of
+                                                   true ->
+                                                       NewOwners = Owners -- [OldOwner],
+                                                       tree_call(Host,
+                                                           set_node,
+                                                           [N#pubsub_node{owners = NewOwners}]);
+                                                   _ ->
+                                                       ok
+                                               end;
+                                           _ ->
+                                               ok
+                                       end
+                               end,
+                               FilteredEntities),
+                           {result, []};
+                       _ ->
+                           {error, ?ERR_FORBIDDEN}
+                   end
+           end,
+           case transaction(Host, Node, Action, sync_dirty) of
+               {result, {_, Result}} -> {result, Result};
+               Other -> Other
+           end
     end.
 
-get_options(Host, Node, JID, SubID, Lang) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
-                    case lists:member(<<"subscription-options">>, features(Type)) of
-                      true ->
-                          get_options_helper(JID, Lang, Node, NodeID, SubID, Type);
-                      false ->
-                          {error,
-                           extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                          unsupported,
-                                          <<"subscription-options">>)}
-                    end
-            end,
+get_options(Host, Node, JID, SubId, Lang) ->
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           case lists:member(<<"subscription-options">>, plugin_features(Host, Type)) of
+               true ->
+                   get_options_helper(Host, JID, Lang, Node, Nidx, SubId, Type);
+               false ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscription-options">>)}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_Node, XForm}} -> {result, [XForm]};
-      Error -> Error
+       {result, {_Node, XForm}} -> {result, [XForm]};
+       Error -> Error
     end.
 
-get_options_helper(JID, Lang, Node, NodeID, SubID, Type) ->
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J -> case jlib:jid_tolower(J) of
-                   error -> {<<"">>, <<"">>, <<"">>};
-                   J1 -> J1
-                  end
-                end,
-    {result, Subs} = node_call(Type, get_subscriptions,
-                              [NodeID, Subscriber]),
-    SubIDs = lists:foldl(fun ({subscribed, SID}, Acc) ->
-                                [SID | Acc];
-                            (_, Acc) -> Acc
-                        end,
-                        [], Subs),
-    case {SubID, SubIDs} of
-      {_, []} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE, <<"not-subscribed">>)};
-      {<<>>, [SID]} ->
-         read_sub(Subscriber, Node, NodeID, SID, Lang);
-      {<<>>, _} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
-      {_, _} ->
-         ValidSubId = lists:member(SubID, SubIDs),
-         if ValidSubId ->
-                read_sub(Subscriber, Node, NodeID, SubID, Lang);
-            true ->
-                {error,
-                 extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
-          end
+get_options_helper(Host, JID, Lang, Node, Nidx, SubId, Type) ->
+    Subscriber = string_to_ljid(JID),
+    {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]),
+    SubIds = [Id || {Sub, Id} <- Subs, Sub == subscribed],
+    case {SubId, SubIds} of
+       {_, []} ->
+           {error,
+               extended_error(?ERR_NOT_ACCEPTABLE, <<"not-subscribed">>)};
+       {<<>>, [SID]} ->
+           read_sub(Host, Node, Nidx, Subscriber, SID, Lang);
+       {<<>>, _} ->
+           {error,
+               extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
+       {_, _} ->
+           ValidSubId = lists:member(SubId, SubIds),
+           if ValidSubId ->
+                   read_sub(Host, Node, Nidx, Subscriber, SubId, Lang);
+               true ->
+                   {error,
+                       extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
+           end
     end.
 
-read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
-    Children = case pubsub_subscription:get_subscription(Subscriber, NodeID, SubID) of
+read_sub(Host, Node, Nidx, Subscriber, SubId, Lang) ->
+    SubModule = subscription_plugin(Host),
+    Children = case SubModule:get_subscription(Subscriber, Nidx, SubId) of
        {error, notfound} ->
            [];
        {result, #pubsub_subscription{options = Options}} ->
-           {result, XdataEl} = pubsub_subscription:get_options_xform(Lang, Options),
+           {result, XdataEl} = SubModule:get_options_xform(Lang, Options),
            [XdataEl]
     end,
     OptionsEl = #xmlel{name = <<"options">>,
-                       attrs =
-                           [{<<"jid">>, jlib:jid_to_string(Subscriber)},
-                           {<<"subid">>, SubID}
-                           | nodeAttr(Node)],
-                       children = Children},
+           attrs = [{<<"jid">>, jlib:jid_to_string(Subscriber)},
+               {<<"subid">>, SubId}
+               | nodeAttr(Node)],
+           children = Children},
     PubsubEl = #xmlel{name = <<"pubsub">>,
-                       attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                       children = [OptionsEl]},
+           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+           children = [OptionsEl]},
     {result, PubsubEl}.
 
-set_options(Host, Node, JID, SubID, Configuration) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
-                    case lists:member(<<"subscription-options">>,
-                                      features(Type))
-                        of
-                      true ->
-                          set_options_helper(Configuration, JID, NodeID, SubID,
-                                             Type);
-                      false ->
-                          {error,
-                           extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                          unsupported,
-                                          <<"subscription-options">>)}
-                    end
-            end,
+set_options(Host, Node, JID, SubId, Configuration) ->
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           case lists:member(<<"subscription-options">>, plugin_features(Host, Type)) of
+               true ->
+                   set_options_helper(Host, Configuration, JID, Nidx, SubId, Type);
+               false ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"subscription-options">>)}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_Node, Result}} -> {result, Result};
-      Error -> Error
+       {result, {_Node, Result}} -> {result, Result};
+       Error -> Error
     end.
 
-set_options_helper(Configuration, JID, NodeID, SubID, Type) ->
-    SubOpts = case pubsub_subscription:parse_options_xform(Configuration) of
-               {result, GoodSubOpts} -> GoodSubOpts;
-               _ -> invalid
-             end,
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J -> jlib:jid_tolower(J)
-                end,
-    {result, Subs} = node_call(Type, get_subscriptions,
-                              [NodeID, Subscriber]),
-    SubIDs = lists:foldl(fun ({subscribed, SID}, Acc) ->
-                                [SID | Acc];
-                            (_, Acc) -> Acc
-                        end,
-                        [], Subs),
-    case {SubID, SubIDs} of
-      {_, []} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"not-subscribed">>)};
-      {<<>>, [SID]} ->
-         write_sub(Subscriber, NodeID, SID, SubOpts);
-      {<<>>, _} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"subid-required">>)};
-      {_, _} -> write_sub(Subscriber, NodeID, SubID, SubOpts)
+set_options_helper(Host, Configuration, JID, Nidx, SubId, Type) ->
+    SubModule = subscription_plugin(Host),
+    SubOpts = case SubModule:parse_options_xform(Configuration) of
+       {result, GoodSubOpts} -> GoodSubOpts;
+       _ -> invalid
+    end,
+    Subscriber = string_to_ljid(JID),
+    {result, Subs} = node_call(Host, Type, get_subscriptions, [Nidx, Subscriber]),
+    SubIds = [Id || {Sub, Id} <- Subs, Sub == subscribed],
+    case {SubId, SubIds} of
+       {_, []} ->
+           {error,
+               extended_error(?ERR_NOT_ACCEPTABLE, <<"not-subscribed">>)};
+       {<<>>, [SID]} ->
+           write_sub(Host, Nidx, Subscriber, SID, SubOpts);
+       {<<>>, _} ->
+           {error,
+               extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
+       {_, _} ->
+           write_sub(Host, Nidx, Subscriber, SubId, SubOpts)
     end.
 
-write_sub(_Subscriber, _NodeID, _SubID, invalid) ->
-    {error, extended_error(?ERR_BAD_REQUEST, <<"invalid-options">>)};
-write_sub(Subscriber, NodeID, SubID, Options) ->
-    case pubsub_subscription:set_subscription(Subscriber, NodeID, SubID, Options) of
-       {error, notfound} ->
-           {error, extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)};
-       {result, _} ->
-           {result, []}
+write_sub(_Host, _Nidx, _Subscriber, _SubId, invalid) ->
+    {error,
+       extended_error(?ERR_BAD_REQUEST, <<"invalid-options">>)};
+write_sub(_Host, _Nidx, _Subscriber, _SubId, []) ->
+    {result, []};
+write_sub(Host, Nidx, Subscriber, SubId, Options) ->
+    SubModule = subscription_plugin(Host),
+    case SubModule:set_subscription(Subscriber, Nidx, SubId, Options) of
+       {result, _} -> {result, []};
+       {error, _} -> {error, extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
     end.
 
 %% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      JID = jid()
-%%      Plugins = [Plugin::string()]
-%%      Reason = stanzaError()
-%%      Response = [pubsubIQResponse()]
+%%         Host = host()
+%%         Node = pubsubNode()
+%%         JID = jid()
+%%         Plugins = [Plugin::string()]
+%%         Reason = stanzaError()
+%%         Response = [pubsubIQResponse()]
 %% @doc <p>Return the list of subscriptions as an XMPP response.</p>
 get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
-    Result = lists:foldl(
-              fun(Type, {Status, Acc}) ->
-                      Features = features(Type),
-                      RetrieveFeature = lists:member(<<"retrieve-subscriptions">>, Features),
-                      if
-                          not RetrieveFeature ->
-                              %% Service does not support retreive subscriptions
-                              {{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"retrieve-subscriptions">>)}, Acc};
-                          true ->
-                              Subscriber = jlib:jid_remove_resource(JID),
-                              {result, Subscriptions} = node_action(Host, Type, get_entity_subscriptions, [Host, Subscriber]),
-                              {Status, [Subscriptions|Acc]}
-                      end
-              end, {ok, []}, Plugins),
+    Result = lists:foldl(fun (Type, {Status, Acc}) ->
+                   Features = plugin_features(Host, Type),
+                   RetrieveFeature = lists:member(<<"retrieve-subscriptions">>, Features),
+                   if not RetrieveFeature ->
+                           {{error,
+                                   extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
+                                       unsupported, <<"retrieve-subscriptions">>)},
+                               Acc};
+                       true ->
+                           Subscriber = jlib:jid_remove_resource(JID),
+                           {result, Subs} = node_action(Host, Type,
+                                   get_entity_subscriptions,
+                                   [Host, Subscriber]),
+                           {Status, [Subs | Acc]}
+                   end
+           end,
+           {ok, []}, Plugins),
     case Result of
-      {ok, Subscriptions} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end;
-                                      ({_, none, _}) -> [];
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription, SubID, SubJID}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subid">>,
-                                                              SubID},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subid">>,
-                                                              SubID},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end;
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription, SubJID}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end
-                                  end,
-                                  lists:usort(lists:flatten(Subscriptions))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"subscriptions">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
+       {ok, Subs} ->
+           Entities = lists:flatmap(fun
+                       ({_, none}) ->
+                           [];
+                       ({#pubsub_node{nodeid = {_, SubsNode}}, Sub}) ->
+                           case Node of
+                               <<>> ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"subscription">>, subscription_to_string(Sub)}
+                                               | nodeAttr(SubsNode)]}];
+                               SubsNode ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"subscription">>, subscription_to_string(Sub)}]}];
+                               _ ->
+                                   []
+                           end;
+                       ({_, none, _}) ->
+                           [];
+                       ({#pubsub_node{nodeid = {_, SubsNode}}, Sub, SubId, SubJID}) ->
+                           case Node of
+                               <<>> ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"jid">>, jlib:jid_to_string(SubJID)},
+                                               {<<"subid">>, SubId},
+                                               {<<"subscription">>, subscription_to_string(Sub)}
+                                               | nodeAttr(SubsNode)]}];
+                               SubsNode ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"jid">>, jlib:jid_to_string(SubJID)},
+                                               {<<"subid">>, SubId},
+                                               {<<"subscription">>, subscription_to_string(Sub)}]}];
+                               _ ->
+                                   []
+                           end;
+                       ({#pubsub_node{nodeid = {_, SubsNode}}, Sub, SubJID}) ->
+                           case Node of
+                               <<>> ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"jid">>, jlib:jid_to_string(SubJID)},
+                                               {<<"subscription">>, subscription_to_string(Sub)}
+                                               | nodeAttr(SubsNode)]}];
+                               SubsNode ->
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs =
+                                           [{<<"jid">>, jlib:jid_to_string(SubJID)},
+                                               {<<"subscription">>, subscription_to_string(Sub)}]}];
+                               _ ->
+                                   []
+                           end
+                   end,
+                   lists:usort(lists:flatten(Subs))),
+           {result,
+               [#xmlel{name = <<"pubsub">>,
+                       attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                       children = [#xmlel{name = <<"subscriptions">>, attrs = [],
+                               children = Entities}]}]};
+       {Error, _} ->
+           Error
     end.
 
 get_subscriptions(Host, Node, JID) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    RetrieveFeature =
-                        lists:member(<<"manage-subscriptions">>, Features),
-                    {result, Affiliation} = node_call(Type, get_affiliation,
-                                                      [NodeId, JID]),
-                    if not RetrieveFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"manage-subscriptions">>)};
-                       Affiliation /= owner -> {error, ?ERR_FORBIDDEN};
-                       true ->
-                           node_call(Type, get_node_subscriptions, [NodeId])
-                    end
-            end,
+    Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
+           Features = plugin_features(Host, Type),
+           RetrieveFeature = lists:member(<<"manage-subscriptions">>, Features),
+           {result, Affiliation} = node_call(Host, Type, get_affiliation, [Nidx, JID]),
+           if not RetrieveFeature ->
+                   {error,
+                       extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"manage-subscriptions">>)};
+               Affiliation /= owner ->
+                   {error, ?ERR_FORBIDDEN};
+               true ->
+                   node_call(Host, Type, get_node_subscriptions, [Nidx])
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Subscriptions}} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({_, pending, _}) -> [];
-                                      ({AJID, Subscription}) ->
-                                          [#xmlel{name = <<"subscription">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"subscription">>,
-                                                        subscription_to_string(Subscription)}],
-                                                  children = []}];
-                                      ({AJID, Subscription, SubId}) ->
-                                          [#xmlel{name = <<"subscription">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"subscription">>,
-                                                        subscription_to_string(Subscription)},
-                                                       {<<"subid">>, SubId}],
-                                                  children = []}]
-                                  end,
-                                  Subscriptions),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                  children =
-                      [#xmlel{name = <<"subscriptions">>,
-                              attrs = nodeAttr(Node), children = Entities}]}]};
-      Error -> Error
+       {result, {_, Subs}} ->
+           Entities = lists:flatmap(fun
+                       ({_, none}) ->
+                           [];
+                       ({_, pending, _}) ->
+                           [];
+                       ({AJID, Sub}) ->
+                           [#xmlel{name = <<"subscription">>,
+                                   attrs =
+                                   [{<<"jid">>, jlib:jid_to_string(AJID)},
+                                       {<<"subscription">>, subscription_to_string(Sub)}]}];
+                       ({AJID, Sub, SubId}) ->
+                           [#xmlel{name = <<"subscription">>,
+                                   attrs =
+                                   [{<<"jid">>, jlib:jid_to_string(AJID)},
+                                       {<<"subscription">>, subscription_to_string(Sub)},
+                                       {<<"subid">>, SubId}]}]
+                   end,
+                   Subs),
+           {result,
+               [#xmlel{name = <<"pubsub">>,
+                       attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
+                       children = [#xmlel{name = <<"subscriptions">>,
+                               attrs = nodeAttr(Node),
+                               children = Entities}]}]};
+       Error ->
+           Error
     end.
 
+get_subscriptions_for_send_last(Host, PType, mnesia, JID, LJID, BJID) ->
+    {result, Subs} = node_action(Host, PType,
+           get_entity_subscriptions,
+           [Host, JID]),
+    [{Node, Sub, SubId, SubJID}
+       || {Node, Sub, SubId, SubJID} <- Subs,
+           Sub =:= subscribed, (SubJID == LJID) or (SubJID == BJID),
+           match_option(Node, send_last_published_item, on_sub_and_presence)];
+get_subscriptions_for_send_last(Host, PType, odbc, JID, LJID, BJID) ->
+    case catch node_action(Host, PType,
+           get_entity_subscriptions_for_send_last,
+           [Host, JID])
+    of
+       {result, Subs} ->
+           [{Node, Sub, SubId, SubJID}
+               || {Node, Sub, SubId, SubJID} <- Subs,
+                   Sub =:= subscribed, (SubJID == LJID) or (SubJID == BJID)];
+       _ ->
+           []
+    end;
+get_subscriptions_for_send_last(_Host, _PType, _, _JID, _LJID, _BJID) ->
+    [].
+
 set_subscriptions(Host, Node, From, EntitiesEls) ->
-    Owner =
-       jlib:jid_tolower(jlib:jid_remove_resource(From)),
-    Entities = lists:foldl(fun (El, Acc) ->
-                                  case Acc of
-                                    error -> error;
-                                    _ ->
-                                        case El of
-                                          #xmlel{name = <<"subscription">>,
-                                                 attrs = Attrs} ->
-                                              JID =
-                                                  jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
-                                                                                    Attrs)),
-                                              Subscription =
-                                                  string_to_subscription(xml:get_attr_s(<<"subscription">>,
-                                                                                        Attrs)),
-                                              SubId =
-                                                  xml:get_attr_s(<<"subid">>,
-                                                                 Attrs),
-                                              if (JID == error) or
-                                                   (Subscription == false) ->
-                                                     error;
-                                                 true ->
-                                                     [{jlib:jid_tolower(JID),
-                                                       Subscription, SubId}
-                                                      | Acc]
-                                              end
-                                        end
-                                  end
-                          end,
-                          [], EntitiesEls),
+    Owner = jlib:jid_tolower(jlib:jid_remove_resource(From)),
+    Entities = lists:foldl(fun
+               (_, error) ->
+                   error;
+               (El, Acc) ->
+                   case El of
+                       #xmlel{name = <<"subscription">>, attrs = Attrs} ->
+                           JID = jlib:string_to_jid(xml:get_attr_s(<<"jid">>, Attrs)),
+                           Sub = string_to_subscription(xml:get_attr_s(<<"subscription">>, Attrs)),
+                           SubId = xml:get_attr_s(<<"subid">>, Attrs),
+                           if (JID == error) or (Sub == false) -> error;
+                               true -> [{jlib:jid_tolower(JID), Sub, SubId} | Acc]
+                           end
+                   end
+           end,
+           [], EntitiesEls),
     case Entities of
-      error -> {error, ?ERR_BAD_REQUEST};
-      _ ->
-         Notify = fun (JID, Sub, _SubId) ->
-                          Stanza = #xmlel{name = <<"message">>, attrs = [],
-                                          children =
-                                              [#xmlel{name = <<"pubsub">>,
-                                                      attrs =
-                                                          [{<<"xmlns">>,
-                                                            ?NS_PUBSUB}],
-                                                      children =
-                                                          [#xmlel{name =
-                                                                      <<"subscription">>,
-                                                                  attrs =
-                                                                      [{<<"jid">>,
-                                                                        jlib:jid_to_string(JID)},
-                                                                       {<<"subscription">>,
-                                                                        subscription_to_string(Sub)}
-                                                                       | nodeAttr(Node)],
-                                                                  children =
-                                                                      []}]}]},
-                          ejabberd_router:route(service_jid(Host),
-                                                jlib:make_jid(JID), Stanza)
-                  end,
-         Action = fun (#pubsub_node{owners = Owners, type = Type,
-                                    id = NodeId}) ->
-                          case lists:member(Owner, Owners) of
-                            true ->
-                                Result = lists:foldl(fun ({JID, Subscription,
-                                                           SubId},
-                                                          Acc) ->
-                                                             case
-                                                               node_call(Type,
-                                                                         set_subscriptions,
-                                                                         [NodeId,
-                                                                          JID,
-                                                                          Subscription,
-                                                                          SubId])
-                                                                 of
-                                                               {error, Err} ->
-                                                                   [{error,
-                                                                     Err}
-                                                                    | Acc];
-                                                               _ ->
-                                                                   Notify(JID,
-                                                                          Subscription,
-                                                                          SubId),
-                                                                   Acc
-                                                             end
-                                                     end,
-                                                     [], Entities),
-                                case Result of
-                                  [] -> {result, []};
-                                  _ -> {error, ?ERR_NOT_ACCEPTABLE}
-                                end;
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
+       error ->
+           {error, ?ERR_BAD_REQUEST};
+       _ ->
+           Notify = fun (JID, Sub, _SubId) ->
+                   Stanza = #xmlel{name = <<"message">>, attrs = [],
+                           children =
+                           [#xmlel{name = <<"pubsub">>,
+                                   attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
+                                   children =
+                                   [#xmlel{name = <<"subscription">>,
+                                           attrs = [{<<"jid">>, jlib:jid_to_string(JID)},
+                                               {<<"subscription">>, subscription_to_string(Sub)}
+                                               | nodeAttr(Node)]}]}]},
+                   ejabberd_router:route(service_jid(Host), jlib:make_jid(JID), Stanza)
+           end,
+           Action = fun (#pubsub_node{type = Type, id = Nidx, owners = O}) ->
+                   Owners = node_owners_call(Host, Type, Nidx, O),
+                   case lists:member(Owner, Owners) of
+                       true ->
+                           Result = lists:foldl(fun ({JID, Sub, SubId}, Acc) ->
+                                           case
+                                               node_call(Host, Type,
+                                                   set_subscriptions,
+                                                   [Nidx, JID, Sub, SubId])
+                                           of
+                                               {error, Err} ->
+                                                   [{error, Err} | Acc];
+                                               _ ->
+                                                   Notify(JID, Sub, SubId),
+                                                   Acc
+                                           end
+                                   end,
+                                   [], Entities),
+                           case Result of
+                               [] -> {result, []};
+                               _ -> {error, ?ERR_NOT_ACCEPTABLE}
+                           end;
+                       _ ->
+                           {error, ?ERR_FORBIDDEN}
+                   end
+           end,
+           case transaction(Host, Node, Action, sync_dirty) of
+               {result, {_, Result}} -> {result, Result};
+               Other -> Other
+           end
     end.
 
 -spec(get_presence_and_roster_permissions/5 ::
-(
-  Host          :: mod_pubsub:host(),
-  From          :: ljid(),
-  Owners        :: [ljid(),...],
-  AccessModel   :: mod_pubsub:accessModel(),
-  AllowedGroups :: [binary()])
+    (
+       Host          :: mod_pubsub:host(),
+       From          :: ljid(),
+       Owners        :: [ljid(),...],
+       AccessModel   :: mod_pubsub:accessModel(),
+       AllowedGroups :: [binary()])
     -> {PresenceSubscription::boolean(), RosterGroup::boolean()}
-).
-
+    ).
 get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
     if (AccessModel == presence) or (AccessModel == roster) ->
-          case Host of
-            {User, Server, _} ->
-                get_roster_info(User, Server, From, AllowedGroups);
-            _ ->
-                [{OUser, OServer, _} | _] = Owners,
-                get_roster_info(OUser, OServer, From, AllowedGroups)
-          end;
-       true -> {true, true}
+           case Host of
+               {User, Server, _} ->
+                   get_roster_info(User, Server, From, AllowedGroups);
+               _ ->
+                   [{OUser, OServer, _} | _] = Owners,
+                   get_roster_info(OUser, OServer, From, AllowedGroups)
+           end;
+       true ->
+           {true, true}
     end.
 
-%% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
-%%    -> {PresenceSubscription, RosterGroup}
-get_roster_info(_, _, {<<"">>, <<"">>, _}, _) ->
+get_roster_info(_, _, {<<>>, <<>>, _}, _) ->
     {false, false};
-get_roster_info(OwnerUser, OwnerServer,
-               {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
-    {Subscription, Groups} =
-       ejabberd_hooks:run_fold(roster_get_jid_info,
-                               OwnerServer, {none, []},
-                               [OwnerUser, OwnerServer,
-                                {SubscriberUser, SubscriberServer, <<"">>}]),
+get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
+    LJID = {SubscriberUser, SubscriberServer, <<>>},
+    {Subscription, Groups} = ejabberd_hooks:run_fold(roster_get_jid_info,
+           OwnerServer, {none, []},
+           [OwnerUser, OwnerServer, LJID]),
     PresenceSubscription = Subscription == both orelse
-                            Subscription == from orelse
-                              {OwnerUser, OwnerServer} ==
-                                {SubscriberUser, SubscriberServer},
+       Subscription == from orelse
+       {OwnerUser, OwnerServer} == {SubscriberUser, SubscriberServer},
     RosterGroup = lists:any(fun (Group) ->
-                                   lists:member(Group, AllowedGroups)
-                           end,
-                           Groups),
+                   lists:member(Group, AllowedGroups)
+           end,
+           Groups),
     {PresenceSubscription, RosterGroup};
-get_roster_info(OwnerUser, OwnerServer, JID,
-               AllowedGroups) ->
-    get_roster_info(OwnerUser, OwnerServer,
-                   jlib:jid_tolower(JID), AllowedGroups).
+get_roster_info(OwnerUser, OwnerServer, JID, AllowedGroups) ->
+    get_roster_info(OwnerUser, OwnerServer, jlib:jid_tolower(JID), AllowedGroups).
 
 string_to_affiliation(<<"owner">>) -> owner;
 string_to_affiliation(<<"publisher">>) -> publisher;
@@ -4141,8 +3082,7 @@ string_to_affiliation(_) -> false.
 
 string_to_subscription(<<"subscribed">>) -> subscribed;
 string_to_subscription(<<"pending">>) -> pending;
-string_to_subscription(<<"unconfigured">>) ->
-    unconfigured;
+string_to_subscription(<<"unconfigured">>) -> unconfigured;
 string_to_subscription(<<"none">>) -> none;
 string_to_subscription(_) -> false.
 
@@ -4158,157 +3098,170 @@ subscription_to_string(unconfigured) -> <<"unconfigured">>;
 subscription_to_string(_) -> <<"none">>.
 
 -spec(service_jid/1 ::
-(
-  Host :: mod_pubsub:host())
+    (
+       Host :: mod_pubsub:host())
     -> jid()
-).
+    ).
 service_jid(Host) ->
     case Host of
-      {U, S, _} -> {jid, U, S, <<"">>, U, S, <<"">>};
-      _ -> {jid, <<"">>, Host, <<"">>, <<"">>, Host, <<"">>}
+       {U, S, _} -> {jid, U, S, <<>>, U, S, <<>>};
+       _ -> {jid, <<>>, Host, <<>>, <<>>, Host, <<>>}
     end.
 
 %% @spec (LJID, NotifyType, Depth, NodeOptions, SubOptions) -> boolean()
-%%     LJID = jid()
-%%     NotifyType = items | nodes
-%%     Depth = integer()
-%%     NodeOptions = [{atom(), term()}]
-%%     SubOptions = [{atom(), term()}]
+%%        LJID = jid()
+%%        NotifyType = items | nodes
+%%        Depth = integer()
+%%        NodeOptions = [{atom(), term()}]
+%%        SubOptions = [{atom(), term()}]
 %% @doc <p>Check if a notification must be delivered or not based on
 %% node and subscription options.</p>
-is_to_deliver(LJID, NotifyType, Depth, NodeOptions,
-             SubOptions) ->
+is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) ->
     sub_to_deliver(LJID, NotifyType, Depth, SubOptions)
-      andalso node_to_deliver(LJID, NodeOptions).
+    andalso node_to_deliver(LJID, NodeOptions).
 
 sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) ->
     lists:all(fun (Option) ->
-                     sub_option_can_deliver(NotifyType, Depth, Option)
-             end,
-             SubOptions).
+               sub_option_can_deliver(NotifyType, Depth, Option)
+       end,
+       SubOptions).
+
+node_to_deliver(LJID, NodeOptions) ->
+    presence_can_deliver(LJID, get_option(NodeOptions, presence_based_delivery)).
 
 sub_option_can_deliver(items, _, {subscription_type, nodes}) -> false;
 sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false;
-sub_option_can_deliver(_, _, {subscription_depth, all})      -> true;
-sub_option_can_deliver(_, Depth, {subscription_depth, D})    -> Depth =< D;
-sub_option_can_deliver(_, _, {deliver, false})        -> false;
-sub_option_can_deliver(_, _, {expire, When})            -> now() < When;
-sub_option_can_deliver(_, _, _)                              -> true.
-
-node_to_deliver(LJID, NodeOptions) ->
-    PresenceDelivery = get_option(NodeOptions, presence_based_delivery),
-    presence_can_deliver(LJID, PresenceDelivery).
+sub_option_can_deliver(_, _, {subscription_depth, all}) -> true;
+sub_option_can_deliver(_, Depth, {subscription_depth, D}) -> Depth =< D;
+sub_option_can_deliver(_, _, {deliver, false}) -> false;
+sub_option_can_deliver(_, _, {expire, When}) -> now() < When;
+sub_option_can_deliver(_, _, _) -> true.
 
 -spec(presence_can_deliver/2 ::
-(
-  Entity :: ljid(),
-  _      :: boolean())
+    (
+       Entity :: ljid(),
+       _      :: boolean())
     -> boolean()
-).
-presence_can_deliver(_, false) -> true;
+    ).
+presence_can_deliver(_, false) ->
+    true;
 presence_can_deliver({User, Server, Resource}, true) ->
     case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
-    [] -> false;
-    Ss ->
-       lists:foldl(fun(_, true) -> true;
-                      ({session, _, _ , _, undefined, _}, _Acc) -> false;
-                      ({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
-                          case Resource of
-                              <<>> -> true;
-                              R -> true;
-                              _ -> false
-                          end
-       end, false, Ss)
+       [] ->
+           false;
+       Ss ->
+           lists:foldl(fun
+                   (_, true) ->
+                       true;
+                   ({session, _, _, _, undefined, _}, _Acc) ->
+                       false;
+                   ({session, {_, _, R}, _, _, _Priority, _}, _Acc) ->
+                       case Resource of
+                           <<>> -> true;
+                           R -> true;
+                           _ -> false
+                       end
+               end,
+               false, Ss)
     end.
 
 -spec(state_can_deliver/2 ::
-(
-  Entity::ljid(),
-  SubOptions :: mod_pubsub:subOptions() | [])
+    (
+       Entity::ljid(),
+       SubOptions :: mod_pubsub:subOptions() | [])
     -> [ljid()]
-).
+    ).
 state_can_deliver({U, S, R}, []) -> [{U, S, R}];
 state_can_deliver({U, S, R}, SubOptions) ->
-    %% Check SubOptions for 'show_values'
-    case lists:keysearch('show_values', 1, SubOptions) of
-  %% If not in suboptions, item can be delivered, case doesn't apply
-  false -> [{U, S, R}];
-  %% If in a suboptions ...
-  {_, {_, ShowValues}} ->
-      %% Get subscriber resources
-      Resources = case R of
-    %% If the subscriber JID is a bare one, get all its resources
-    <<>> -> user_resources(U, S);
-    %% If the subscriber JID is a full one, use its resource
-    R  -> [R]
-      end,
-      %% For each resource, test if the item is allowed to be delivered
-      %% based on resource state
-      lists:foldl(
-        fun(Resource, Acc) ->
-          get_resource_state({U, S, Resource}, ShowValues, Acc)
-        end, [], Resources)
+    case lists:keysearch(show_values, 1, SubOptions) of
+       %% If not in suboptions, item can be delivered, case doesn't apply
+       false -> [{U, S, R}];
+       %% If in a suboptions ...
+       {_, {_, ShowValues}} ->
+           Resources = case R of
+               %% If the subscriber JID is a bare one, get all its resources
+               <<>> -> user_resources(U, S);
+               %% If the subscriber JID is a full one, use its resource
+               R -> [R]
+           end,
+           lists:foldl(fun (Resource, Acc) ->
+                       get_resource_state({U, S, Resource}, ShowValues, Acc)
+               end,
+               [], Resources)
     end.
 
 -spec(get_resource_state/3 ::
-(
-  Entity     :: ljid(),
-  ShowValues :: [binary()],
-  JIDs       :: [ljid()])
+    (
+       Entity     :: ljid(),
+       ShowValues :: [binary()],
+       JIDs       :: [ljid()])
     -> [ljid()]
-).
+    ).
 get_resource_state({U, S, R}, ShowValues, JIDs) ->
     case ejabberd_sm:get_session_pid(U, S, R) of
-  %% If no PID, item can be delivered
-  none -> lists:append([{U, S, R}], JIDs);
-  %% If PID ...
-  Pid ->
-      %% Get user resource state
-      %% TODO : add a catch clause
-      Show = case ejabberd_c2s:get_presence(Pid) of
-    {_, _, <<"available">>, _} -> <<"online">>;
-    {_, _, State, _}           -> State
-      end,
-      %% Is current resource state listed in 'show-values' suboption ?
-      case lists:member(Show, ShowValues) of %andalso Show =/= "online" of
-    %% If yes, item can be delivered
-    true  -> lists:append([{U, S, R}], JIDs);
-    %% If no, item can't be delivered
-    false -> JIDs
-      end
+       none ->
+           %% If no PID, item can be delivered
+           lists:append([{U, S, R}], JIDs);
+       Pid ->
+           Show = case ejabberd_c2s:get_presence(Pid) of
+               {_, _, <<"available">>, _} -> <<"online">>;
+               {_, _, State, _} -> State
+           end,
+           case lists:member(Show, ShowValues) of
+               %% If yes, item can be delivered
+               true -> lists:append([{U, S, R}], JIDs);
+               %% If no, item can't be delivered
+               false -> JIDs
+           end
     end.
 
 -spec(payload_xmlelements/1 ::
-(
-  Payload :: mod_pubsub:payload())
+    (
+       Payload :: mod_pubsub:payload())
     -> Count :: non_neg_integer()
-).
-%% @spec (Payload) -> int()
-%%     Payload = term()
-%% @doc <p>Count occurence of XML elements in payload.</p>
-payload_xmlelements(Payload) -> payload_xmlelements(Payload, 0).
+    ).
+payload_xmlelements(Payload) ->
+    payload_xmlelements(Payload, 0).
+
 payload_xmlelements([], Count) -> Count;
 payload_xmlelements([#xmlel{} | Tail], Count) ->
     payload_xmlelements(Tail, Count + 1);
 payload_xmlelements([_ | Tail], Count) ->
     payload_xmlelements(Tail, Count).
 
-%% @spec (Els) -> stanza()
-%%     Els = [xmlelement()]
-%% @doc <p>Build pubsub event stanza</p>
+items_event_stanza(Node, Items) ->
+    MoreEls = case Items of
+       [LastItem] ->
+           {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
+           DateTime = calendar:now_to_datetime(ModifNow),
+           {T_string, Tz_string} = jlib:timestamp_to_iso(DateTime, utc),
+           [#xmlel{name = <<"delay">>, attrs = [{<<"xmlns">>, ?NS_DELAY},
+                       {<<"from">>, jlib:jid_to_string(ModifUSR)},
+                       {<<"stamp">>, <<T_string/binary, Tz_string/binary>>}],
+                   children = [{xmlcdata, <<>>}]}];
+       _ ->
+           []
+    end,
+    event_stanza_with_els([#xmlel{name = <<"items">>,
+               attrs = [{<<"type">>, <<"headline">>} | nodeAttr(Node)],
+               children = itemsEls(Items)}],
+       MoreEls).
+
 event_stanza(Els) ->
+    event_stanza_with_els(Els, []).
+event_stanza_with_els(Els, MoreEls) ->
     #xmlel{name = <<"message">>, attrs = [],
-          children =
-              [#xmlel{name = <<"event">>,
-                      attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
-                      children = Els}]}.
+       children = [#xmlel{name = <<"event">>,
+               attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
+               children = Els}
+           | MoreEls]}.
 
-event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
-    jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow).
+event_stanza(Event, EvAttr) ->
+    event_stanza_with_els([#xmlel{name = Event, attrs = EvAttr}], []).
 
 %%%%%% broadcast functions
 
-broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, From, Payload) ->
+broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payload, Removed) ->
     case get_collection_subscriptions(Host, Node) of
        SubsByDepth when is_list(SubsByDepth) ->
            Content = case get_option(NodeOptions, deliver_payloads) of
@@ -4316,11 +3269,11 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, F
                false -> []
            end,
            Stanza = event_stanza(
-               [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                       children = [#xmlel{name = <<"item">>, attrs = itemAttr(ItemId),
-                                          children = Content}]}]),
-           broadcast_stanza(Host, From, Node, NodeId, Type,
-                            NodeOptions, SubsByDepth, items, Stanza, true),
+                   [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
+                           children = [#xmlel{name = <<"item">>, attrs = itemAttr(ItemId),
+                                   children = Content}]}]),
+           broadcast_stanza(Host, From, Node, Nidx, Type,
+               NodeOptions, SubsByDepth, items, Stanza, true),
            case Removed of
                [] ->
                    ok;
@@ -4328,11 +3281,11 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, F
                    case get_option(NodeOptions, notify_retract) of
                        true ->
                            RetractStanza = event_stanza(
-                               [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                                       children = [#xmlel{name = <<"retract">>, attrs = itemAttr(RId)} || RId <- Removed]}]),
-                           broadcast_stanza(Host, Node, NodeId, Type,
-                                            NodeOptions, SubsByDepth,
-                                            items, RetractStanza, true);
+                                   [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
+                                           children = [#xmlel{name = <<"retract">>, attrs = itemAttr(RId)} || RId <- Removed]}]),
+                           broadcast_stanza(Host, Node, Nidx, Type,
+                               NodeOptions, SubsByDepth,
+                               items, RetractStanza, true);
                        _ ->
                            ok
                    end
@@ -4342,20 +3295,20 @@ broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, F
            {result, false}
     end.
 
-broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds) ->
-    broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, false).
-broadcast_retract_items(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _ForceNotify) ->
+broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds) ->
+    broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, false).
+broadcast_retract_items(_Host, _Node, _Nidx, _Type, _NodeOptions, [], _ForceNotify) ->
     {result, false};
-broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNotify) ->
+broadcast_retract_items(Host, Node, Nidx, Type, NodeOptions, ItemIds, ForceNotify) ->
     case (get_option(NodeOptions, notify_retract) or ForceNotify) of
        true ->
            case get_collection_subscriptions(Host, Node) of
                SubsByDepth when is_list(SubsByDepth) ->
                    Stanza = event_stanza(
-                       [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                               children = [#xmlel{name = <<"retract">>, attrs = itemAttr(ItemId)} || ItemId <- ItemIds]}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, items, Stanza, true),
+                           [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
+                                   children = [#xmlel{name = <<"retract">>, attrs = itemAttr(ItemId)} || ItemId <- ItemIds]}]),
+                   broadcast_stanza(Host, Node, Nidx, Type,
+                       NodeOptions, SubsByDepth, items, Stanza, true),
                    {result, true};
                _ ->
                    {result, false}
@@ -4364,15 +3317,15 @@ broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNot
            {result, false}
     end.
 
-broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
+broadcast_purge_node(Host, Node, Nidx, Type, NodeOptions) ->
     case get_option(NodeOptions, notify_retract) of
        true ->
            case get_collection_subscriptions(Host, Node) of
                SubsByDepth when is_list(SubsByDepth) ->
                    Stanza = event_stanza(
-                       [#xmlel{name = <<"purge">>, attrs = nodeAttr(Node)}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
+                           [#xmlel{name = <<"purge">>, attrs = nodeAttr(Node)}]),
+                   broadcast_stanza(Host, Node, Nidx, Type,
+                       NodeOptions, SubsByDepth, nodes, Stanza, false),
                    {result, true};
                _ ->
                    {result, false}
@@ -4381,7 +3334,7 @@ broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
            {result, false}
     end.
 
-broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
+broadcast_removed_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth) ->
     case get_option(NodeOptions, notify_delete) of
        true ->
            case SubsByDepth of
@@ -4389,9 +3342,9 @@ broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
                    {result, false};
                _ ->
                    Stanza = event_stanza(
-                       [#xmlel{name = <<"delete">>, attrs = nodeAttr(Node)}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
+                           [#xmlel{name = <<"delete">>, attrs = nodeAttr(Node)}]),
+                   broadcast_stanza(Host, Node, Nidx, Type,
+                       NodeOptions, SubsByDepth, nodes, Stanza, false),
                    {result, true}
            end;
        _ ->
@@ -4400,12 +3353,12 @@ broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
 
 broadcast_created_node(_, _, _, _, _, []) ->
     {result, false};
-broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
+broadcast_created_node(Host, Node, Nidx, Type, NodeOptions, SubsByDepth) ->
     Stanza = event_stanza([#xmlel{name = <<"create">>, attrs = nodeAttr(Node)}]),
-    broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza, true),
+    broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, nodes, Stanza, true),
     {result, true}.
 
-broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
+broadcast_config_notification(Host, Node, Nidx, Type, NodeOptions, Lang) ->
     case get_option(NodeOptions, notify_config) of
        true ->
            case get_collection_subscriptions(Host, Node) of
@@ -4418,9 +3371,9 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
                            []
                    end,
                    Stanza = event_stanza(
-                       [#xmlel{name = <<"configuration">>, attrs = nodeAttr(Node), children = Content}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
+                           [#xmlel{name = <<"configuration">>, attrs = nodeAttr(Node), children = Content}]),
+                   broadcast_stanza(Host, Node, Nidx, Type,
+                       NodeOptions, SubsByDepth, nodes, Stanza, false),
                    {result, true};
                _ ->
                    {result, false}
@@ -4431,34 +3384,35 @@ broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
 
 get_collection_subscriptions(Host, Node) ->
     Action = fun() ->
-           {result, lists:map(fun({Depth, Nodes}) ->
-                       {Depth, [{N, get_node_subs(N)} || N <- Nodes]}
-           end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))}
-       end,
-    case transaction(Action, sync_dirty) of
+           {result, get_node_subs_by_depth(Host, Node, service_jid(Host))}
+    end,
+    case transaction(Host, Action, sync_dirty) of
        {result, CollSubs} -> CollSubs;
        _ -> []
     end.
 
-get_node_subs(#pubsub_node{type   = Type,
-                          id     = NodeID}) ->
-    case node_call(Type, get_node_subscriptions, [NodeID]) of
-       {result, Subs} -> get_options_for_subs(NodeID, Subs);
+get_node_subs_by_depth(Host, Node, From) ->
+    ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, From]),
+    [{Depth, [{N, get_node_subs(Host, N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree].
+
+get_node_subs(Host, #pubsub_node{type = Type, id = Nidx}) ->
+    case node_call(Host, Type, get_node_subscriptions, [Nidx]) of
+       {result, Subs} -> get_options_for_subs(Host, Nidx, Subs);
        Other -> Other
     end.
 
-get_options_for_subs(NodeID, Subs) ->
+get_options_for_subs(Host, Nidx, Subs) ->
+    SubModule = subscription_plugin(Host),
     lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
-                       case pubsub_subscription:read_subscription(JID, NodeID, SubID) of
-                           {error, notfound} -> [{JID, SubID, []} | Acc];
-                           #pubsub_subscription{options = Options} -> [{JID, SubID, Options} | Acc];
-                           _ -> Acc
-                       end;
-                   (_, Acc) ->
-                       Acc
-               end, [], Subs).
+               case SubModule:get_subscription(JID, Nidx, SubID) of
+                   #pubsub_subscription{options = Options} -> [{JID, SubID, Options} | Acc];
+                   {error, notfound} -> [{JID, SubID, []} | Acc]
+               end;
+           (_, Acc) ->
+               Acc
+       end, [], Subs).
 
-broadcast_stanza(Host, _Node, _NodeId, _Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
+broadcast_stanza(Host, _Node, _Nidx, _Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
     NotificationType = get_option(NodeOptions, notification_type, headline),
     BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull
     From = service_jid(Host),
@@ -4466,30 +3420,30 @@ broadcast_stanza(Host, _Node, _NodeId, _Type, NodeOptions, SubsByDepth, NotifyTy
     %% Handles explicit subscriptions
     SubIDsByJID = subscribed_nodes_by_jid(NotifyType, SubsByDepth),
     lists:foreach(fun ({LJID, NodeName, SubIDs}) ->
-                         LJIDs = case BroadcastAll of
-                                     true ->
-                                         {U, S, _} = LJID,
-                                         [{U, S, R} || R <- user_resources(U, S)];
-                                     false ->
-                                         [LJID]
-                                 end,
-        %% Determine if the stanza should have SHIM ('SubID' and 'name') headers
-             StanzaToSend = case {SHIM, SubIDs} of
-                                                {false, _} ->
-                                                  Stanza;
-                                                %% If there's only one SubID, don't add it
-                                                {true, [_]} ->
-                                                  add_shim_headers(Stanza, collection_shim(NodeName));
-                                                {true, SubIDs} ->
-                                                  add_shim_headers(Stanza, lists:append(collection_shim(NodeName), subid_shim(SubIDs)))
-                                  end,
-                         lists:foreach(fun(To) ->
-                                       ejabberd_router:route(From, jlib:make_jid(To), StanzaToSend)
-                               end, LJIDs)
-               end, SubIDsByJID).
-
-broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
-    broadcast_stanza({LUser, LServer, LResource}, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM),
+               LJIDs = case BroadcastAll of
+                   true ->
+                       {U, S, _} = LJID,
+                       [{U, S, R} || R <- user_resources(U, S)];
+                   false ->
+                       [LJID]
+               end,
+               %% Determine if the stanza should have SHIM ('SubID' and 'name') headers
+               StanzaToSend = case {SHIM, SubIDs} of
+                   {false, _} ->
+                       Stanza;
+                   %% If there's only one SubID, don't add it
+                   {true, [_]} ->
+                       add_shim_headers(Stanza, collection_shim(NodeName));
+                   {true, SubIDs} ->
+                       add_shim_headers(Stanza, lists:append(collection_shim(NodeName), subid_shim(SubIDs)))
+               end,
+               lists:foreach(fun(To) ->
+                           ejabberd_router:route(From, jlib:make_jid(To), StanzaToSend)
+                   end, LJIDs)
+       end, SubIDsByJID).
+
+broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
+    broadcast_stanza({LUser, LServer, LResource}, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM),
     %% Handles implicit presence subscriptions
     SenderResource = user_resource(LUser, LServer, LResource),
     case ejabberd_sm:get_session_pid(LUser, LServer, SenderResource) of
@@ -4500,66 +3454,66 @@ broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, Nod
            %% Also, add "replyto" if entity has presence subscription to the account owner
            %% See XEP-0163 1.1 section 4.3.1
            ejabberd_c2s:broadcast(C2SPid,
-               {pep_message, <<((Node))/binary, "+notify">>},
-               _Sender = jlib:make_jid(LUser, LServer, <<"">>),
-               _StanzaToSend = add_extended_headers(Stanza,
-                   _ReplyTo = extended_headers([jlib:jid_to_string(Publisher)])));
+               {pep_message, <<((Node))/binary, "+notify">>},
+               _Sender = jlib:make_jid(LUser, LServer, <<"">>),
+               _StanzaToSend = add_extended_headers(Stanza,
+                   _ReplyTo = extended_headers([jlib:jid_to_string(Publisher)])));
        _ ->
            ?DEBUG("~p@~p has no session; can't deliver ~p to contacts", [LUser, LServer, BaseStanza])
     end;
-broadcast_stanza(Host, _Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
-    broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM).
+broadcast_stanza(Host, _Publisher, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
+    broadcast_stanza(Host, Node, Nidx, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM).
 
 subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
-    NodesToDeliver = fun(Depth, Node, Subs, Acc) ->
+    NodesToDeliver = fun (Depth, Node, Subs, Acc) ->
            NodeName = case Node#pubsub_node.nodeid of
                {_, N} -> N;
                Other -> Other
            end,
            NodeOptions = Node#pubsub_node.options,
            lists:foldl(fun({LJID, SubID, SubOptions}, {JIDs, Recipients}) ->
-               case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
-       true  ->
-                 %% If is to deliver :
-                 case state_can_deliver(LJID, SubOptions) of
-               []            -> {JIDs, Recipients};
-               JIDsToDeliver ->
-                   lists:foldl(
-                     fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
-                   case lists:member(JIDToDeliver, JIDs) of
-                   %% check if the JIDs co-accumulator contains the Subscription Jid,
-                 false ->
-                       %%  - if not,
-                       %%  - add the Jid to JIDs list co-accumulator ;
-                       %%  - create a tuple of the Jid, NodeId, and SubID (as list),
-                       %%    and add the tuple to the Recipients list co-accumulator
-                           {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
-                 true ->
-                       %% - if the JIDs co-accumulator contains the Jid
-                       %%   get the tuple containing the Jid from the Recipient list co-accumulator
-                           {_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
-                       %%   delete the tuple from the Recipients list
-                       % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
-                       % v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}),
-                       %%   add the SubID to the SubIDs list in the tuple,
-                       %%   and add the tuple back to the Recipients list co-accumulator
-                       % v1.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
-                       % v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
-                       % v2: {JIDs, Recipients1}
-                           {JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
-                   end
-                     end, {JIDs, Recipients}, JIDsToDeliver)
-                 end;
-               false ->
-                   {JIDs, Recipients}
-               end
-           end, Acc, Subs)
-       end,
+                       case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
+                           true  ->
+                               %% If is to deliver :
+                               case state_can_deliver(LJID, SubOptions) of
+                                   []            -> {JIDs, Recipients};
+                                   JIDsToDeliver ->
+                                       lists:foldl(
+                                           fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
+                                                   case lists:member(JIDToDeliver, JIDs) of
+                                                       %% check if the JIDs co-accumulator contains the Subscription Jid,
+                                                       false ->
+                                                           %%  - if not,
+                                                           %%  - add the Jid to JIDs list co-accumulator ;
+                                                           %%  - create a tuple of the Jid, Nidx, and SubID (as list),
+                                                           %%    and add the tuple to the Recipients list co-accumulator
+                                                           {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
+                                                       true ->
+                                                           %% - if the JIDs co-accumulator contains the Jid
+                                                           %%   get the tuple containing the Jid from the Recipient list co-accumulator
+                                                           {_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
+                                                           %%   delete the tuple from the Recipients list
+                                                           % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
+                                                           % v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, Nidx1, [SubID | SubIDs]}),
+                                                           %%   add the SubID to the SubIDs list in the tuple,
+                                                           %%   and add the tuple back to the Recipients list co-accumulator
+                                                           % v1.1 : {JIDs, lists:append(Recipients1, [{LJID, Nidx1, lists:append(SubIDs, [SubID])}])}
+                                                           % v1.2 : {JIDs, [{LJID, Nidx1, [SubID | SubIDs]} | Recipients1]}
+                                                           % v2: {JIDs, Recipients1}
+                                                           {JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
+                                                   end
+                                           end, {JIDs, Recipients}, JIDsToDeliver)
+                               end;
+                           false ->
+                               {JIDs, Recipients}
+                       end
+               end, Acc, Subs)
+    end,
     DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
            lists:foldl(fun({Node, Subs}, Acc2) ->
-                   NodesToDeliver(Depth, Node, Subs, Acc2)
-           end, Acc1, SubsByNode)
-       end,
+                       NodesToDeliver(Depth, Node, Subs, Acc2)
+               end, Acc1, SubsByNode)
+    end,
     {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
     JIDSubs.
 
@@ -4571,97 +3525,108 @@ user_resource(User, Server, <<>>) ->
        [R | _] -> R;
        _ -> <<>>
     end;
-user_resource(_, _, Resource) -> Resource.
+user_resource(_, _, Resource) ->
+    Resource.
 
 %%%%%%% Configuration handling
 
-%%<p>There are several reasons why the default node configuration options request might fail:</p>
-%%<ul>
-%%<li>The service does not support node configuration.</li>
-%%<li>The service does not support retrieval of default node configuration.</li>
-%%</ul>
 get_configure(Host, ServerHost, Node, From, Lang) ->
-    Action = fun (#pubsub_node{options = Options,
-                              type = Type, id = NodeId}) ->
-                    case node_call(Type, get_affiliation, [NodeId, From]) of
-                      {result, owner} ->
-                          Groups = ejabberd_hooks:run_fold(roster_groups,
-                                                           ServerHost, [],
-                                                           [ServerHost]),
-                          {result,
-                           [#xmlel{name = <<"pubsub">>,
-                                   attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                                   children =
-                                       [#xmlel{name = <<"configure">>,
-                                               attrs = nodeAttr(Node),
+    Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx}) ->
+           case node_call(Host, Type, get_affiliation, [Nidx, From]) of
+               {result, owner} ->
+                   Groups = ejabberd_hooks:run_fold(roster_groups, ServerHost, [], [ServerHost]),
+                   {result,
+                       [#xmlel{name = <<"pubsub">>,
+                               attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
+                               children =
+                               [#xmlel{name = <<"configure">>,
+                                       attrs = nodeAttr(Node),
+                                       children =
+                                       [#xmlel{name = <<"x">>,
+                                               attrs = [{<<"xmlns">>, ?NS_XDATA},
+                                                   {<<"type">>, <<"form">>}],
                                                children =
-                                                   [#xmlel{name = <<"x">>,
-                                                           attrs =
-                                                               [{<<"xmlns">>,
-                                                                 ?NS_XDATA},
-                                                                {<<"type">>,
-                                                                 <<"form">>}],
-                                                           children =
-                                                               get_configure_xfields(Type,
-                                                                                     Options,
-                                                                                     Lang,
-                                                                                     Groups)}]}]}]};
-                      _ -> {error, ?ERR_FORBIDDEN}
-                    end
-            end,
+                                               get_configure_xfields(Type, Options, Lang, Groups)}]}]}]};
+               _ ->
+                   {error, ?ERR_FORBIDDEN}
+           end
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> {result, Result};
-      Other -> Other
+       {result, {_, Result}} -> {result, Result};
+       Other -> Other
     end.
 
 get_default(Host, Node, _From, Lang) ->
     Type = select_type(Host, Host, Node),
-    Options = node_options(Type),
-%% Get node option
-%% The result depend of the node type plugin system.
+    Options = node_options(Host, Type),
     {result,
-     [#xmlel{name = <<"pubsub">>,
-            attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-            children =
-                [#xmlel{name = <<"default">>, attrs = [],
-                        children =
-                            [#xmlel{name = <<"x">>,
-                                    attrs =
-                                        [{<<"xmlns">>, ?NS_XDATA},
-                                         {<<"type">>, <<"form">>}],
-                                    children =
-                                        get_configure_xfields(Type, Options,
-                                                              Lang, [])}]}]}]}.
+       [#xmlel{name = <<"pubsub">>,
+               attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
+               children =
+               [#xmlel{name = <<"default">>, attrs = [],
+                       children =
+                       [#xmlel{name = <<"x">>,
+                               attrs = [{<<"xmlns">>, ?NS_XDATA},
+                                   {<<"type">>, <<"form">>}],
+                               children = get_configure_xfields(Type, Options, Lang, [])}]}]}]}.
+
+match_option(Node, Var, Val) when is_record(Node, pubsub_node) ->
+    match_option(Node#pubsub_node.options, Var, Val);
+match_option(Options, Var, Val) when is_list(Options) ->
+    get_option(Options, Var) == Val;
+match_option(_, _, _) ->
+    false.
 
 get_option([], _) -> false;
-get_option(Options, Var) ->
-    get_option(Options, Var, false).
+get_option(Options, Var) -> get_option(Options, Var, false).
 
 get_option(Options, Var, Def) ->
     case lists:keysearch(Var, 1, Options) of
-      {value, {_Val, Ret}} -> Ret;
-      _ -> Def
+       {value, {_Val, Ret}} -> Ret;
+       _ -> Def
     end.
 
-%% Get default options from the module plugin.
-node_options(Type) ->
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary>>),
+node_options(Host, Type) ->
+    Module = plugin(Host, Type),
     case catch Module:options() of
-      {'EXIT', {undef, _}} ->
-         DefaultModule =
-             jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                     (?STDNODE)/binary>>),
-         DefaultModule:options();
-      Result -> Result
+       {'EXIT', {undef, _}} ->
+           DefaultModule = plugin(Host, ?STDNODE),
+           DefaultModule:options();
+       Result ->
+           Result
     end.
 
+node_owners_action(Host, Type, Nidx, []) ->
+    case gen_mod:db_type(serverhost(Host), ?MODULE) of
+       odbc ->
+           case node_action(Host, Type, get_node_affiliations, [Nidx]) of
+               {result, Affs} -> [LJID || {LJID, Aff} <- Affs, Aff =:= owner];
+               _ -> []
+           end;
+       _ ->
+           []
+    end;
+node_owners_action(_Host, _Type, _Nidx, Owners) ->
+    Owners.
+
+node_owners_call(Host, Type, Nidx, []) ->
+    case gen_mod:db_type(serverhost(Host), ?MODULE) of
+       odbc ->
+           case node_call(Host, Type, get_node_affiliations, [Nidx]) of
+               {result, Affs} -> [LJID || {LJID, Aff} <- Affs, Aff =:= owner];
+               _ -> []
+           end;
+       _ ->
+           []
+    end;
+node_owners_call(_Host, _Type, _Nidx, Owners) ->
+    Owners.
+
 %% @spec (Host, Options) -> MaxItems
-%%      Host = host()
-%%      Options = [Option]
-%%      Option = {Key::atom(), Value::term()}
-%%      MaxItems = integer() | unlimited
+%%         Host = host()
+%%         Options = [Option]
+%%         Option = {Key::atom(), Value::term()}
+%%         MaxItems = integer() | unlimited
 %% @doc <p>Return the maximum number of items for a given node.</p>
 %% <p>Unlimited means that there is no limit in the number of items that can
 %% be stored.</p>
@@ -4670,116 +3635,99 @@ node_options(Type) ->
 %% version.
 max_items(Host, Options) ->
     case get_option(Options, persist_items) of
-      true ->
-         case get_option(Options, max_items) of
-           false -> unlimited;
-           Result when Result < 0 -> 0;
-           Result -> Result
-         end;
-      false ->
-         case get_option(Options, send_last_published_item) of
-           never -> 0;
-           _ ->
-               case is_last_item_cache_enabled(Host) of
-                 true -> 0;
-                 false -> 1
-               end
-         end
+       true ->
+           case get_option(Options, max_items) of
+               false -> unlimited;
+               Result when Result < 0 -> 0;
+               Result -> Result
+           end;
+       false ->
+           case get_option(Options, send_last_published_item) of
+               never ->
+                   0;
+               _ ->
+                   case is_last_item_cache_enabled(Host) of
+                       true -> 0;
+                       false -> 1
+                   end
+           end
     end.
 
 -define(BOOL_CONFIG_FIELD(Label, Var),
-       ?BOOLXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (get_option(Options, Var)))).
+    ?BOOLXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (get_option(Options, Var)))).
 
 -define(STRING_CONFIG_FIELD(Label, Var),
-       ?STRINGXFIELD(Label,
-                     <<"pubsub#",
-                       (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                     (get_option(Options, Var, <<"">>)))).
+    ?STRINGXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (get_option(Options, Var, <<>>)))).
 
 -define(INTEGER_CONFIG_FIELD(Label, Var),
-       ?STRINGXFIELD(Label,
-                     <<"pubsub#",
-                       (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                     (iolist_to_binary(integer_to_list(get_option(Options,
-                                                                  Var)))))).
+    ?STRINGXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (integer_to_binary(get_option(Options, Var))))).
 
 -define(JLIST_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (jlib:jid_to_string(get_option(Options, Var))),
-                   [jlib:jid_to_string(O) || O <- Opts])).
+    ?LISTXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (jlib:jid_to_string(get_option(Options, Var))),
+       [jlib:jid_to_string(O) || O <- Opts])).
 
 -define(ALIST_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (iolist_to_binary(atom_to_list(get_option(Options,
-                                                             Var)))),
-                   [iolist_to_binary(atom_to_list(O)) || O <- Opts])).
+    ?LISTXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (atom_to_binary(get_option(Options, Var), latin1)),
+       [atom_to_binary(O, latin1) || O <- Opts])).
 
 -define(LISTM_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTMXFIELD(Label,
-                    <<"pubsub#",
-                      (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                    (get_option(Options, Var)), Opts)).
+    ?LISTMXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       (get_option(Options, Var)), Opts)).
 
 -define(NLIST_CONFIG_FIELD(Label, Var),
-       ?STRINGMXFIELD(Label,
-                      <<"pubsub#",
-                        (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                      get_option(Options, Var, []))).
+    ?STRINGMXFIELD(Label,
+       <<"pubsub#", (atom_to_binary(Var, latin1))/binary>>,
+       get_option(Options, Var, []))).
 
 get_configure_xfields(_Type, Options, Lang, Groups) ->
-    [?XFIELD(<<"hidden">>, <<"">>, <<"FORM_TYPE">>,
-            (?NS_PUBSUB_NODE_CONFIG)),
-     ?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
-                       deliver_payloads),
-     ?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>,
-                       deliver_notifications),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node configuratio"
-                         "n changes">>,
-                       notify_config),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node is "
-                         "deleted">>,
-                       notify_delete),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when items are removed "
-                         "from the node">>,
-                       notify_retract),
-     ?BOOL_CONFIG_FIELD(<<"Persist items to storage">>,
-                       persist_items),
-     ?STRING_CONFIG_FIELD(<<"A friendly name for the node">>,
-                         title),
-     ?INTEGER_CONFIG_FIELD(<<"Max # of items to persist">>,
-                          max_items),
-     ?BOOL_CONFIG_FIELD(<<"Whether to allow subscriptions">>,
-                       subscribe),
-     ?ALIST_CONFIG_FIELD(<<"Specify the access model">>,
-                        access_model,
-                        [open, authorize, presence, roster, whitelist]),
-     ?LISTM_CONFIG_FIELD(<<"Roster groups allowed to subscribe">>,
-                        roster_groups_allowed, Groups),
-     ?ALIST_CONFIG_FIELD(<<"Specify the publisher model">>,
-                        publish_model, [publishers, subscribers, open]),
-     ?BOOL_CONFIG_FIELD(<<"Purge all items when the relevant publisher "
-                         "goes offline">>,
-                       purge_offline),
-     ?ALIST_CONFIG_FIELD(<<"Specify the event message type">>,
-                        notification_type, [headline, normal]),
-     ?INTEGER_CONFIG_FIELD(<<"Max payload size in bytes">>,
-                          max_payload_size),
-     ?ALIST_CONFIG_FIELD(<<"When to send the last published item">>,
-                        send_last_published_item,
-                        [never, on_sub, on_sub_and_presence]),
-     ?BOOL_CONFIG_FIELD(<<"Only deliver notifications to available "
-                         "users">>,
-                       presence_based_delivery),
-     ?NLIST_CONFIG_FIELD(<<"The collections with which a node is "
-                          "affiliated">>,
-                        collection)].
+    [?XFIELD(<<"hidden">>, <<>>, <<"FORM_TYPE">>, (?NS_PUBSUB_NODE_CONFIG)),
+       ?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
+           deliver_payloads),
+       ?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>,
+           deliver_notifications),
+       ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node configuration changes">>,
+           notify_config),
+       ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node is deleted">>,
+           notify_delete),
+       ?BOOL_CONFIG_FIELD(<<"Notify subscribers when items are removed from the node">>,
+           notify_retract),
+       ?BOOL_CONFIG_FIELD(<<"Persist items to storage">>,
+           persist_items),
+       ?STRING_CONFIG_FIELD(<<"A friendly name for the node">>,
+           title),
+       ?INTEGER_CONFIG_FIELD(<<"Max # of items to persist">>,
+           max_items),
+       ?BOOL_CONFIG_FIELD(<<"Whether to allow subscriptions">>,
+           subscribe),
+       ?ALIST_CONFIG_FIELD(<<"Specify the access model">>,
+           access_model, [open, authorize, presence, roster, whitelist]),
+       ?LISTM_CONFIG_FIELD(<<"Roster groups allowed to subscribe">>,
+           roster_groups_allowed, Groups),
+       ?ALIST_CONFIG_FIELD(<<"Specify the publisher model">>,
+           publish_model, [publishers, subscribers, open]),
+       ?BOOL_CONFIG_FIELD(<<"Purge all items when the relevant publisher goes offline">>,
+           purge_offline),
+       ?ALIST_CONFIG_FIELD(<<"Specify the event message type">>,
+           notification_type, [headline, normal]),
+       ?INTEGER_CONFIG_FIELD(<<"Max payload size in bytes">>,
+           max_payload_size),
+       ?ALIST_CONFIG_FIELD(<<"When to send the last published item">>,
+           send_last_published_item, [never, on_sub, on_sub_and_presence]),
+       ?BOOL_CONFIG_FIELD(<<"Only deliver notifications to available users">>,
+           presence_based_delivery),
+       ?NLIST_CONFIG_FIELD(<<"The collections with which a node is affiliated">>,
+           collection)].
 
 %%<p>There are several reasons why the node configuration request might fail:</p>
 %%<ul>
@@ -4791,193 +3739,143 @@ get_configure_xfields(_Type, Options, Lang, Groups) ->
 %%</ul>
 set_configure(Host, Node, From, Els, Lang) ->
     case xml:remove_cdata(Els) of
-      [#xmlel{name = <<"x">>} = XEl] ->
-         case {xml:get_tag_attr_s(<<"xmlns">>, XEl),
-               xml:get_tag_attr_s(<<"type">>, XEl)}
-             of
-           {?NS_XDATA, <<"cancel">>} -> {result, []};
-           {?NS_XDATA, <<"submit">>} ->
-               Action = fun (#pubsub_node{options = Options,
-                                          type = Type, id = NodeId} =
-                                 N) ->
-                                case node_call(Type, get_affiliation,
-                                               [NodeId, From])
-                                    of
-                                  {result, owner} ->
-                                      case jlib:parse_xdata_submit(XEl) of
-                                        invalid -> {error, ?ERR_BAD_REQUEST};
-                                        XData ->
-                                            OldOpts = case Options of
-                                                        [] ->
-                                                            node_options(Type);
-                                                        _ -> Options
-                                                      end,
-                                            case set_xoption(Host, XData,
-                                                             OldOpts)
-                                                of
-                                              NewOpts
-                                                  when is_list(NewOpts) ->
-                                                  case tree_call(Host,
-                                                                 set_node,
-                                                                 [N#pubsub_node{options
-                                                                                    =
-                                                                                    NewOpts}])
-                                                      of
-                                                    ok -> {result, ok};
-                                                    Err -> Err
-                                                  end;
-                                              Err -> Err
-                                            end
-                                      end;
-                                  _ -> {error, ?ERR_FORBIDDEN}
-                                end
-                        end,
-               case transaction(Host, Node, Action, transaction) of
-                 {result, {TNode, ok}} ->
-                     NodeId = TNode#pubsub_node.id,
-                     Type = TNode#pubsub_node.type,
-                     Options = TNode#pubsub_node.options,
-                     broadcast_config_notification(Host, Node, NodeId, Type,
-                                                   Options, Lang),
-                     {result, []};
-                 Other -> Other
-               end;
-           _ -> {error, ?ERR_BAD_REQUEST}
-         end;
-      _ -> {error, ?ERR_BAD_REQUEST}
+       [#xmlel{name = <<"x">>} = XEl] ->
+           case {xml:get_tag_attr_s(<<"xmlns">>, XEl), xml:get_tag_attr_s(<<"type">>, XEl)} of
+               {?NS_XDATA, <<"cancel">>} ->
+                   {result, []};
+               {?NS_XDATA, <<"submit">>} ->
+                   Action = fun (#pubsub_node{options = Options, type = Type, id = Nidx} = N) ->
+                           case node_call(Host, Type, get_affiliation, [Nidx, From]) of
+                               {result, owner} ->
+                                   case jlib:parse_xdata_submit(XEl) of
+                                       invalid ->
+                                           {error, ?ERR_BAD_REQUEST};
+                                       XData ->
+                                           OldOpts = case Options of
+                                               [] -> node_options(Host, Type);
+                                               _ -> Options
+                                           end,
+                                           case set_xoption(Host, XData, OldOpts) of
+                                               NewOpts when is_list(NewOpts) ->
+                                                   case tree_call(Host,
+                                                           set_node,
+                                                           [N#pubsub_node{options = NewOpts}])
+                                                   of
+                                                       ok -> {result, ok};
+                                                       Err -> Err
+                                                   end;
+                                               Error ->
+                                                   Error
+                                           end
+                                   end;
+                               _ ->
+                                   {error, ?ERR_FORBIDDEN}
+                           end
+                   end,
+                   case transaction(Host, Node, Action, transaction) of
+                       {result, {TNode, ok}} ->
+                           Nidx = TNode#pubsub_node.id,
+                           Type = TNode#pubsub_node.type,
+                           Options = TNode#pubsub_node.options,
+                           broadcast_config_notification(Host, Node, Nidx, Type, Options, Lang),
+                           {result, []};
+                       Other ->
+                           Other
+                   end;
+               _ ->
+                   {error, ?ERR_BAD_REQUEST}
+           end;
+       _ ->
+           {error, ?ERR_BAD_REQUEST}
     end.
 
 add_opt(Key, Value, Opts) ->
-    Opts1 = lists:keydelete(Key, 1, Opts),
-    [{Key, Value} | Opts1].
+    [{Key, Value} | lists:keydelete(Key, 1, Opts)].
 
 -define(SET_BOOL_XOPT(Opt, Val),
-       BoolVal = case Val of
-                   <<"0">> -> false;
-                   <<"1">> -> true;
-                   <<"false">> -> false;
-                   <<"true">> -> true;
-                   _ -> error
-                 end,
-       case BoolVal of
-         error -> {error, ?ERR_NOT_ACCEPTABLE};
-         _ ->
-             set_xoption(Host, Opts, add_opt(Opt, BoolVal, NewOpts))
-       end).
+    BoolVal = case Val of
+       <<"0">> -> false;
+       <<"1">> -> true;
+       <<"false">> -> false;
+       <<"true">> -> true;
+       _ -> error
+    end,
+    case BoolVal of
+       error -> {error, ?ERR_NOT_ACCEPTABLE};
+       _ -> set_xoption(Host, Opts, add_opt(Opt, BoolVal, NewOpts))
+    end).
 
 -define(SET_STRING_XOPT(Opt, Val),
-       set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
+    set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
 
 -define(SET_INTEGER_XOPT(Opt, Val, Min, Max),
-       case catch jlib:binary_to_integer(Val) of
-         IVal when is_integer(IVal), IVal >= Min, IVal =< Max ->
-             set_xoption(Host, Opts, add_opt(Opt, IVal, NewOpts));
-         _ -> {error, ?ERR_NOT_ACCEPTABLE}
-       end).
+    case catch jlib:binary_to_integer(Val) of
+       IVal when is_integer(IVal), IVal >= Min, IVal =< Max ->
+           set_xoption(Host, Opts, add_opt(Opt, IVal, NewOpts));
+       _ ->
+           {error, ?ERR_NOT_ACCEPTABLE}
+    end).
 
 -define(SET_ALIST_XOPT(Opt, Val, Vals),
-       case lists:member(Val,
-                         [iolist_to_binary(atom_to_list(V)) || V <- Vals])
-           of
-         true ->
-             set_xoption(Host, Opts,
-                         add_opt(Opt, jlib:binary_to_atom(Val), NewOpts));
-         false -> {error, ?ERR_NOT_ACCEPTABLE}
-       end).
+    case lists:member(Val, [atom_to_binary(V, latin1) || V <- Vals]) of
+       true ->
+           set_xoption(Host, Opts, add_opt(Opt, jlib:binary_to_atom(Val), NewOpts));
+       false ->
+           {error, ?ERR_NOT_ACCEPTABLE}
+    end).
 
 -define(SET_LIST_XOPT(Opt, Val),
-       set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
+    set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
 
 set_xoption(_Host, [], NewOpts) -> NewOpts;
-set_xoption(Host, [{<<"FORM_TYPE">>, _} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"FORM_TYPE">>, _} | Opts], NewOpts) ->
     set_xoption(Host, Opts, NewOpts);
-set_xoption(Host,
-           [{<<"pubsub#roster_groups_allowed">>, Value} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#roster_groups_allowed">>, Value} | Opts], NewOpts) ->
     ?SET_LIST_XOPT(roster_groups_allowed, Value);
-set_xoption(Host,
-           [{<<"pubsub#deliver_payloads">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#deliver_payloads">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(deliver_payloads, Val);
-set_xoption(Host,
-           [{<<"pubsub#deliver_notifications">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#deliver_notifications">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(deliver_notifications, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_config">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#notify_config">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(notify_config, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_delete">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#notify_delete">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(notify_delete, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_retract">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#notify_retract">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(notify_retract, Val);
-set_xoption(Host,
-           [{<<"pubsub#persist_items">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#persist_items">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(persist_items, Val);
-set_xoption(Host,
-           [{<<"pubsub#max_items">>, [Val]} | Opts], NewOpts) ->
+set_xoption(Host, [{<<"pubsub#max_items">>, [Val]} | Opts], NewOpts) ->
     MaxItems = get_max_items_node(Host),
     ?SET_INTEGER_XOPT(max_items, Val, 0, MaxItems);
-set_xoption(Host,
-           [{<<"pubsub#subscribe">>, [Val]} | Opts], NewOpts) ->
+set_xoption(Host, [{<<"pubsub#subscribe">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(subscribe, Val);
-set_xoption(Host,
-           [{<<"pubsub#access_model">>, [Val]} | Opts], NewOpts) ->
-    ?SET_ALIST_XOPT(access_model, Val,
-                   [open, authorize, presence, roster, whitelist]);
-set_xoption(Host,
-           [{<<"pubsub#publish_model">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(publish_model, Val,
-                   [publishers, subscribers, open]);
-set_xoption(Host,
-           [{<<"pubsub#notification_type">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(notification_type, Val,
-                   [headline, normal]);
-set_xoption(Host,
-           [{<<"pubsub#node_type">>, [Val]} | Opts], NewOpts) ->
+set_xoption(Host, [{<<"pubsub#access_model">>, [Val]} | Opts], NewOpts) ->
+    ?SET_ALIST_XOPT(access_model, Val, [open, authorize, presence, roster, whitelist]);
+set_xoption(Host, [{<<"pubsub#publish_model">>, [Val]} | Opts], NewOpts) ->
+    ?SET_ALIST_XOPT(publish_model, Val, [publishers, subscribers, open]);
+set_xoption(Host, [{<<"pubsub#notification_type">>, [Val]} | Opts], NewOpts) ->
+    ?SET_ALIST_XOPT(notification_type, Val, [headline, normal]);
+set_xoption(Host, [{<<"pubsub#node_type">>, [Val]} | Opts], NewOpts) ->
     ?SET_ALIST_XOPT(node_type, Val, [leaf, collection]);
-set_xoption(Host,
-           [{<<"pubsub#max_payload_size">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_INTEGER_XOPT(max_payload_size, Val, 0,
-                     (?MAX_PAYLOAD_SIZE));
-set_xoption(Host,
-           [{<<"pubsub#send_last_published_item">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(send_last_published_item, Val,
-                   [never, on_sub, on_sub_and_presence]);
-set_xoption(Host,
-           [{<<"pubsub#presence_based_delivery">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#max_payload_size">>, [Val]} | Opts], NewOpts) ->
+    ?SET_INTEGER_XOPT(max_payload_size, Val, 0, (?MAX_PAYLOAD_SIZE));
+set_xoption(Host, [{<<"pubsub#send_last_published_item">>, [Val]} | Opts], NewOpts) ->
+    ?SET_ALIST_XOPT(send_last_published_item, Val, [never, on_sub, on_sub_and_presence]);
+set_xoption(Host, [{<<"pubsub#presence_based_delivery">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(presence_based_delivery, Val);
-set_xoption(Host,
-           [{<<"pubsub#purge_offline">>, [Val]} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#purge_offline">>, [Val]} | Opts], NewOpts) ->
     ?SET_BOOL_XOPT(purge_offline, Val);
-set_xoption(Host, [{<<"pubsub#title">>, Value} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#title">>, Value} | Opts], NewOpts) ->
     ?SET_STRING_XOPT(title, Value);
-set_xoption(Host, [{<<"pubsub#type">>, Value} | Opts],
-           NewOpts) ->
+set_xoption(Host, [{<<"pubsub#type">>, Value} | Opts], NewOpts) ->
     ?SET_STRING_XOPT(type, Value);
-set_xoption(Host,
-           [{<<"pubsub#body_xslt">>, Value} | Opts], NewOpts) ->
+set_xoption(Host, [{<<"pubsub#body_xslt">>, Value} | Opts], NewOpts) ->
     ?SET_STRING_XOPT(body_xslt, Value);
-set_xoption(Host,
-           [{<<"pubsub#collection">>, Value} | Opts], NewOpts) ->
-%    NewValue = [string_to_node(V) || V <- Value],
+set_xoption(Host, [{<<"pubsub#collection">>, Value} | Opts], NewOpts) ->
+    %    NewValue = [string_to_node(V) || V <- Value],
     ?SET_LIST_XOPT(collection, Value);
-set_xoption(Host, [{<<"pubsub#node">>, [Value]} | Opts],
-           NewOpts) ->
-%    NewValue = string_to_node(Value),
+set_xoption(Host, [{<<"pubsub#node">>, [Value]} | Opts], NewOpts) ->
+    %    NewValue = string_to_node(Value),
     ?SET_LIST_XOPT(node, Value);
 set_xoption(Host, [_ | Opts], NewOpts) ->
     set_xoption(Host, Opts, NewOpts).
@@ -4985,164 +3883,180 @@ set_xoption(Host, [_ | Opts], NewOpts) ->
 get_max_items_node({_, ServerHost, _}) ->
     get_max_items_node(ServerHost);
 get_max_items_node(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         max_items_node)
-       of
-      [{max_items_node, Integer}] -> Integer;
-      _ -> ?MAXITEMS
-    end.
+    config(serverhost(Host), max_items_node, ?MAXITEMS).
 
 %%%% last item cache handling
 
 is_last_item_cache_enabled({_, ServerHost, _}) ->
     is_last_item_cache_enabled(ServerHost);
 is_last_item_cache_enabled(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         last_item_cache)
-       of
-      [{last_item_cache, true}] -> true;
-      _ -> false
-    end.
+    config(serverhost(Host), last_item_cache, false).
 
-set_cached_item({_, ServerHost, _}, NodeId, ItemId,
-               Publisher, Payload) ->
-    set_cached_item(ServerHost, NodeId, ItemId, Publisher,
-                   Payload);
-set_cached_item(Host, NodeId, ItemId, Publisher,
-               Payload) ->
+set_cached_item({_, ServerHost, _}, Nidx, ItemId, Publisher, Payload) ->
+    set_cached_item(ServerHost, Nidx, ItemId, Publisher, Payload);
+set_cached_item(Host, Nidx, ItemId, Publisher, Payload) ->
     case is_last_item_cache_enabled(Host) of
-      true ->
-         mnesia:dirty_write({pubsub_last_item, NodeId, ItemId,
-                             {now(),
-                              jlib:jid_tolower(jlib:jid_remove_resource(Publisher))},
-                             Payload});
-      _ -> ok
+       true -> mnesia:dirty_write({pubsub_last_item, Nidx, ItemId,
+                   {now(), jlib:jid_tolower(jlib:jid_remove_resource(Publisher))},
+                   Payload});
+       _ -> ok
     end.
 
-unset_cached_item({_, ServerHost, _}, NodeId) ->
-    unset_cached_item(ServerHost, NodeId);
-unset_cached_item(Host, NodeId) ->
+unset_cached_item({_, ServerHost, _}, Nidx) ->
+    unset_cached_item(ServerHost, Nidx);
+unset_cached_item(Host, Nidx) ->
     case is_last_item_cache_enabled(Host) of
-      true -> mnesia:dirty_delete({pubsub_last_item, NodeId});
-      _ -> ok
+       true -> mnesia:dirty_delete({pubsub_last_item, Nidx});
+       _ -> ok
     end.
 
 -spec(get_cached_item/2 ::
-(
-  Host    :: mod_pubsub:host(),
-  NodeIdx :: mod_pubsub:nodeIdx())
+    (
+       Host    :: mod_pubsub:host(),
+       Nidx :: mod_pubsub:nodeIdx())
     -> undefined | mod_pubsub:pubsubItem()
-).
-get_cached_item({_, ServerHost, _}, NodeId) ->
-    get_cached_item(ServerHost, NodeId);
-get_cached_item(Host, NodeIdx) ->
+    ).
+get_cached_item({_, ServerHost, _}, Nidx) ->
+    get_cached_item(ServerHost, Nidx);
+get_cached_item(Host, Nidx) ->
     case is_last_item_cache_enabled(Host) of
-      true ->
-         case mnesia:dirty_read({pubsub_last_item, NodeIdx}) of
-           [#pubsub_last_item{itemid = ItemId, creation = Creation, payload = Payload}] ->
-%          [{pubsub_last_item, NodeId, ItemId, Creation,
-%            Payload}] ->
-               #pubsub_item{itemid = {ItemId, NodeIdx},
-                            payload = Payload, creation = Creation,
-                            modification = Creation};
-           _ -> undefined
-         end;
-      _ -> undefined
+       true ->
+           case mnesia:dirty_read({pubsub_last_item, Nidx}) of
+               [#pubsub_last_item{itemid = ItemId, creation = Creation, payload = Payload}] ->
+                   %            [{pubsub_last_item, Nidx, ItemId, Creation,
+                   %              Payload}] ->
+                   #pubsub_item{itemid = {ItemId, Nidx},
+                       payload = Payload, creation = Creation,
+                       modification = Creation};
+               _ ->
+                   undefined
+           end;
+       _ ->
+           undefined
     end.
 
 %%%% plugin handling
 
 host(ServerHost) ->
-    case catch
-          ets:lookup(gen_mod:get_module_proc(ServerHost, config),
-                     host)
-       of
-      [{host, Host}] -> Host;
-      _ -> <<"pubsub.", ServerHost/binary>>
+    config(ServerHost, host, <<"pubsub.", ServerHost/binary>>).
+
+serverhost({_U, Server, _R})->
+    Server;
+serverhost(Host) ->
+    case binary:match(Host, <<"pubsub.">>) of
+       {0,7} ->
+           [_,ServerHost] = binary:split(Host, <<".">>),
+           ServerHost;
+       _ ->
+           Host
+    end.
+
+tree(Host) ->
+    case config(serverhost(Host), nodetree) of
+       undefined -> tree(Host, ?STDTREE);
+       Tree -> Tree
+    end.
+
+tree(Host, Name) ->
+    case gen_mod:db_type(serverhost(Host), ?MODULE) of
+       mnesia -> jlib:binary_to_atom(<<"nodetree_", Name/binary>>);
+       odbc -> jlib:binary_to_atom(<<"nodetree_", Name/binary, "_odbc">>);
+       _ -> Name
+    end.
+
+plugin(Host, Name) ->
+    case gen_mod:db_type(serverhost(Host), ?MODULE) of
+       mnesia -> jlib:binary_to_atom(<<"node_", Name/binary>>);
+       odbc -> jlib:binary_to_atom(<<"node_", Name/binary, "_odbc">>);
+       _ -> Name
     end.
 
 plugins(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         plugins)
-       of
-      [{plugins, []}] -> [?STDNODE];
-      [{plugins, PL}] -> PL;
-      _ -> [?STDNODE]
+    case config(serverhost(Host), plugins) of
+       undefined -> [?STDNODE];
+       [] -> [?STDNODE];
+       Plugins -> Plugins
+    end.
+
+subscription_plugin(Host) ->
+    case gen_mod:db_type(serverhost(Host), ?MODULE) of
+       mnesia -> pubsub_subscription;
+       odbc -> pubsub_subscription_odbc;
+       _ -> none
+    end.
+
+config(ServerHost, Key) ->
+    config(ServerHost, Key, undefined).
+config(ServerHost, Key, Default) ->
+    case catch ets:lookup(gen_mod:get_module_proc(ServerHost, config), Key) of
+       [{Key, Value}] -> Value;
+       _ -> Default
     end.
 
 select_type(ServerHost, Host, Node, Type) ->
     SelectedType = case Host of
-                    {_User, _Server, _Resource} ->
-                        case catch
-                               ets:lookup(gen_mod:get_module_proc(ServerHost,
-                                                                  config),
-                                          pep_mapping)
-                            of
-                          [{pep_mapping, PM}] ->
-                              proplists:get_value(Node, PM, ?PEPNODE);
-                          _ -> ?PEPNODE
-                        end;
-                    _ -> Type
-                  end,
+       {_User, _Server, _Resource} ->
+           case config(ServerHost, pep_mapping) of
+               undefined -> ?PEPNODE;
+               Mapping -> proplists:get_value(Node, Mapping, ?PEPNODE)
+           end;
+       _ ->
+           Type
+    end,
     ConfiguredTypes = plugins(ServerHost),
     case lists:member(SelectedType, ConfiguredTypes) of
-      true -> SelectedType;
-      false -> hd(ConfiguredTypes)
+       true -> SelectedType;
+       false -> hd(ConfiguredTypes)
     end.
 
 select_type(ServerHost, Host, Node) ->
-    select_type(ServerHost, Host, Node,
-               hd(plugins(ServerHost))).
+    select_type(ServerHost, Host, Node, hd(plugins(ServerHost))).
+
+feature(<<"rsm">>) -> ?NS_RSM;
+feature(Feature) -> <<(?NS_PUBSUB)/binary, "#", Feature/binary>>.
 
 features() ->
     [% see plugin "access-authorize",   % OPTIONAL
-     <<"access-open">>,   % OPTIONAL this relates to access_model option in node_hometree
-     <<"access-presence">>,   % OPTIONAL this relates to access_model option in node_pep
-     <<"access-whitelist">>,   % OPTIONAL
-     <<"collections">>,   % RECOMMENDED
-     <<"config-node">>,   % RECOMMENDED
-     <<"create-and-configure">>,   % RECOMMENDED
-     <<"item-ids">>,   % RECOMMENDED
-     <<"last-published">>,   % RECOMMENDED
-     <<"member-affiliation">>,   % RECOMMENDED
-     <<"presence-notifications">>,   % OPTIONAL
-     <<"presence-subscribe">>,   % RECOMMENDED
-     <<"publisher-affiliation">>,   % RECOMMENDED
-     <<"retrieve-default">>].
-
-         % see plugin "retrieve-items",   % RECOMMENDED
-        % see plugin "retrieve-subscriptions",   % RECOMMENDED
-        %TODO "shim", % OPTIONAL
-        % see plugin "subscribe",   % REQUIRED
-        % see plugin "subscription-options",   % OPTIONAL
-        % see plugin "subscription-notifications"   % OPTIONAL
-
-features(Type) ->
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary>>),
-    features() ++
-      case catch Module:features() of
+       <<"access-open">>,   % OPTIONAL this relates to access_model option in node_hometree
+       <<"access-presence">>,   % OPTIONAL this relates to access_model option in node_pep
+       <<"access-whitelist">>,   % OPTIONAL
+       <<"collections">>,   % RECOMMENDED
+       <<"config-node">>,   % RECOMMENDED
+       <<"create-and-configure">>,   % RECOMMENDED
+       <<"item-ids">>,   % RECOMMENDED
+       <<"last-published">>,   % RECOMMENDED
+       <<"member-affiliation">>,   % RECOMMENDED
+       <<"presence-notifications">>,   % OPTIONAL
+       <<"presence-subscribe">>,   % RECOMMENDED
+       <<"publisher-affiliation">>,   % RECOMMENDED
+       <<"retrieve-default">>,
+       <<"shim">>].   % RECOMMENDED
+
+% see plugin "retrieve-items",   % RECOMMENDED
+% see plugin "retrieve-subscriptions",   % RECOMMENDED
+% see plugin "subscribe",   % REQUIRED
+% see plugin "subscription-options",   % OPTIONAL
+% see plugin "subscription-notifications"   % OPTIONAL
+
+plugin_features(Host, Type) ->
+    Module = plugin(Host, Type),
+    case catch Module:features() of
        {'EXIT', {undef, _}} -> [];
        Result -> Result
-      end.
+    end.
 
 features(Host, <<>>) ->
     lists:usort(lists:foldl(fun (Plugin, Acc) ->
-                                   Acc ++ features(Plugin)
-                           end,
-                           [], plugins(Host)));
-features(Host, Node) ->
+                   Acc ++ plugin_features(Host, Plugin)
+           end,
+           features(), plugins(Host)));
+features(Host, Node) when is_binary(Node) ->
     Action = fun (#pubsub_node{type = Type}) ->
-                    {result, features(Type)}
-            end,
+           {result, plugin_features(Host, Type)}
+    end,
     case transaction(Host, Node, Action, sync_dirty) of
-      {result, Features} ->
-         lists:usort(features() ++ Features);
-      _ -> features()
+       {result, Features} -> lists:usort(features() ++ Features);
+       _ -> features()
     end.
 
 %% @doc <p>node tree plugin call.</p>
@@ -5150,113 +4064,143 @@ tree_call({_User, Server, _Resource}, Function, Args) ->
     tree_call(Server, Function, Args);
 tree_call(Host, Function, Args) ->
     ?DEBUG("tree_call ~p ~p ~p", [Host, Function, Args]),
-    Module = case catch
-                   ets:lookup(gen_mod:get_module_proc(Host, config),
-                              nodetree)
-                of
-              [{nodetree, N}] -> N;
-              _ ->
-                  jlib:binary_to_atom(<<(?TREE_PREFIX)/binary,
-                                          (?STDTREE)/binary>>)
-            end,
-    catch apply(Module, Function, Args).
+    catch apply(tree(Host), Function, Args).
 
 tree_action(Host, Function, Args) ->
     ?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]),
+    ServerHost = serverhost(Host),
     Fun = fun () -> tree_call(Host, Function, Args) end,
-    catch mnesia:sync_dirty(Fun).
+    case gen_mod:db_type(ServerHost, ?MODULE) of
+       mnesia ->
+           catch mnesia:sync_dirty(Fun);
+       odbc ->
+           case catch ejabberd_odbc:sql_bloc(ServerHost, Fun) of
+               {atomic, Result} ->
+                   Result;
+               {aborted, Reason} ->
+                   ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
+                   {error, ?ERR_INTERNAL_SERVER_ERROR}
+           end;
+       Other ->
+           ?ERROR_MSG("unsupported backend: ~p~n", [Other]),
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
+    end.
 
 %% @doc <p>node plugin call.</p>
-node_call(Type, Function, Args) ->
+node_call(Host, Type, Function, Args) ->
     ?DEBUG("node_call ~p ~p ~p", [Type, Function, Args]),
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary>>),
+    Module = plugin(Host, Type),
     case apply(Module, Function, Args) of
-      {result, Result} -> {result, Result};
-      {error, Error} -> {error, Error};
-      {'EXIT', {undef, Undefined}} ->
-         case Type of
-           ?STDNODE -> {error, {undef, Undefined}};
-           _ -> node_call(?STDNODE, Function, Args)
-         end;
-      {'EXIT', Reason} -> {error, Reason};
-      Result ->
-         {result,
-          Result} %% any other return value is forced as result
+       {result, Result} ->
+           {result, Result};
+       {error, Error} ->
+           {error, Error};
+       {'EXIT', {undef, Undefined}} ->
+           case Type of
+               ?STDNODE -> {error, {undef, Undefined}};
+               _ -> node_call(Host, ?STDNODE, Function, Args)
+           end;
+       {'EXIT', Reason} ->
+           {error, Reason};
+       Result ->
+           {result, Result} %% any other return value is forced as result
     end.
 
 node_action(Host, Type, Function, Args) ->
-    ?DEBUG("node_action ~p ~p ~p ~p",
-          [Host, Type, Function, Args]),
-    transaction(fun () -> node_call(Type, Function, Args)
-               end,
-               sync_dirty).
+    ?DEBUG("node_action ~p ~p ~p ~p", [Host, Type, Function, Args]),
+    transaction(Host, fun () ->
+               node_call(Host, Type, Function, Args)
+       end,
+       sync_dirty).
 
 %% @doc <p>plugin transaction handling.</p>
 transaction(Host, Node, Action, Trans) ->
-    transaction(fun () ->
-                       case tree_call(Host, get_node, [Host, Node]) of
-                         N when is_record(N, pubsub_node) ->
-                             case Action(N) of
-                               {result, Result} -> {result, {N, Result}};
-                               {atomic, {result, Result}} ->
-                                   {result, {N, Result}};
-                               Other -> Other
-                             end;
-                         Error -> Error
-                       end
-               end,
-               Trans).
-
-transaction(Host, Action, Trans) ->
-    transaction(fun () ->
-                       {result,
-                        lists:foldl(Action, [],
-                                    tree_call(Host, get_nodes, [Host]))}
-               end,
-               Trans).
-
-transaction(Fun, Trans) ->
-    case catch mnesia:Trans(Fun) of
-      {result, Result} -> {result, Result};
-      {error, Error} -> {error, Error};
-      {atomic, {result, Result}} -> {result, Result};
-      {atomic, {error, Error}} -> {error, Error};
-      {aborted, Reason} ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [{aborted, Reason}]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      {'EXIT', Reason} ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [{'EXIT', Reason}]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      Other ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [Other]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR}
+    transaction(Host, fun () ->
+               case tree_call(Host, get_node, [Host, Node]) of
+                   N when is_record(N, pubsub_node) ->
+                       case Action(N) of
+                           {result, Result} -> {result, {N, Result}};
+                           {atomic, {result, Result}} -> {result, {N, Result}};
+                           Other -> Other
+                       end;
+                   Error ->
+                       Error
+               end
+       end,
+       Trans).
+
+transaction(Host, Fun, Trans) ->
+    ServerHost = serverhost(Host),
+    DBType = gen_mod:db_type(ServerHost, ?MODULE),
+    Retry = case DBType of
+       odbc -> 2;
+       _ -> 1
+    end,
+    transaction_retry(Host, ServerHost, Fun, Trans, DBType, Retry).
+
+transaction_retry(_Host, _ServerHost, _Fun, _Trans, _DBType, 0) ->
+    {error, ?ERR_INTERNAL_SERVER_ERROR};
+transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) ->
+    Res = case DBType of
+       mnesia ->
+           catch mnesia:Trans(Fun);
+       odbc ->
+           SqlFun = case Trans of
+               transaction -> sql_transaction;
+               _ -> sql_bloc
+           end,
+           catch ejabberd_odbc:SqlFun(ServerHost, Fun);
+       _ ->
+           {unsupported, DBType}
+    end,
+    case Res of
+       {result, Result} ->
+           {result, Result};
+       {error, Error} ->
+           {error, Error};
+       {atomic, {result, Result}} ->
+           {result, Result};
+       {atomic, {error, Error}} ->
+           {error, Error};
+       {aborted, Reason} ->
+           ?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
+           {error, ?ERR_INTERNAL_SERVER_ERROR};
+       {'EXIT', {timeout, _} = Reason} ->
+           ?ERROR_MSG("transaction return internal error: ~p~n", [Reason]),
+           transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count - 1);
+       {'EXIT', Reason} ->
+           ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
+           {error, ?ERR_INTERNAL_SERVER_ERROR};
+       Other ->
+           ?ERROR_MSG("transaction return internal error: ~p~n", [Other]),
+           {error, ?ERR_INTERNAL_SERVER_ERROR}
     end.
 
 %%%% helpers
 
 %% Add pubsub-specific error element
 extended_error(Error, Ext) ->
-    extended_error(Error, Ext,
-                  [{<<"xmlns">>, ?NS_PUBSUB_ERRORS}]).
+    extended_error(Error, Ext, [{<<"xmlns">>, ?NS_PUBSUB_ERRORS}]).
 
 extended_error(Error, unsupported, Feature) ->
-%% Give a uniq identifier
+    %% Give a uniq identifier
     extended_error(Error, <<"unsupported">>,
-                  [{<<"xmlns">>, ?NS_PUBSUB_ERRORS},
-                   {<<"feature">>, Feature}]);
-extended_error(#xmlel{name = Error, attrs = Attrs,
-                     children = SubEls},
-              Ext, ExtAttrs) ->
+       [{<<"xmlns">>, ?NS_PUBSUB_ERRORS},
+           {<<"feature">>, Feature}]);
+extended_error(#xmlel{name = Error, attrs = Attrs, children = SubEls}, Ext, ExtAttrs) ->
     #xmlel{name = Error, attrs = Attrs,
-          children =
-              lists:reverse([#xmlel{name = Ext, attrs = ExtAttrs,
-                                    children = []}
-                             | SubEls])}.
+       children = lists:reverse([#xmlel{name = Ext, attrs = ExtAttrs} | SubEls])}.
+
+string_to_ljid(JID) ->
+    case jlib:string_to_jid(JID) of
+       error ->
+           {<<>>, <<>>, <<>>};
+       J ->
+           case jlib:jid_tolower(J) of
+               error -> {<<>>, <<>>, <<>>};
+               J1 -> J1
+           end
+    end.
 
 -spec(uniqid/0 :: () -> mod_pubsub:itemId()).
 uniqid() ->
@@ -5269,24 +4213,23 @@ itemAttr([]) -> [];
 itemAttr(ItemId) -> [{<<"id">>, ItemId}].
 
 itemsEls(Items) ->
-    lists:map(fun (#pubsub_item{itemid = {ItemId, _}, payload = Payload}) ->
-               #xmlel{name = <<"item">>, attrs = itemAttr(ItemId), children = Payload}
-       end, Items).
+    [#xmlel{name = <<"item">>, attrs = itemAttr(ItemId), children = Payload}
+       || #pubsub_item{itemid = {ItemId, _}, payload = Payload} <- Items].
 
 -spec(add_message_type/2 ::
-(
-  Message :: xmlel(),
-  Type    :: atom())
+    (
+       Message :: xmlel(),
+       Type    :: atom())
     -> xmlel()
-).
+    ).
 
 add_message_type(Message, normal) -> Message;
-add_message_type(#xmlel{name = <<"message">>, attrs = Attrs, children = Els},
-  Type) ->
+add_message_type(#xmlel{name = <<"message">>, attrs = Attrs, children = Els}, Type) ->
     #xmlel{name = <<"message">>,
-           attrs = [{<<"type">>, jlib:atom_to_binary(Type)} | Attrs],
-           children = Els};
-add_message_type(XmlEl, _Type) -> XmlEl.
+       attrs = [{<<"type">>, jlib:atom_to_binary(Type)} | Attrs],
+       children = Els};
+add_message_type(XmlEl, _Type) ->
+    XmlEl.
 
 %% Place of <headers/> changed at the bottom of the stanza
 %% cf. http://xmpp.org/extensions/xep-0060.html#publisher-publish-success-subid
@@ -5298,230 +4241,98 @@ add_shim_headers(Stanza, HeaderEls) ->
     add_headers(Stanza, <<"headers">>, ?NS_SHIM, HeaderEls).
 
 add_extended_headers(Stanza, HeaderEls) ->
-    add_headers(Stanza, <<"addresses">>, ?NS_ADDRESS,
-               HeaderEls).
+    add_headers(Stanza, <<"addresses">>, ?NS_ADDRESS, HeaderEls).
 
-add_headers(#xmlel{name = Name, attrs = Attrs, children = Els},
-       HeaderName, HeaderNS, HeaderEls) ->
+add_headers(#xmlel{name = Name, attrs = Attrs, children = Els}, HeaderName, HeaderNS, HeaderEls) ->
     HeaderEl = #xmlel{name = HeaderName,
-                     attrs = [{<<"xmlns">>, HeaderNS}],
-                     children = HeaderEls},
+           attrs = [{<<"xmlns">>, HeaderNS}],
+           children = HeaderEls},
     #xmlel{name = Name, attrs = Attrs,
-          children = lists:append(Els, [HeaderEl])}.
-
-%% Removed multiple <header name=Collection>Foo</header/> elements
-%% Didn't seem compliant, but not sure. Confirmation required.
-%% cf. http://xmpp.org/extensions/xep-0248.html#notify
-%%
-%% "If an item is published to a node which is also included by a collection,
-%%  and an entity is subscribed to that collection with a subscription type of
-%%  "items" (Is there a way to check that currently ?), then the notifications
-%%  generated by the service MUST contain additional information. The <items/>
-%%  element contained in the notification message MUST specify the node
-%%  identifier of the node that generated the notification (not the collection)
-%%  and the <item/> element MUST contain a SHIM header that specifies the node
-%%  identifier of the collection".
+       children = lists:append(Els, [HeaderEl])}.
 
 collection_shim(Node) ->
     [#xmlel{name = <<"header">>,
            attrs = [{<<"name">>, <<"Collection">>}],
            children = [{xmlcdata, Node}]}].
 
-subid_shim(SubIDs) ->
+subid_shim(SubIds) ->
     [#xmlel{name = <<"header">>,
-           attrs = [{<<"name">>, <<"SubID">>}],
-           children = [{xmlcdata, SubID}]}
-     || SubID <- SubIDs].
+           attrs = [{<<"name">>, <<"SubId">>}],
+           children = [{xmlcdata, SubId}]}
+       || SubId <- SubIds].
 
 %% The argument is a list of Jids because this function could be used
 %% with the 'pubsub#replyto' (type=jid-multi) node configuration.
 
 extended_headers(Jids) ->
     [#xmlel{name = <<"address">>,
-           attrs = [{<<"type">>, <<"replyto">>}, {<<"jid">>, Jid}],
-           children = []}
-     || Jid <- Jids].
+           attrs = [{<<"type">>, <<"replyto">>}, {<<"jid">>, Jid}]}
+       || Jid <- Jids].
 
 on_user_offline(_, JID, _) ->
     {User, Server, Resource} = jlib:jid_tolower(JID),
-    case ejabberd_sm:get_user_resources(User, Server) of
+    case user_resources(User, Server) of
        [] -> purge_offline({User, Server, Resource});
-       _  -> true
+       _ -> true
     end.
 
 purge_offline({User, Server, _} = LJID) ->
     Host = host(element(2, LJID)),
     Plugins = plugins(Host),
     Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                case lists:member(<<"retrieve-affiliations">>,
-                                                  features(Type))
-                                    of
-                                  false ->
-                                      {{error,
-                                        extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                       unsupported,
-                                                       <<"retrieve-affiliations">>)},
-                                       Acc};
-                                  true ->
-                                      {result, Affiliations} =
-                                          node_action(Host, Type,
-                                                      get_entity_affiliations,
-                                                      [Host, LJID]),
-                                      {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
+                   case lists:member(<<"retrieve-affiliations">>, plugin_features(Host, Type)) of
+                       false ->
+                           {{error,
+                                   extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
+                                       unsupported, <<"retrieve-affiliations">>)},
+                               Acc};
+                       true ->
+                           {result, Affs} = node_action(Host, Type,
+                                   get_entity_affiliations,
+                                   [Host, LJID]),
+                           {Status, [Affs | Acc]}
+                   end
+           end,
+           {ok, []}, Plugins),
     case Result of
-      {ok, Affiliations} ->
-         lists:foreach(fun ({#pubsub_node{nodeid = {_, NodeId},
-                                          options = Options, type = Type},
-                             Affiliation})
-                               when Affiliation == owner orelse
-                                      Affiliation == publisher ->
-                               Action = fun (#pubsub_node{type = NType,
-                                                          id = NodeIdx}) ->
-                                                node_call(NType, get_items,
-                                                          [NodeIdx,
-                                                           service_jid(Host)])
-                                        end,
-                               case transaction(Host, NodeId, Action,
-                                                sync_dirty)
-                                   of
-                                 {result, {_, []}} -> true;
-                                 {result, {_, Items}} ->
-                                     Features = features(Type),
-                                     case {lists:member(<<"retract-items">>,
-                                                        Features),
-                                           lists:member(<<"persistent-items">>,
-                                                        Features),
-                                           get_option(Options, persist_items),
-                                           get_option(Options, purge_offline)}
-                                         of
-                                       {true, true, true, true} ->
-                                           ForceNotify = get_option(Options,
-                                                                    notify_retract),
-                                           lists:foreach(fun
-                                                           (#pubsub_item{itemid
-                                                                             =
-                                                                             {ItemId,
-                                                                              _},
-                                                                         modification
-                                                                             =
-                                                                             {_,
-                                                                              Modification}}) ->
-                                                               case
-                                                                 Modification
-                                                                   of
-                                                                 {User, Server,
-                                                                  _} ->
-                                                                     delete_item(Host,
-                                                                                 NodeId,
-                                                                                 LJID,
-                                                                                 ItemId,
-                                                                                 ForceNotify);
-                                                                 _ -> true
-                                                               end;
-                                                           (_) -> true
-                                                         end,
-                                                         Items);
-                                       _ -> true
-                                     end;
-                                 Error -> Error
-                               end;
-                           (_) -> true
+       {ok, Affs} ->
+           lists:foreach(fun
+                   ({#pubsub_node{nodeid = {_, Node}, options = Options, type = Type}, Aff})
+                           when Aff == owner orelse Aff == publisher ->
+                       Action = fun (#pubsub_node{type = NType, id = Nidx}) ->
+                               node_call(Host, NType, get_items, [Nidx, service_jid(Host), none])
                        end,
-                       lists:usort(lists:flatten(Affiliations)));
-      {Error, _} -> ?DEBUG("on_user_offline ~p", [Error])
+                       case transaction(Host, Node, Action, sync_dirty) of
+                           {result, {_, {[], _}}} ->
+                               true;
+                           {result, {_, {Items, _}}} ->
+                               Features = plugin_features(Host, Type),
+                               case {lists:member(<<"retract-items">>, Features),
+                                       lists:member(<<"persistent-items">>, Features),
+                                       get_option(Options, persist_items),
+                                       get_option(Options, purge_offline)}
+                               of
+                                   {true, true, true, true} ->
+                                       ForceNotify = get_option(Options, notify_retract),
+                                       lists:foreach(fun
+                                               (#pubsub_item{itemid = {ItemId, _},
+                                                               modification = {_, {U, S, _}}})
+                                                       when (U == User) and (S == Server) ->
+                                                   delete_item(Host, Node, LJID, ItemId, ForceNotify);
+                                               (_) ->
+                                                   true
+                                           end,
+                                           Items);
+                                   _ ->
+                                       true
+                               end;
+                           Error ->
+                               Error
+                       end;
+                   (_) ->
+                       true
+               end,
+               lists:usort(lists:flatten(Affs)));
+       {Error, _} ->
+           ?DEBUG("on_user_offline ~p", [Error])
     end.
-
-
-%% REVIEW:
-%% * this code takes NODEID from Itemid2, and forgets about Nodeidx
-%% * this code assumes Payload only contains one xmlelement()
-%% * PUBLISHER is taken from Creation
-export(_Server) ->
-    [{pubsub_item,
-      fun(_Host, #pubsub_item{itemid = {Itemid1, Itemid2},
-                              %nodeidx = _Nodeidx,
-                              creation = {{C1, C2, C3}, Cusr},
-                              modification = {{M1, M2, M3}, _Musr},
-                              payload = Payload}) ->
-              ITEMID = ejabberd_odbc:escape(Itemid1),
-              NODEID = integer_to_list(Itemid2),
-              CREATION = ejabberd_odbc:escape(
-                string:join([string:right(integer_to_list(I),6,$0)||I<-[C1,C2,C3]],":")),
-              MODIFICATION = ejabberd_odbc:escape(
-                string:join([string:right(integer_to_list(I),6,$0)||I<-[M1,M2,M3]],":")),
-              PUBLISHER = ejabberd_odbc:escape(jlib:jid_to_string(Cusr)),
-              [PayloadEl] = [El || {xmlelement,_,_,_} = El <- Payload],
-              PAYLOAD = ejabberd_odbc:escape(xml:element_to_binary(PayloadEl)),
-              ["delete from pubsub_item where itemid='", ITEMID, "';\n"
-               "insert into pubsub_item(itemid,nodeid,creation,modification,publisher,payload) \n"
-               " values ('", ITEMID, "', ", NODEID, ", '", CREATION, "', '",
-                 MODIFICATION, "', '", PUBLISHER, "', '", PAYLOAD, "');\n"];
-         (_Host, _R) ->
-              []
-      end},
-%% REVIEW:
-%% * From the mnesia table, the #pubsub_state.items is not used in ODBC
-%% * Right now AFFILIATION is the first letter of Affiliation
-%% * Right now SUBSCRIPTIONS expects only one Subscription
-%% * Right now SUBSCRIPTIONS letter is the first letter of Subscription
-      {pubsub_state,
-      fun(_Host, #pubsub_state{stateid = {Jid, Stateid},
-                               %nodeidx = Nodeidx,
-                               items = _Items,
-                               affiliation = Affiliation,
-                               subscriptions = Subscriptions}) ->
-              STATEID = integer_to_list(Stateid),
-              JID = ejabberd_odbc:escape(jlib:jid_to_string(Jid)),
-              NODEID = "unknown", %% TODO: integer_to_list(Nodeidx),
-              AFFILIATION = string:substr(atom_to_list(Affiliation),1,1),
-              SUBSCRIPTIONS = parse_subscriptions(Subscriptions),
-              ["delete from pubsub_state where stateid='", STATEID, "';\n"
-               "insert into pubsub_state(stateid,jid,nodeid,affiliation,subscriptions) \n"
-               " values (", STATEID, ", '", JID, "', ", NODEID, ", '",
-                AFFILIATION, "', '", SUBSCRIPTIONS, "');\n"];
-         (_Host, _R) ->
-              []
-      end},
-
-%% REVIEW:
-%% * Parents is not migrated to PARENTs
-%% * Probably some option VALs are not correctly represented in mysql
-      {pubsub_node,
-      fun(_Host, #pubsub_node{nodeid = {Hostid, Nodeid},
-                              id = Id,
-                              parents = _Parents,
-                              type = Type,
-                              owners = Owners,
-                              options = Options}) ->
-              HOST = case Hostid of
-                    {U,S,R} -> ejabberd_odbc:escape(jlib:jid_to_string({U,S,R}));
-                    _ -> ejabberd_odbc:escape(Hostid)
-                    end,
-              NODE = ejabberd_odbc:escape(Nodeid),
-              NODEID = integer_to_list(Id),
-              PARENT = "",
-              TYPE = ejabberd_odbc:escape(Type++"_odbc"),
-              ["delete from pubsub_node where nodeid='", NODEID, "';\n"
-               "insert into pubsub_node(host,node,nodeid,parent,type) \n"
-               " values ('", HOST, "', '", NODE, "', ", NODEID, ", '", PARENT, "', '", TYPE, "');\n"
-               "delete from pubsub_node_option where nodeid='", NODEID, "';\n",
-               [["insert into pubsub_node_option(nodeid,name,val)\n"
-                 " values (", NODEID, ", '", atom_to_list(Name), "', '",
-                           io_lib:format("~p", [Val]), "');\n"] || {Name,Val} <- Options],
-               "delete from pubsub_node_owner where nodeid='", NODEID, "';\n",
-               [["insert into pubsub_node_owner(nodeid,owner)\n"
-                 " values (", NODEID, ", '", jlib:jid_to_string(Usr), "');\n"] || Usr <- Owners],"\n"];
-         (_Host, _R) ->
-              []
-      end}].
-
-parse_subscriptions([]) ->
-    "";
-parse_subscriptions([{State, Item}]) ->
-    STATE = case State of
-        subscribed -> "s"
-    end,
-    string:join([STATE, Item],":").
diff --git a/src/mod_pubsub_odbc.erl b/src/mod_pubsub_odbc.erl
deleted file mode 100644 (file)
index 8b32b83..0000000
+++ /dev/null
@@ -1,5103 +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
-%%% ====================================================================
-
-%%% @doc The module <strong>{@module}</strong> is the core of the PubSub
-%%% extension. It relies on PubSub plugins for a large part of its functions.
-%%%
-%%% @headerfile "pubsub.hrl"
-%%%
-%%% @reference See <a href="http://www.xmpp.org/extensions/xep-0060.html">XEP-0060: Pubsub</a> for
-%%% the latest version of the PubSub specification.
-%%% This module uses version 1.12 of the specification as a base.
-%%% Most of the specification is implemented.
-%%% Functions concerning configuration should be rewritten.
-%%%
-%%% Support for subscription-options and multi-subscribe features was
-%%% added by Brian Cully (bjc AT kublai.com). Subscriptions and options are
-%%% stored in the pubsub_subscription table, with a link to them provided
-%%% by the subscriptions field of pubsub_state. For information on
-%%% subscription-options and mulit-subscribe see XEP-0060 sections 6.1.6,
-%%% 6.2.3.1, 6.2.3.5, and 6.3. For information on subscription leases see
-%%% XEP-0060 section 12.18.
-
--module(mod_pubsub_odbc).
-
--author('christophe.romain@process-one.net').
-
--version('1.13-0').
-
--behaviour(gen_server).
-
--behaviour(gen_mod).
-
--include("ejabberd.hrl").
--include("logger.hrl").
-
--include("adhoc.hrl").
-
--include("jlib.hrl").
-
--include("pubsub.hrl").
-
--define(STDTREE, <<"tree">>).
-
--define(STDNODE, <<"flat">>).
-
--define(PEPNODE, <<"pep">>).
-
-%% exports for hooks
--export([presence_probe/3, caps_update/3,
-        in_subscription/6, out_subscription/4,
-        on_user_offline/3, remove_user/2,
-        disco_local_identity/5, disco_local_features/5,
-        disco_local_items/5, disco_sm_identity/5,
-        disco_sm_features/5, disco_sm_items/5]).
-
-%% exported iq handlers
--export([iq_sm/3]).
-
-%% exports for console debug manual use
--export([create_node/5,
-        delete_node/3,
-        subscribe_node/5,
-        unsubscribe_node/5,
-        publish_item/6,
-        delete_item/4,
-        send_items/7,
-        get_items/2,
-        get_item/3,
-        get_cached_item/2,
-        broadcast_stanza/9,
-        get_configure/5,
-        set_configure/5,
-        tree_action/3,
-        node_action/4
-       ]).
-
-%% general helpers for plugins
--export([subscription_to_string/1, affiliation_to_string/1,
-        string_to_subscription/1, string_to_affiliation/1,
-        extended_error/2, extended_error/3,
-        escape/1]).
-
-%% API and gen_server callbacks
--export([start_link/2, start/2, stop/1, init/1,
-        handle_call/3, handle_cast/2, handle_info/2,
-        terminate/2, code_change/3]).
-
-%% calls for parallel sending of last items
--export([send_loop/1]).
-
--define(PROCNAME, ejabberd_mod_pubsub_odbc).
-
--define(LOOPNAME, ejabberd_mod_pubsub_loop).
-
-%%====================================================================
-%% API
-%%====================================================================
-%%--------------------------------------------------------------------
-%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
-%% Description: Starts the server
-%%--------------------------------------------------------------------
--define(PLUGIN_PREFIX, <<"node_">>).
-
--define(TREE_PREFIX, <<"nodetree_">>).
-
--define(ODBC_SUFFIX, <<"_odbc">>).
-
-%
--export_type([
-    host/0,
-    hostPubsub/0,
-    hostPEP/0,
-    %%
-    nodeIdx/0,
-    nodeId/0,
-    itemId/0,
-    subId/0,
-    payload/0,
-    %%
-    nodeOption/0,
-    nodeOptions/0,
-    subOption/0,
-    subOptions/0,
-    %%
-    affiliation/0,
-    subscription/0,
-    accessModel/0,
-    publishModel/0
-]).
-
-%% -type payload() defined here because the -type xmlel() is not accessible
-%% from pubsub.hrl
--type(payload() :: [] | [xmlel(),...]).
-
--export_type([
-    pubsubNode/0,
-    pubsubState/0,
-    pubsubItem/0,
-    pubsubSubscription/0,
-    pubsubLastItem/0
-]).
-
--type(pubsubNode() ::
-    #pubsub_node{
-        nodeid  :: {Host::mod_pubsub:host(), NodeId::mod_pubsub:nodeId()},
-        id      :: mod_pubsub:nodeIdx(),
-        parents :: [Parent_NodeId::mod_pubsub:nodeId()],
-        type    :: binary(),
-        owners  :: [Owner::ljid(),...],
-        options :: mod_pubsub:nodeOptions()
-    }
-).
-
--type(pubsubState() ::
-    #pubsub_state{
-        stateid       :: {Entity::ljid(), NodeIdx::mod_pubsub:nodeIdx()},
-        items         :: [ItemId::mod_pubsub:itemId()],
-        affiliation   :: mod_pubsub:affiliation(),
-        subscriptions :: [{mod_pubsub:subscription(), mod_pubsub:subId()}]
-    }
-).
-
--type(pubsubItem() ::
-    #pubsub_item{
-        itemid       :: {mod_pubsub:itemId(), mod_pubsub:nodeIdx()},
-        creation     :: {erlang:timestamp(), ljid()},
-        modification :: {erlang:timestamp(), ljid()},
-        payload      :: mod_pubsub:payload()
-    }
-).
-
--type(pubsubSubscription() ::
-    #pubsub_subscription{
-        subid   :: mod_pubsub:subId(),
-        options :: [] | mod_pubsub:subOptions()
-    }
-).
-
--type(pubsubLastItem() ::
-    #pubsub_last_item{
-        nodeid   :: mod_pubsub:nodeIdx(),
-        itemid   :: mod_pubsub:itemId(),
-        creation :: {erlang:timestamp(), ljid()},
-        payload  :: mod_pubsub:payload()
-    }
-).
-
--record(state,
-{
-    server_host,
-    host,
-    access,
-    pep_mapping             = [],
-    ignore_pep_from_offline = true,
-    last_item_cache         = false,
-    max_items_node          = ?MAXITEMS,
-    nodetree                = ?STDTREE,
-    plugins                 = [?STDNODE]
-}).
-
--type(state() ::
-    #state{
-        server_host             :: binary(),
-        host                    :: mod_pubsub:hostPubsub(),
-        access                  :: atom(),
-        pep_mapping             :: [{binary(), binary()}],
-        ignore_pep_from_offline :: boolean(),
-        last_item_cache         :: boolean(),
-        max_items_node          :: non_neg_integer(),
-        nodetree                :: binary(),
-        plugins                 :: [binary(),...]
-    }
-
-).
-
-
-start_link(Host, Opts) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:start_link({local, Proc}, ?MODULE,
-                         [Host, Opts], []).
-
-start(Host, Opts) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
-                transient, 1000, worker, [?MODULE]},
-    supervisor:start_child(ejabberd_sup, ChildSpec).
-
-stop(Host) ->
-    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
-    gen_server:call(Proc, stop),
-    supervisor:delete_child(ejabberd_sup, Proc).
-
-%%====================================================================
-%% gen_server callbacks
-%%====================================================================
-
-%%--------------------------------------------------------------------
-%% Function: init(Args) -> {ok, State} |
-%%                      {ok, State, Timeout} |
-%%                      ignore        |
-%%                      {stop, Reason}
-%% Description: Initiates the server
-%%--------------------------------------------------------------------
--spec(init/1 ::
-(
-    _:: _)
-    -> {ok, state()}
-).
-
-init([ServerHost, Opts]) ->
-    ?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
-    Host = gen_mod:get_opt_host(ServerHost, Opts, <<"pubsub.@HOST@">>),
-    Access = gen_mod:get_opt(access_createnode, Opts,
-        fun(A) when is_atom(A) -> A end, all),
-    PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts,
-                                fun(A) when is_boolean(A) -> A end, true),
-    IQDisc = gen_mod:get_opt(iqdisc, Opts,
-                fun(A) when is_atom(A) -> A end, one_queue),
-    LastItemCache = gen_mod:get_opt(last_item_cache, Opts,
-                                   fun(A) when is_boolean(A) -> A end, false),
-    MaxItemsNode = gen_mod:get_opt(max_items_node, Opts,
-                                  fun(A) when is_integer(A) andalso A >= 0 -> A end, ?MAXITEMS),
-    pubsub_index:init(Host, ServerHost, Opts),
-    ets:new(gen_mod:get_module_proc(Host, config),
-           [set, named_table]),
-    ets:new(gen_mod:get_module_proc(ServerHost, config),
-           [set, named_table]),
-    {Plugins, NodeTree, PepMapping} = init_plugins(Host,
-                                                  ServerHost, Opts),
-    mnesia:create_table(pubsub_last_item,
-                       [{ram_copies, [node()]},
-                        {attributes, record_info(fields, pubsub_last_item)}]),
-    mod_disco:register_feature(ServerHost, ?NS_PUBSUB),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {nodetree, NodeTree}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {plugins, Plugins}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {last_item_cache, LastItemCache}),
-    ets:insert(gen_mod:get_module_proc(Host, config),
-              {max_items_node, MaxItemsNode}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {nodetree, NodeTree}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {plugins, Plugins}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {last_item_cache, LastItemCache}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {max_items_node, MaxItemsNode}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {pep_mapping, PepMapping}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {ignore_pep_from_offline, PepOffline}),
-    ets:insert(gen_mod:get_module_proc(ServerHost, config),
-              {host, Host}),
-    ejabberd_hooks:add(sm_remove_connection_hook,
-                      ServerHost, ?MODULE, on_user_offline, 75),
-    ejabberd_hooks:add(disco_local_identity, ServerHost,
-                      ?MODULE, disco_local_identity, 75),
-    ejabberd_hooks:add(disco_local_features, ServerHost,
-                      ?MODULE, disco_local_features, 75),
-    ejabberd_hooks:add(disco_local_items, ServerHost,
-                      ?MODULE, disco_local_items, 75),
-    ejabberd_hooks:add(presence_probe_hook, ServerHost,
-                      ?MODULE, presence_probe, 80),
-    ejabberd_hooks:add(roster_in_subscription, ServerHost,
-                      ?MODULE, in_subscription, 50),
-    ejabberd_hooks:add(roster_out_subscription, ServerHost,
-                      ?MODULE, out_subscription, 50),
-    ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
-                      remove_user, 50),
-    ejabberd_hooks:add(anonymous_purge_hook, ServerHost,
-                      ?MODULE, remove_user, 50),
-    case lists:member(?PEPNODE, Plugins) of
-      true ->
-         ejabberd_hooks:add(caps_update, ServerHost, ?MODULE,
-                            caps_update, 80),
-         ejabberd_hooks:add(disco_sm_identity, ServerHost,
-                            ?MODULE, disco_sm_identity, 75),
-         ejabberd_hooks:add(disco_sm_features, ServerHost,
-                            ?MODULE, disco_sm_features, 75),
-         ejabberd_hooks:add(disco_sm_items, ServerHost, ?MODULE,
-                            disco_sm_items, 75),
-         gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
-                                       ?NS_PUBSUB, ?MODULE, iq_sm, IQDisc),
-         gen_iq_handler:add_iq_handler(ejabberd_sm, ServerHost,
-                                       ?NS_PUBSUB_OWNER, ?MODULE, iq_sm,
-                                       IQDisc);
-      false -> ok
-    end,
-    ejabberd_router:register_route(Host),
-    put(server_host, ServerHost),
-    init_nodes(Host, ServerHost, NodeTree, Plugins),
-    State = #state{host = Host, server_host = ServerHost,
-                  access = Access, pep_mapping = PepMapping,
-                  ignore_pep_from_offline = PepOffline,
-                  last_item_cache = LastItemCache,
-                  max_items_node = MaxItemsNode, nodetree = NodeTree,
-                  plugins = Plugins},
-    init_send_loop(ServerHost, State),
-    {ok, State}.
-
-init_send_loop(ServerHost, State) ->
-    Proc = gen_mod:get_module_proc(ServerHost, ?LOOPNAME),
-    SendLoop = spawn(?MODULE, send_loop, [State]),
-    register(Proc, SendLoop),
-    SendLoop.
-
-%% @spec (Host, ServerHost, Opts) -> Plugins
-%%      Host = mod_pubsub:host()   Opts = [{Key,Value}]
-%%      ServerHost = host()
-%%      Key = atom()
-%%      Value = term()
-%%      Plugins = [Plugin::string()]
-%% @doc Call the init/1 function for each plugin declared in the config file.
-%% The default plugin module is implicit.
-%% <p>The Erlang code for the plugin is located in a module called
-%% <em>node_plugin</em>. The 'node_' prefix is mandatory.</p>
-%% <p>The modules are initialized in alphetical order and the list is checked
-%% and sorted to ensure that each module is initialized only once.</p>
-%% <p>See {@link node_hometree:init/1} for an example implementation.</p>
-init_plugins(Host, ServerHost, Opts) ->
-    TreePlugin =
-       jlib:binary_to_atom(<<(?TREE_PREFIX)/binary,
-                               (gen_mod:get_opt(nodetree, Opts, fun(A) when is_binary(A) -> A end,
-                                                ?STDTREE))/binary,
-                               (?ODBC_SUFFIX)/binary>>),
-    ?DEBUG("** tree plugin is ~p", [TreePlugin]),
-    TreePlugin:init(Host, ServerHost, Opts),
-    Plugins = gen_mod:get_opt(plugins, Opts,
-        fun(A) when is_list(A) -> A end, [?STDNODE]),
-    PepMapping = gen_mod:get_opt(pep_mapping, Opts,
-        fun(A) when is_list(A) -> A end, []),
-    ?DEBUG("** PEP Mapping : ~p~n", [PepMapping]),
-    PluginsOK = lists:foldl(fun (Name, Acc) ->
-                                   Plugin =
-                                       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                                               Name/binary,
-                                                               (?ODBC_SUFFIX)/binary>>),
-                                   case catch apply(Plugin, init,
-                                                    [Host, ServerHost, Opts])
-                                       of
-                                     {'EXIT', _Error} -> Acc;
-                                     _ ->
-                                         ?DEBUG("** init ~s plugin", [Name]),
-                                         [Name | Acc]
-                                   end
-                           end,
-                           [], Plugins),
-    {lists:reverse(PluginsOK), TreePlugin, PepMapping}.
-
-terminate_plugins(Host, ServerHost, Plugins,
-                 TreePlugin) ->
-    lists:foreach(fun (Name) ->
-                         ?DEBUG("** terminate ~s plugin", [Name]),
-                         Plugin =
-                             jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                                     Name/binary,
-                                                     (?ODBC_SUFFIX)/binary>>),
-                         Plugin:terminate(Host, ServerHost)
-                 end,
-                 Plugins),
-    TreePlugin:terminate(Host, ServerHost),
-    ok.
-
-init_nodes(Host, ServerHost, _NodeTree, Plugins) ->
-    case lists:member(<<"hometree_odbc">>, Plugins) of
-      true ->
-           create_node(Host, ServerHost, <<"/home">>, service_jid(Host), <<"hometree_odbc">>),
-           create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, service_jid(Host),
-             <<"hometree_odbc">>);
-      false -> ok
-    end.
-
-send_loop(State) ->
-    receive
-      {presence, JID, Pid} ->
-         Host = State#state.host,
-         ServerHost = State#state.server_host,
-         LJID = jlib:jid_tolower(JID),
-         BJID = jlib:jid_remove_resource(LJID),
-         lists:foreach(fun (PType) ->
-                               Subscriptions = case catch node_action(Host,
-                                                                     PType,
-                                                                     get_entity_subscriptions_for_send_last,
-                                                                     [Host, JID]) of
-                                                               {result, S} -> S;
-                                                               _ -> []
-                                                       end,
-                               lists:foreach(fun ({Node, subscribed, _,
-                                                   SubJID}) ->
-                                                     if (SubJID == LJID) or
-                                                          (SubJID == BJID) ->
-                                                            #pubsub_node{nodeid
-                                                                             =
-                                                                             {H,
-                                                                              N},
-                                                                         type =
-                                                                             Type,
-                                                                         id =
-                                                                             NodeId,
-                                                                         options
-                                                                             =
-                                                                             Options} =
-                                                                Node,
-                                                               send_items(H,
-                                                                             N,
-                                                                             NodeId,
-                                                                             Type,
-                                                                             Options,
-                                                                             LJID,
-                                                                             last);
-                                                        true ->
-                                                            % resource not concerned about that subscription
-                                                            ok
-                                                     end;
-                                                 (_) -> ok
-                                             end,
-                                             lists:usort(Subscriptions))
-                       end,
-                       State#state.plugins),
-         if not State#state.ignore_pep_from_offline ->
-                {User, Server, Resource} = jlib:jid_tolower(JID),
-                case catch ejabberd_c2s:get_subscribed(Pid) of
-                  Contacts when is_list(Contacts) ->
-                      lists:foreach(fun ({U, S, R}) ->
-                                            case S of
-                                              ServerHost ->  %% local contacts
-                                                  case user_resources(U, S) of
-                                                    [] -> %% offline
-                                                        PeerJID =
-                                                            jlib:make_jid(U, S,
-                                                                          R),
-                                                        self() !
-                                                          {presence, User,
-                                                           Server, [Resource],
-                                                           PeerJID};
-                                                    _ -> %% online
-                                                        % this is already handled by presence probe
-                                                        ok
-                                                  end;
-                                              _ ->  %% remote contacts
-                                                  % we can not do anything in any cases
-                                                  ok
-                                            end
-                                    end,
-                                    Contacts);
-                  _ -> ok
-                end;
-            true -> ok
-         end,
-         send_loop(State);
-      {presence, User, Server, Resources, JID} ->
-         spawn(fun () ->
-                       Host = State#state.host,
-                       Owner = jlib:jid_remove_resource(jlib:jid_tolower(JID)),
-                       lists:foreach(fun (#pubsub_node{nodeid = {_, Node},
-                                                       type = Type,
-                                                       id = NodeId,
-                                                       options = Options}) ->
-                                             case get_option(Options,
-                                                             send_last_published_item)
-                                                 of
-                                               on_sub_and_presence ->
-                                                   lists:foreach(fun
-                                                                   (Resource) ->
-                                                                       LJID =
-                                                                           {User,
-                                                                            Server,
-                                                                            Resource},
-                                                                       Subscribed =
-                                                                           case
-                                                                             get_option(Options,
-                                                                                        access_model)
-                                                                               of
-                                                                             open ->
-                                                                                 true;
-                                                                             presence ->
-                                                                                 true;
-                                                                             whitelist ->
-                                                                                 false; % subscribers are added manually
-                                                                             authorize ->
-                                                                                 false; % likewise
-                                                                             roster ->
-                                                                                 Grps =
-                                                                                     get_option(Options,
-                                                                                                roster_groups_allowed,
-                                                                                                []),
-                                                                                 {OU,
-                                                                                  OS,
-                                                                                  _} =
-                                                                                     Owner,
-                                                                                 element(2,
-                                                                                         get_roster_info(OU,
-                                                                                                         OS,
-                                                                                                         LJID,
-                                                                                                         Grps))
-                                                                           end,
-                                                                       if
-                                                                         Subscribed ->
-                                                                             send_items(Owner,
-                                                                                        Node,
-                                                                                        NodeId,
-                                                                                        Type,
-                                                                                        Options,
-                                                                                        LJID,
-                                                                                        last);
-                                                                         true ->
-                                                                             ok
-                                                                       end
-                                                                 end,
-                                                                 Resources);
-                                               _ -> ok
-                                             end
-                                     end,
-                                     tree_action(Host, get_nodes,
-                                                 [Owner, JID]))
-               end),
-         send_loop(State);
-      stop -> ok
-    end.
-
-%% -------
-%% disco hooks handling functions
-%%
-
--spec(disco_local_identity/5 ::
-(
-  Acc    :: [xmlel()],
-  _From  :: jid(),
-  To     :: jid(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  Lang   :: binary())
-    -> [xmlel()]
-).
-disco_local_identity(Acc, _From, To, <<>>, _Lang) ->
-    case lists:member(?PEPNODE, plugins(To#jid.lserver)) of
-      true ->
-         [#xmlel{name = <<"identity">>,
-                 attrs =
-                     [{<<"category">>, <<"pubsub">>},
-                      {<<"type">>, <<"pep">>}],
-                 children = []}
-          | Acc];
-      false -> Acc
-    end;
-disco_local_identity(Acc, _From, _To, _Node, _Lang) ->
-    Acc.
-
--spec(disco_local_features/5 ::
-(
-  Acc    :: [xmlel()],
-  _From  :: jid(),
-  To     :: jid(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  Lang   :: binary())
-    -> [binary(),...]
-).
-disco_local_features(Acc, _From, To, <<>>, _Lang) ->
-    Host = To#jid.lserver,
-    Feats = case Acc of
-             {result, I} -> I;
-             _ -> []
-           end,
-    {result,
-     Feats ++
-       lists:map(fun (Feature) ->
-                        <<(?NS_PUBSUB)/binary, "#", Feature/binary>>
-                end,
-                features(Host, <<>>))};
-disco_local_features(Acc, _From, _To, _Node, _Lang) ->
-    Acc.
-
-disco_local_items(Acc, _From, _To, <<>>, _Lang) -> Acc;
-disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc.
-
-%disco_sm_identity(Acc, From, To, Node, Lang)
-%    when is_binary(Node) ->
-%    disco_sm_identity(Acc, From, To, iolist_to_binary(Node),
-%                    Lang);
--spec(disco_sm_identity/5 ::
-(
-  Acc  :: empty | [xmlel()],
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
-    -> [xmlel()]
-).
-disco_sm_identity(empty, From, To, Node, Lang) ->
-    disco_sm_identity([], From, To, Node, Lang);
-disco_sm_identity(Acc, From, To, Node, _Lang) ->
-    disco_identity(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)
-      ++ Acc.
-
-disco_identity(_Host, <<>>, _From) ->
-    [#xmlel{name = <<"identity">>,
-           attrs =
-               [{<<"category">>, <<"pubsub">>},
-                {<<"type">>, <<"pep">>}],
-           children = []}];
-disco_identity(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options}) ->
-                    Owners = node_owners_call(Type, Idx),
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          {result,
-                           [#xmlel{name = <<"identity">>,
-                                   attrs =
-                                       [{<<"category">>, <<"pubsub">>},
-                                        {<<"type">>, <<"pep">>}],
-                                   children = []},
-                            #xmlel{name = <<"identity">>,
-                                   attrs =
-                                       [{<<"category">>, <<"pubsub">>},
-                                        {<<"type">>, <<"leaf">>}
-                                        | case get_option(Options, title) of
-                                            false -> [];
-                                            [Title] -> [{<<"name">>, Title}]
-                                          end],
-                                   children = []}]};
-                      _ -> {result, []}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
-    end.
-
--spec(disco_sm_features/5 ::
-(
-  Acc  :: empty | {result, Features::[Feature::binary()]},
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
-    -> {result, Features::[Feature::binary()]}
-).
-%disco_sm_features(Acc, From, To, Node, Lang)
-%    when is_binary(Node) ->
-%    disco_sm_features(Acc, From, To, iolist_to_binary(Node),
-%                    Lang);
-disco_sm_features(empty, From, To, Node, Lang) ->
-    disco_sm_features({result, []}, From, To, Node, Lang);
-disco_sm_features({result, OtherFeatures} = _Acc, From, To, Node, _Lang) ->
-    {result,
-     OtherFeatures ++
-       disco_features(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From)};
-disco_sm_features(Acc, _From, _To, _Node, _Lang) -> Acc.
-
-disco_features(_Host, <<>>, _From) ->
-    [?NS_PUBSUB | [<<(?NS_PUBSUB)/binary, "#", Feature/binary>>
-        || Feature <- features(<<"pep">>)]];
-disco_features(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options}) ->
-                    Owners = node_owners_call(Type, Idx),
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          {result,
-                           [?NS_PUBSUB | [<<(?NS_PUBSUB)/binary, "#",
-                                            Feature/binary>>
-                                          || Feature <- features(<<"pep">>)]]};
-                      _ -> {result, []}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
-    end.
-
--spec(disco_sm_items/5 ::
-(
-  Acc  :: empty | {result, [xmlel()]},
-  From :: jid(),
-  To   :: jid(),
-  Node :: mod_pubsub:nodeId(),
-  Lang :: binary())
-    -> {result, [xmlel()]}
-).
-%disco_sm_items(Acc, From, To, Node, Lang)
-%    when is_binary(Node) ->
-%    disco_sm_items(Acc, From, To, iolist_to_binary(Node),
-%                 Lang);
-disco_sm_items(empty, From, To, Node, Lang) ->
-    disco_sm_items({result, []}, From, To, Node, Lang);
-disco_sm_items({result, OtherItems}, From, To, Node, _Lang) ->
-    {result,
-     lists:usort(OtherItems ++
-               disco_items(jlib:jid_tolower(jlib:jid_remove_resource(To)), Node, From))};
-disco_sm_items(Acc, _From, _To, _Node, _Lang) -> Acc.
-
--spec(disco_items/3 ::
-(
-  Host :: mod_pubsub:host(),
-  Node :: mod_pubsub:nodeId(),
-  From :: jid())
-    -> [xmlel()]
-).
-disco_items(Host, <<>>, From) ->
-    Action = fun (#pubsub_node{nodeid = {_, NodeID},
-                              options = Options, type = Type, id = Idx},
-                 Acc) ->
-                    Owners = node_owners_call(Type, Idx),
-                    case get_allowed_items_call(Host, Idx, From, Type, Options, Owners) of
-                      {result, _} ->
-                          [#xmlel{name = <<"item">>,
-                                  attrs =
-                                      [{<<"node">>, (NodeID)},
-                                       {<<"jid">>,
-                                        case Host of
-                                          {_, _, _} ->
-                                              jlib:jid_to_string(Host);
-                                          _Host -> Host
-                                        end}
-                                       | case get_option(Options, title) of
-                                           false -> [];
-                                           [Title] -> [{<<"name">>, Title}]
-                                         end],
-                                  children = []}
-                           | Acc];
-                      _ -> Acc
-                    end
-            end,
-    case transaction_on_nodes(Host, Action, sync_dirty) of
-      {result, Items} -> Items;
-      _ -> []
-    end;
-disco_items(Host, Node, From) ->
-    Action = fun (#pubsub_node{id = Idx, type = Type,
-                              options = Options}) ->
-                    Owners = node_owners_call(Type, Idx),
-                    case get_allowed_items_call(Host, Idx, From, Type,
-                                                Options, Owners)
-                        of
-                      {result, Items} ->
-                          {result,
-                           [#xmlel{name = <<"item">>,
-                                   attrs =
-                                       [{<<"jid">>,
-                                         case Host of
-                                           {_, _, _} ->
-                                               jlib:jid_to_string(Host);
-                                           _Host -> Host
-                                         end},
-                                        {<<"name">>, ItemID}],
-                                   children = []}
-                            || #pubsub_item{itemid = {ItemID, _}} <- Items]};
-                      _ -> {result, []}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> Result;
-      _ -> []
-    end.
-
-%% -------
-%% presence hooks handling functions
-%%
-
-caps_update(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = Host} = JID, _Features)
-       when Host =/= S ->
-    presence(Host, {presence, U, S, [R], JID});
-caps_update(_From, _To, _Feature) ->
-    ok.
-
-presence_probe(#jid{luser = U, lserver = S, lresource = R} = JID, JID, Pid) ->
-    presence(S, {presence, JID, Pid}),
-    presence(S, {presence, U, S, [R], JID});
-presence_probe(#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, _Pid) ->
-    %% ignore presence_probe from my other ressources
-    %% to not get duplicated last items
-    ok;
-presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = S} = JID, _Pid) ->
-    presence(S, {presence, U, S, [R], JID});
-presence_probe(_Host, _JID, _Pid) ->
-    %% ignore presence_probe from remote contacts,
-    %% those are handled via caps_update
-    ok.
-
-presence(ServerHost, Presence) ->
-    SendLoop = case
-                whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME))
-                  of
-                undefined ->
-                    Host = host(ServerHost),
-                    Plugins = plugins(Host),
-                    PepOffline = case catch
-                                        ets:lookup(gen_mod:get_module_proc(ServerHost,
-                                                                           config),
-                                                   ignore_pep_from_offline)
-                                     of
-                                   [{ignore_pep_from_offline, PO}] -> PO;
-                                   _ -> true
-                                 end,
-                    State = #state{host = Host, server_host = ServerHost,
-                                   ignore_pep_from_offline = PepOffline,
-                                   plugins = Plugins},
-                    init_send_loop(ServerHost, State);
-                Pid -> Pid
-              end,
-    SendLoop ! Presence.
-
-%% -------
-%% subscription hooks handling functions
-%%
-
-out_subscription(User, Server, JID, subscribed) ->
-    Owner = jlib:make_jid(User, Server, <<"">>),
-    {PUser, PServer, PResource} = jlib:jid_tolower(JID),
-    PResources = case PResource of
-                  <<>> -> user_resources(PUser, PServer);
-                  _ -> [PResource]
-                end,
-    presence(Server,
-            {presence, PUser, PServer, PResources, Owner}),
-    true;
-out_subscription(_, _, _, _) -> true.
-
-in_subscription(_, User, Server, Owner, unsubscribed,
-               _) ->
-    unsubscribe_user(jlib:make_jid(User, Server, <<"">>),
-                    Owner),
-    true;
-in_subscription(_, _, _, _, _, _) -> true.
-
-unsubscribe_user(Entity, Owner) ->
-    BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    Host = host(element(2, BJID)),
-    spawn(fun () ->
-                 lists:foreach(fun (PType) ->
-                                       case node_action(Host, PType,
-                                                       get_entity_subscriptions,
-                                                       [Host, Entity]) of
-                                               {result, Subscriptions} ->
-                                       lists:foreach(fun ({#pubsub_node{options
-                                                                            =
-                                                                            Options,
-                                                                        id =
-                                                                            NodeId},
-                                                           subscribed, _,
-                                                           JID}) ->
-                                                             case
-                                                               get_option(Options,
-                                                                          access_model)
-                                                                 of
-                                                               presence ->
-                                                                   case
-                                                                     lists:member(BJID,
-                                                                                  node_owners(Host, PType, NodeId))
-                                                                       of
-                                                                     true ->
-                                                                         node_action(Host,
-                                                                                     PType,
-                                                                                     unsubscribe_node,
-                                                                                     [NodeId,
-                                                                                      Entity,
-                                                                                      JID,
-                                                                                      all]);
-                                                                     false ->
-                                                                         {result,
-                                                                          ok}
-                                                                   end;
-                                                               _ ->
-                                                                   {result, ok}
-                                                             end;
-                                                         (_) -> ok
-                                                     end,
-                                                     Subscriptions);
-                                       Error ->
-                                           ?DEBUG("Error at node_action: ~p", [Error])
-                                       end
-                               end,
-                               plugins(Host))
-         end).
-
-%% -------
-%% user remove hook handling function
-%%
-
-remove_user(User, Server) ->
-    LUser = jlib:nodeprep(User),
-    LServer = jlib:nameprep(Server),
-    Entity = jlib:make_jid(LUser, LServer, <<"">>),
-    Host = host(LServer),
-    HomeTreeBase = <<"/home/", LServer/binary, "/", LUser/binary>>,
-    spawn(fun () ->
-                 lists:foreach(fun (PType) ->
-                                       {result, Subscriptions} =
-                                           node_action(Host, PType,
-                                                       get_entity_subscriptions,
-                                                       [Host, Entity]),
-                                       lists:foreach(fun ({#pubsub_node{id =
-                                                                            NodeId},
-                                                           _, _, JID}) ->
-                                                             node_action(Host,
-                                                                         PType,
-                                                                         unsubscribe_node,
-                                                                         [NodeId,
-                                                                          Entity,
-                                                                          JID,
-                                                                          all]);
-                                                         (_) -> ok
-                                                     end,
-                                                     Subscriptions),
-                                       {result, Affiliations} =
-                                           node_action(Host, PType,
-                                                       get_entity_affiliations,
-                                                       [Host, Entity]),
-                                       lists:foreach(fun ({#pubsub_node{nodeid
-                                                                            =
-                                                                            {H,
-                                                                             N},
-                                                                        parents
-                                                                            =
-                                                                            []},
-                                                           owner}) ->
-                                                             delete_node(H, N,
-                                                                         Entity);
-                                                         ({#pubsub_node{nodeid
-                                                                            =
-                                                                            {H,
-                                                                             N},
-                                                                        type =
-                                                                            <<"hometree">>},
-                                                           owner})
-                                                             when N ==
-                                                                    HomeTreeBase ->
-                                                             delete_node(H, N,
-                                                                         Entity);
-                                                         ({#pubsub_node{id =
-                                                                            NodeId},
-                                                           publisher}) ->
-                                                             node_action(Host,
-                                                                         PType,
-                                                                         set_affiliation,
-                                                                         [NodeId,
-                                                                          Entity,
-                                                                          none]);
-                                                         (_) -> ok
-                                                     end,
-                                                     Affiliations)
-                               end,
-                               plugins(Host))
-         end).
-
-handle_call(server_host, _From, State) ->
-    {reply, State#state.server_host, State};
-handle_call(plugins, _From, State) ->
-    {reply, State#state.plugins, State};
-handle_call(pep_mapping, _From, State) ->
-    {reply, State#state.pep_mapping, State};
-handle_call(nodetree, _From, State) ->
-    {reply, State#state.nodetree, State};
-handle_call(stop, _From, State) ->
-    {stop, normal, ok, State}.
-
-%%--------------------------------------------------------------------
-%% Function: handle_cast(Msg, State) -> {noreply, State} |
-%%                                   {noreply, State, Timeout} |
-%%                                   {stop, Reason, State}
-%% Description: Handling cast messages
-%%--------------------------------------------------------------------
-%% @private
-handle_cast(_Msg, State) -> {noreply, State}.
-
--spec(handle_info/2 ::
-(
-  _     :: {route, From::jid(), To::jid(), Packet::xmlel()},
-  State :: state())
-    -> {noreply, state()}
-).
-
-%%--------------------------------------------------------------------
-%% Function: handle_info(Info, State) -> {noreply, State} |
-%%                                    {noreply, State, Timeout} |
-%%                                    {stop, Reason, State}
-%% Description: Handling all non call/cast messages
-%%--------------------------------------------------------------------
-%% @private
-handle_info({route, From, To, Packet},
-           #state{server_host = ServerHost, access = Access,
-                  plugins = Plugins} =
-               State) ->
-    case catch do_route(ServerHost, Access, Plugins,
-                       To#jid.lserver, From, To, Packet)
-       of
-      {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
-      _ -> ok
-    end,
-    {noreply, State};
-handle_info(_Info, State) ->
-    {noreply, State}.
-
-%%--------------------------------------------------------------------
-%% Function: terminate(Reason, State) -> void()
-%% Description: This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any necessary
-%% cleaning up. When it returns, the gen_server terminates with Reason.
-%% The return value is ignored.
-%%--------------------------------------------------------------------
-%% @private
-terminate(_Reason,
-         #state{host = Host, server_host = ServerHost,
-                nodetree = TreePlugin, plugins = Plugins}) ->
-    ejabberd_router:unregister_route(Host),
-    case lists:member(?PEPNODE, Plugins) of
-      true ->
-         ejabberd_hooks:delete(caps_update, ServerHost, ?MODULE,
-                               caps_update, 80),
-         ejabberd_hooks:delete(disco_sm_identity, ServerHost,
-                               ?MODULE, disco_sm_identity, 75),
-         ejabberd_hooks:delete(disco_sm_features, ServerHost,
-                               ?MODULE, disco_sm_features, 75),
-         ejabberd_hooks:delete(disco_sm_items, ServerHost,
-                               ?MODULE, disco_sm_items, 75),
-         gen_iq_handler:remove_iq_handler(ejabberd_sm,
-                                          ServerHost, ?NS_PUBSUB),
-         gen_iq_handler:remove_iq_handler(ejabberd_sm,
-                                          ServerHost, ?NS_PUBSUB_OWNER);
-      false -> ok
-    end,
-    ejabberd_hooks:delete(sm_remove_connection_hook,
-                         ServerHost, ?MODULE, on_user_offline, 75),
-    ejabberd_hooks:delete(disco_local_identity, ServerHost,
-                         ?MODULE, disco_local_identity, 75),
-    ejabberd_hooks:delete(disco_local_features, ServerHost,
-                         ?MODULE, disco_local_features, 75),
-    ejabberd_hooks:delete(disco_local_items, ServerHost,
-                         ?MODULE, disco_local_items, 75),
-    ejabberd_hooks:delete(presence_probe_hook, ServerHost,
-                         ?MODULE, presence_probe, 80),
-    ejabberd_hooks:delete(roster_in_subscription,
-                         ServerHost, ?MODULE, in_subscription, 50),
-    ejabberd_hooks:delete(roster_out_subscription,
-                         ServerHost, ?MODULE, out_subscription, 50),
-    ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
-                         remove_user, 50),
-    ejabberd_hooks:delete(anonymous_purge_hook, ServerHost,
-                         ?MODULE, remove_user, 50),
-    mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB),
-    gen_mod:get_module_proc(ServerHost, ?LOOPNAME) ! stop,
-    terminate_plugins(Host, ServerHost, Plugins,
-                     TreePlugin).
-
-%%--------------------------------------------------------------------
-%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% Description: Convert process state when code is changed
-%%--------------------------------------------------------------------
-%% @private
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
-
--spec(do_route/7 ::
-(
-  ServerHost :: binary(),
-  Access     :: atom(),
-  Plugins    :: [binary(),...],
-  Host       :: mod_pubsub:hostPubsub(),
-  From       :: jid(),
-  To         :: jid(),
-  Packet     :: xmlel())
-    -> ok
-).
-
-%%--------------------------------------------------------------------
-%%% Internal functions
-%%--------------------------------------------------------------------
-do_route(ServerHost, Access, Plugins, Host, From, To, Packet) ->
-    #xmlel{name = Name, attrs = Attrs} = Packet,
-    case To of
-      #jid{luser = <<"">>, lresource = <<"">>} ->
-         case Name of
-           <<"iq">> ->
-               case jlib:iq_query_info(Packet) of
-                 #iq{type = get, xmlns = ?NS_DISCO_INFO, sub_el = SubEl,
-                     lang = Lang} =
-                     IQ ->
-                     #xmlel{attrs = QAttrs} = SubEl,
-                     Node = xml:get_attr_s(<<"node">>, QAttrs),
-                     Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
-                                                    [],
-                                                    [ServerHost, ?MODULE,
-                                                     <<"">>, <<"">>]),
-                     Res = case iq_disco_info(Host, Node, From, Lang) of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el =
-                                                          [#xmlel{name =
-                                                                      <<"query">>,
-                                                                  attrs =
-                                                                      QAttrs,
-                                                                  children =
-                                                                      IQRes ++
-                                                                        Info}]});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
-                           end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = get, xmlns = ?NS_DISCO_ITEMS,
-                     sub_el = SubEl} =
-                     IQ ->
-                     #xmlel{attrs = QAttrs} = SubEl,
-                     Node = xml:get_attr_s(<<"node">>, QAttrs),
-                     Rsm = jlib:rsm_decode(IQ),
-                     Res = case iq_disco_items(Host, Node, From, Rsm) of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el =
-                                                          [#xmlel{name =
-                                                                      <<"query">>,
-                                                                  attrs =
-                                                                      QAttrs,
-                                                                  children =
-                                                                      IQRes}]})
-%                            {error, Error} ->
-%                                jlib:make_error_reply(Packet, Error)
-                           end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = IQType, xmlns = ?NS_PUBSUB, lang = Lang,
-                     sub_el = SubEl} =
-                     IQ ->
-                     Res = case iq_pubsub(Host, ServerHost, From, IQType,
-                                          SubEl, Lang, Access, Plugins)
-                               of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
-                           end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = IQType, xmlns = ?NS_PUBSUB_OWNER,
-                     lang = Lang, sub_el = SubEl} =
-                     IQ ->
-                     Res = case iq_pubsub_owner(Host, ServerHost, From,
-                                                IQType, SubEl, Lang)
-                               of
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes});
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error)
-                           end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{type = get, xmlns = (?NS_VCARD) = XMLNS,
-                     lang = Lang, sub_el = _SubEl} =
-                     IQ ->
-                     Res = IQ#iq{type = result,
-                                 sub_el =
-                                     [#xmlel{name = <<"vCard">>,
-                                             attrs = [{<<"xmlns">>, XMLNS}],
-                                             children = iq_get_vcard(Lang)}]},
-                     ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
-                 #iq{type = set, xmlns = ?NS_COMMANDS} = IQ ->
-                     Res = case iq_command(Host, ServerHost, From, IQ,
-                                           Access, Plugins)
-                               of
-                             {error, Error} ->
-                                 jlib:make_error_reply(Packet, Error);
-                             {result, IQRes} ->
-                                 jlib:iq_to_xml(IQ#iq{type = result,
-                                                      sub_el = IQRes})
-                           end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{} ->
-                     Err = jlib:make_error_reply(Packet,
-                                                 ?ERR_FEATURE_NOT_IMPLEMENTED),
-                     ejabberd_router:route(To, From, Err);
-                 _ -> ok
-               end;
-           <<"message">> ->
-               case xml:get_attr_s(<<"type">>, Attrs) of
-                 <<"error">> -> ok;
-                 _ ->
-                     case find_authorization_response(Packet) of
-                       none -> ok;
-                       invalid ->
-                           ejabberd_router:route(To, From,
-                                                 jlib:make_error_reply(Packet,
-                                                                       ?ERR_BAD_REQUEST));
-                       XFields ->
-                           handle_authorization_response(Host, From, To,
-                                                         Packet, XFields)
-                     end
-               end;
-           _ -> ok
-         end;
-      _ ->
-         case xml:get_attr_s(<<"type">>, Attrs) of
-           <<"error">> -> ok;
-           <<"result">> -> ok;
-           _ ->
-               Err = jlib:make_error_reply(Packet,
-                                           ?ERR_ITEM_NOT_FOUND),
-               ejabberd_router:route(To, From, Err)
-         end
-    end.
-
-command_disco_info(_Host, ?NS_COMMANDS, _From) ->
-    IdentityEl = #xmlel{name = <<"identity">>,
-                       attrs =
-                           [{<<"category">>, <<"automation">>},
-                            {<<"type">>, <<"command-list">>}],
-                       children = []},
-    {result, [IdentityEl]};
-command_disco_info(_Host, ?NS_PUBSUB_GET_PENDING,
-                  _From) ->
-    IdentityEl = #xmlel{name = <<"identity">>,
-                       attrs =
-                           [{<<"category">>, <<"automation">>},
-                            {<<"type">>, <<"command-node">>}],
-                       children = []},
-    FeaturesEl = #xmlel{name = <<"feature">>,
-                       attrs = [{<<"var">>, ?NS_COMMANDS}], children = []},
-    {result, [IdentityEl, FeaturesEl]}.
-
-node_disco_info(Host, Node, From) ->
-    node_disco_info(Host, Node, From, true, true).
-
-node_disco_info(Host, Node, From, _Identity, _Features) ->
-%    Action =
-%      fun(#pubsub_node{type = Type, id = NodeId}) ->
-%              I = case Identity of
-%                      false ->
-%                          [];
-%                      true ->
-%                          Types =
-%                              case tree_call(Host, get_subnodes, [Host, Node, From]) of
-%                                  [] ->
-%                                      [<<"leaf">>]; %% No sub-nodes: it's a leaf node
-%                                  _ ->
-%                                      case node_call(Type, get_items, [NodeId, From, none]) of
-%                                          {result, []} -> [<<"collection">>];
-%                                          {result, _} -> [<<"leaf">>, <<"collection">>];
-%                                          _ -> []
-%                                      end
-%                              end,
-%                          lists:map(fun(T) ->
-%                                               #xmlel{name = <<"identity">>,
-%                                                      attrs =
-%                                                          [{<<"category">>,
-%                                                            <<"pubsub">>},
-%                                                           {<<"type">>, T}],
-%                                                      children = []}
-%                                    end, Types)
-%                  end,
-%              F = case Features of
-%                      false ->
-%                          [];
-%                      true ->
-%                          [#xmlel{name = <<"feature">>,
-%                                     attrs = [{<<"var">>, ?NS_PUBSUB}],
-%                                     children = []}
-%                              | lists:map(fun
-%                                                  (<<"rsm">>)->
-%                                                  #xmlel{name = <<"feature">>,
-%                                                         attrs = [{<<"var">>, ?NS_RSM}]};
-%                                                  (T) ->
-%                                                  #xmlel{name = <<"feature">>,
-%                                                         attrs =
-%                                                             [{<<"var">>,
-%                                                               <<(?NS_PUBSUB)/binary,
-%                                                                 "#",
-%                                                                 T/binary>>}],
-%                                                         children = []}
-%                                          end,
-%                                          features(Type))]
-%                  end,
-%              %% TODO: add meta-data info (spec section 5.4)
-%              {result, I ++ F}
-%      end,
-%    case transaction(Host, Node, Action, sync_dirty) of
-%      {result, {_, Result}} -> {result, Result};
-%      Other -> Other
-%    end.
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Types = case tree_call(Host, get_subnodes,
-                                                     [Host, Node, From])
-                                          of
-                                        [] -> [<<"leaf">>];
-                                        _ ->
-                                            case node_call(Type, get_items,
-                                                           [NodeId, From, none])
-                                                of
-                                              {result, []} ->
-                                                  [<<"collection">>];
-                                              {result, _} ->
-                                                  [<<"leaf">>,
-                                                   <<"collection">>];
-                                              _ -> []
-                                            end
-                                      end,
-                   I = lists:map(fun (T) ->
-                                                #xmlel{name = <<"identity">>,
-                                                       attrs =
-                                                           [{<<"category">>,
-                                                             <<"pubsub">>},
-                                                            {<<"type">>, T}],
-                                                       children = []}
-                                        end,
-                                        Types),
-                    F = [#xmlel{name = <<"feature">>,
-                                      attrs = [{<<"var">>, ?NS_PUBSUB}],
-                                      children = []}
-                               | lists:map(fun
-                                                   (<<"rsm">>)->
-                                                   #xmlel{name = <<"feature">>,
-                                                          attrs = [{<<"var">>, ?NS_RSM}]};
-                                                   (T) ->
-                                                   #xmlel{name = <<"feature">>,
-                                                          attrs =
-                                                              [{<<"var">>,
-                                                                <<(?NS_PUBSUB)/binary,
-                                                                  "#",
-                                                                  T/binary>>}],
-                                                          children = []}
-                                           end,
-                                           features(Type))],
-                    {result, I ++ F}
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> {result, Result};
-      Other -> Other
-    end.
-
-iq_disco_info(Host, SNode, From, Lang) ->
-    [Node | _] = case SNode of
-                       <<>> -> [<<>>];
-                       _ -> str:tokens(SNode, <<"!">>)
-                     end,
- %   Node = string_to_node(RealSNode),
-    case Node of
-      <<>> ->
-         {result,
-          [#xmlel{name = <<"identity">>,
-                  attrs =
-                      [{<<"category">>, <<"pubsub">>},
-                       {<<"type">>, <<"service">>},
-                       {<<"name">>,
-                        translate:translate(Lang, <<"Publish-Subscribe">>)}],
-                  children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_PUBSUB}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_COMMANDS}], children = []},
-           #xmlel{name = <<"feature">>,
-                  attrs = [{<<"var">>, ?NS_VCARD}], children = []}]
-            ++
-            lists:map(fun
-                               (<<"rsm">>)->
-                                       #xmlel{name = <<"feature">>,
-                                               attrs = [{<<"var">>, ?NS_RSM}]};
-                               (Feature) ->
-                              #xmlel{name = <<"feature">>,
-                                     attrs =
-                                         [{<<"var">>, <<(?NS_PUBSUB)/binary, "#", Feature/binary>>}],
-                                     children = []}
-                      end,
-                      features(Host, Node))};
-      ?NS_COMMANDS -> command_disco_info(Host, Node, From);
-      ?NS_PUBSUB_GET_PENDING ->
-         command_disco_info(Host, Node, From);
-      _ -> node_disco_info(Host, Node, From)
-    end.
-
--spec(iq_disco_items/4 ::
-(
-  Host   :: mod_pubsub:host(),
-  NodeId :: <<>> | mod_pubsub:nodeId(),
-  From   :: jid(),
-  Rsm    :: any())
-    -> {result, [xmlel()]}
-).
-iq_disco_items(Host, <<>>, From, _RSM) ->
-         {result,
-          lists:map(fun (#pubsub_node{nodeid = {_, SubNode},
-                                      options = Options}) ->
-                            Attrs = case get_option(Options, title) of
-                                      false ->
-                                          [{<<"jid">>, Host}
-                                           | nodeAttr(SubNode)];
-                                      Title ->
-                                          [{<<"jid">>, Host},
-                                           {<<"name">>, Title}
-                                           | nodeAttr(SubNode)]
-                                    end,
-                            #xmlel{name = <<"item">>, attrs = Attrs,
-                                   children = []}
-                    end,
-                    tree_action(Host, get_subnodes, [Host, <<>>, From]))};
-%    case tree_action(Host, get_subnodes, [Host, <<>>, From]) of
-%      Nodes when is_list(Nodes) ->
-%        {result,
-%         lists:map(fun (#pubsub_node{nodeid = {_, SubNode},
-%                                     options = Options}) ->
-%                           Attrs = case get_option(Options, title) of
-%                                     false ->
-%                                         [{<<"jid">>, Host}
-%                                          | nodeAttr(SubNode)];
-%                                     Title ->
-%                                         [{<<"jid">>, Host},
-%                                          {<<"name">>, Title}
-%                                          | nodeAttr(SubNode)]
-%                                   end,
-%                           #xmlel{name = <<"item">>, attrs = Attrs,
-%                                  children = []}
-%                   end,
-%                   Nodes)};
-%      Other -> Other
-%    end;
-iq_disco_items(Host, ?NS_COMMANDS, _From, _RSM) ->
-    CommandItems = [#xmlel{name = <<"item">>,
-                          attrs =
-                              [{<<"jid">>, Host},
-                               {<<"node">>, ?NS_PUBSUB_GET_PENDING},
-                               {<<"name">>, <<"Get Pending">>}],
-                          children = []}],
-    {result, CommandItems};
-iq_disco_items(_Host, ?NS_PUBSUB_GET_PENDING, _From, _RSM) ->
-    CommandItems = [], {result, CommandItems};
-iq_disco_items(Host, Item, From, RSM) ->
-    case str:tokens(Item, <<"!">>) of
-      [_Node, _ItemID] -> {result, []};
-      [Node] ->
-%        Node = string_to_node(SNode),
-         Action = fun (#pubsub_node{id = Idx, type = Type,
-                                    options = Options}) ->
-                          Owners = node_owners_call(Type, Idx),
-                          {NodeItems, RsmOut} = case get_allowed_items_call(Host, Idx, From, Type, Options, Owners, RSM) of
-                                        {result, R} -> R;
-                                        _ -> {[], none}
-                                      end,
-                          Nodes = lists:map(fun (#pubsub_node{nodeid =
-                                                                  {_, SubNode},
-                                                              options =
-                                                                  SubOptions}) ->
-                                                    Attrs = case
-                                                              get_option(SubOptions,
-                                                                         title)
-                                                                of
-                                                              false ->
-                                                                  [{<<"jid">>,
-                                                                    Host}
-                                                                   | nodeAttr(SubNode)];
-                                                              Title ->
-                                                                  [{<<"jid">>,
-                                                                    Host},
-                                                                   {<<"name">>,
-                                                                    Title}
-                                                                   | nodeAttr(SubNode)]
-                                                            end,
-                                                    #xmlel{name = <<"item">>,
-                                                           attrs = Attrs,
-                                                           children = []}
-                                            end,
-                                            tree_call(Host, get_subnodes,
-                                                      [Host, Node, From])),
-                          Items = lists:map(fun (#pubsub_item{itemid =
-                                                                  {RN, _}}) ->
-                                                    {result, Name} =
-                                                        node_call(Type,
-                                                                  get_item_name,
-                                                                  [Host, Node,
-                                                                   RN]),
-                                                    #xmlel{name = <<"item">>,
-                                                           attrs =
-                                                               [{<<"jid">>,
-                                                                 Host},
-                                                                {<<"name">>,
-                                                                 Name}],
-                                                           children = []}
-                                            end,
-                                            NodeItems),
-                          {result, Nodes ++ Items ++ jlib:rsm_encode(RsmOut)}
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
-    end.
-
--spec(iq_sm/3 ::
-(
-  From :: jid(),
-  To   :: jid(),
-  IQ   :: iq_request())
-    -> iq_result() | iq_error()
-).
-iq_sm(From, To, #iq{type = Type, sub_el = SubEl, xmlns = XMLNS, lang = Lang} = IQ) ->
-    ServerHost = To#jid.lserver,
-    LOwner = jlib:jid_tolower(jlib:jid_remove_resource(To)),
-    Res = case XMLNS of
-           ?NS_PUBSUB ->
-               iq_pubsub(LOwner, ServerHost, From, Type, SubEl, Lang);
-           ?NS_PUBSUB_OWNER ->
-               iq_pubsub_owner(LOwner, ServerHost, From, Type, SubEl,
-                               Lang)
-         end,
-    case Res of
-      {result, IQRes} -> IQ#iq{type = result, sub_el = IQRes};
-      {error, Error} ->
-         IQ#iq{type = error, sub_el = [Error, SubEl]}
-    end.
-
-iq_get_vcard(Lang) ->
-    [#xmlel{name = <<"FN">>, attrs = [],
-           children = [{xmlcdata, <<"ejabberd/mod_pubsub">>}]},
-     #xmlel{name = <<"URL">>, attrs = [],
-           children = [{xmlcdata, ?EJABBERD_URI}]},
-     #xmlel{name = <<"DESC">>, attrs = [],
-           children =
-               [{xmlcdata,
-                 <<(translate:translate(Lang,
-                                        <<"ejabberd Publish-Subscribe module">>))/binary,
-                   "\nCopyright (c) 2004-2015 ProcessOne">>}]}].
-
--spec(iq_pubsub/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary())
-    -> {result, [xmlel()]}
-    %%%
-     | {error, xmlel()}
-).
-
-iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang) ->
-    iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, all, plugins(ServerHost)).
-
--spec(iq_pubsub/8 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary(),
-  Access     :: atom(),
-  Plugins    :: [binary(),...])
-    -> {result, [xmlel()]}
-    %%%
-     | {error, xmlel()}
-).
-
-iq_pubsub(Host, ServerHost, From, IQType, SubEl, Lang, Access, Plugins) ->
-    #xmlel{children = SubEls} = SubEl,
-    case xml:remove_cdata(SubEls) of
-      [#xmlel{name = Name, attrs = Attrs, children = Els} | Rest] ->
-         Node = xml:get_attr_s(<<"node">>, Attrs),
-         case {IQType, Name} of
-           {set, <<"create">>} ->
-               Config = case Rest of
-                          [#xmlel{name = <<"configure">>, children = C}] -> C;
-                          _ -> []
-                        end,
-               Type = case xml:get_attr_s(<<"type">>, Attrs) of
-                        <<>> -> hd(Plugins);
-                        T -> T
-                      end,
-               case lists:member(Type, Plugins) of
-                 false ->
-                     {error,
-                      extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                     unsupported, <<"create-nodes">>)};
-                 true ->
-                     create_node(Host, ServerHost, Node, From, Type, Access, Config)
-               end;
-           {set, <<"publish">>} ->
-               case xml:remove_cdata(Els) of
-                 [#xmlel{name = <<"item">>, attrs = ItemAttrs,
-                         children = Payload}] ->
-                     ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
-                     publish_item(Host, ServerHost, Node, From, ItemId, Payload, Access);
-                 [] ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
-                 _ ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)}
-               end;
-           {set, <<"retract">>} ->
-               ForceNotify = case xml:get_attr_s(<<"notify">>, Attrs)
-                                 of
-                               <<"1">> -> true;
-                               <<"true">> -> true;
-                               _ -> false
-                             end,
-               case xml:remove_cdata(Els) of
-                 [#xmlel{name = <<"item">>, attrs = ItemAttrs}] ->
-                     ItemId = xml:get_attr_s(<<"id">>, ItemAttrs),
-                     delete_item(Host, Node, From, ItemId, ForceNotify);
-                 _ ->
-                     {error,
-                      extended_error(?ERR_BAD_REQUEST, <<"item-required">>)}
-               end;
-           {set, <<"subscribe">>} ->
-               Config = case Rest of
-                          [#xmlel{name = <<"options">>, children = C}] -> C;
-                          _ -> []
-                        end,
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               subscribe_node(Host, Node, From, JID, Config);
-           {set, <<"unsubscribe">>} ->
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               SubId = xml:get_attr_s(<<"subid">>, Attrs),
-               unsubscribe_node(Host, Node, From, JID, SubId);
-           {get, <<"items">>} ->
-               MaxItems = xml:get_attr_s(<<"max_items">>, Attrs),
-               SubId = xml:get_attr_s(<<"subid">>, Attrs),
-               ItemIDs = lists:foldl(fun (#xmlel{name = <<"item">>,
-                                                 attrs = ItemAttrs},
-                                          Acc) ->
-                                             case xml:get_attr_s(<<"id">>,
-                                                                 ItemAttrs)
-                                                 of
-                                               <<"">> -> Acc;
-                                               ItemID -> [ItemID | Acc]
-                                             end;
-                                         (_, Acc) -> Acc
-                                     end,
-                                     [], xml:remove_cdata(Els)),
-               RSM = jlib:rsm_decode(SubEl),
-               get_items(Host, Node, From, SubId, MaxItems, ItemIDs, RSM);
-           {get, <<"subscriptions">>} ->
-               get_subscriptions(Host, Node, From, Plugins);
-           {get, <<"affiliations">>} ->
-               get_affiliations(Host, Node, From, Plugins);
-           {get, <<"options">>} ->
-               SubID = xml:get_attr_s(<<"subid">>, Attrs),
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               get_options(Host, Node, JID, SubID, Lang);
-           {set, <<"options">>} ->
-               SubID = xml:get_attr_s(<<"subid">>, Attrs),
-               JID = xml:get_attr_s(<<"jid">>, Attrs),
-               set_options(Host, Node, JID, SubID, Els);
-           _ -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-         end;
-      Other ->
-         ?INFO_MSG("Too many actions: ~p", [Other]),
-         {error, ?ERR_BAD_REQUEST}
-    end.
-
-
--spec(iq_pubsub_owner/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  From       :: jid(),
-  IQType     :: 'get' | 'set',
-  SubEl      :: xmlel(),
-  Lang       :: binary())
-    -> {result, [xmlel()]}
-    %%%
-     | {error, xmlel()}
-).
-iq_pubsub_owner(Host, ServerHost, From, IQType, SubEl, Lang) ->
-    #xmlel{children = SubEls} = SubEl,
-    Action = lists:filter(fun(#xmlel{name = <<"set">>, _ = '_'}) -> false;
-                             (_) -> true
-                        end, xml:remove_cdata(SubEls)),
-    case Action of
-      [#xmlel{name = Name, attrs = Attrs, children = Els}] ->
-         Node = xml:get_attr_s(<<"node">>, Attrs),
-         case {IQType, Name} of
-           {get, <<"configure">>} ->
-               get_configure(Host, ServerHost, Node, From, Lang);
-           {set, <<"configure">>} ->
-               set_configure(Host, Node, From, Els, Lang);
-           {get, <<"default">>} ->
-               get_default(Host, Node, From, Lang);
-           {set, <<"delete">>} -> delete_node(Host, Node, From);
-           {set, <<"purge">>} -> purge_node(Host, Node, From);
-           {get, <<"subscriptions">>} ->
-               get_subscriptions(Host, Node, From);
-           {set, <<"subscriptions">>} ->
-               set_subscriptions(Host, Node, From,
-                                 xml:remove_cdata(Els));
-           {get, <<"affiliations">>} ->
-               get_affiliations(Host, Node, From);
-           {set, <<"affiliations">>} ->
-               set_affiliations(Host, Node, From, xml:remove_cdata(Els));
-           _ -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-         end;
-      _ ->
-         ?INFO_MSG("Too many actions: ~p", [Action]),
-         {error, ?ERR_BAD_REQUEST}
-    end.
-
-iq_command(Host, ServerHost, From, IQ, Access, Plugins) ->
-    case adhoc:parse_request(IQ) of
-      Req when is_record(Req, adhoc_request) ->
-         case adhoc_request(Host, ServerHost, From, Req, Access,
-                            Plugins)
-             of
-           Resp when is_record(Resp, adhoc_response) ->
-               {result, [adhoc:produce_response(Req, Resp)]};
-           Error -> Error
-         end;
-      Err -> Err
-    end.
-
-%% @doc <p>Processes an Ad Hoc Command.</p>
-adhoc_request(Host, _ServerHost, Owner,
-             #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
-                            lang = Lang, action = <<"execute">>,
-                            xdata = false},
-             _Access, Plugins) ->
-    send_pending_node_form(Host, Owner, Lang, Plugins);
-adhoc_request(Host, _ServerHost, Owner,
-             #adhoc_request{node = ?NS_PUBSUB_GET_PENDING,
-                            action = <<"execute">>, xdata = XData},
-             _Access, _Plugins) ->
-    ParseOptions = case XData of
-                    #xmlel{name = <<"x">>} = XEl ->
-                        case jlib:parse_xdata_submit(XEl) of
-                          invalid -> {error, ?ERR_BAD_REQUEST};
-                          XData2 ->
-                              case set_xoption(Host, XData2, []) of
-                                NewOpts when is_list(NewOpts) ->
-                                    {result, NewOpts};
-                                Err -> Err
-                              end
-                        end;
-                    _ ->
-                        ?INFO_MSG("Bad XForm: ~p", [XData]),
-                        {error, ?ERR_BAD_REQUEST}
-                  end,
-    case ParseOptions of
-      {result, XForm} ->
-         case lists:keysearch(node, 1, XForm) of
-           {value, {_, Node}} ->
-               send_pending_auth_events(Host, Node, Owner);
-           false ->
-               {error,
-                extended_error(?ERR_BAD_REQUEST, <<"bad-payload">>)}
-         end;
-      Error -> Error
-    end;
-adhoc_request(_Host, _ServerHost, _Owner,
-             #adhoc_request{action = <<"cancel">>}, _Access,
-             _Plugins) ->
-    #adhoc_response{status = canceled};
-adhoc_request(Host, ServerHost, Owner,
-             #adhoc_request{action = <<>>} = R, Access, Plugins) ->
-    adhoc_request(Host, ServerHost, Owner,
-                 R#adhoc_request{action = <<"execute">>}, Access,
-                 Plugins);
-adhoc_request(_Host, _ServerHost, _Owner, Other,
-             _Access, _Plugins) ->
-    ?DEBUG("Couldn't process ad hoc command:~n~p", [Other]),
-    {error, ?ERR_ITEM_NOT_FOUND}.
-
-%% @spec (Host, Owner, Lang, Plugins) -> iqRes()
-%% @doc <p>Sends the process pending subscriptions XForm for Host to
-%% Owner.</p>
-send_pending_node_form(Host, Owner, _Lang, Plugins) ->
-    Filter = fun (Plugin) ->
-                    lists:member(<<"get-pending">>, features(Plugin))
-            end,
-    case lists:filter(Filter, Plugins) of
-      [] -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
-      Ps ->
-         XOpts = lists:map(fun (Node) ->
-                                   #xmlel{name = <<"option">>, attrs = [],
-                                          children =
-                                              [#xmlel{name = <<"value">>,
-                                                      attrs = [],
-                                                      children =
-                                                          [{xmlcdata, Node}]}]}
-                           end,
-                           get_pending_nodes(Host, Owner, Ps)),
-         XForm = #xmlel{name = <<"x">>,
-                        attrs =
-                            [{<<"xmlns">>, ?NS_XDATA},
-                             {<<"type">>, <<"form">>}],
-                        children =
-                            [#xmlel{name = <<"field">>,
-                                    attrs =
-                                        [{<<"type">>, <<"list-single">>},
-                                         {<<"var">>, <<"pubsub#node">>}],
-                                    children = lists:usort(XOpts)}]},
-         #adhoc_response{status = executing,
-                         defaultaction = <<"execute">>, elements = [XForm]}
-    end.
-
-get_pending_nodes(Host, Owner, Plugins) ->
-    Tr = fun (Type) ->
-                case node_call(Type, get_pending_nodes, [Host, Owner])
-                    of
-                  {result, Nodes} -> Nodes;
-                  _ -> []
-                end
-        end,
-    case transaction(Host,
-                    fun () ->
-                            {result, lists:flatmap(Tr, Plugins)}
-                    end,
-                    sync_dirty)
-       of
-      {result, Res} -> Res;
-      Err -> Err
-    end.
-
-%% @spec (Host, Node, Owner) -> iqRes()
-%% @doc <p>Send a subscription approval form to Owner for all pending
-%% subscriptions on Host and Node.</p>
-send_pending_auth_events(Host, Node, Owner) ->
-    ?DEBUG("Sending pending auth events for ~s on "
-          "~s:~s",
-          [jlib:jid_to_string(Owner), Host, Node]),
-    Action = fun (#pubsub_node{id = NodeID, type = Type}) ->
-                    case lists:member(<<"get-pending">>, features(Type)) of
-                      true ->
-                          case node_call(Type, get_affiliation,
-                                         [NodeID, Owner])
-                              of
-                            {result, owner} ->
-                                node_call(Type, get_node_subscriptions,
-                                          [NodeID]);
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end;
-                      false -> {error, ?ERR_FEATURE_NOT_IMPLEMENTED}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {N, Subscriptions}} ->
-         lists:foreach(fun ({J, pending, _SubID}) ->
-                               send_authorization_request(N, jlib:make_jid(J));
-                           ({J, pending}) ->
-                               send_authorization_request(N, jlib:make_jid(J));
-                           (_) -> ok
-                       end,
-                       Subscriptions),
-         #adhoc_response{};
-      Err -> Err
-    end.
-
-%%% authorization handling
-
-send_authorization_request(#pubsub_node{nodeid = {Host, Node},
-       type = Type, id = NodeId},
-       Subscriber) ->
-    Lang = <<"en">>,
-    Stanza = #xmlel{name = <<"message">>, attrs = [],
-                   children =
-                       [#xmlel{name = <<"x">>,
-                               attrs =
-                                   [{<<"xmlns">>, ?NS_XDATA},
-                                    {<<"type">>, <<"form">>}],
-                               children =
-                                   [#xmlel{name = <<"title">>, attrs = [],
-                                           children =
-                                               [{xmlcdata,
-                                                 translate:translate(Lang,
-                                                                     <<"PubSub subscriber request">>)}]},
-                                    #xmlel{name = <<"instructions">>,
-                                           attrs = [],
-                                           children =
-                                               [{xmlcdata,
-                                                 translate:translate(Lang,
-                                                                     <<"Choose whether to approve this entity's "
-                                                                       "subscription.">>)}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>, <<"FORM_TYPE">>},
-                                                {<<"type">>, <<"hidden">>}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             ?NS_PUBSUB_SUB_AUTH}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>, <<"pubsub#node">>},
-                                                {<<"type">>,
-                                                 <<"text-single">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Node ID">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Node}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>,
-                                                 <<"pubsub#subscriber_jid">>},
-                                                {<<"type">>, <<"jid-single">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Subscriber Address">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             jlib:jid_to_string(Subscriber)}]}]},
-                                    #xmlel{name = <<"field">>,
-                                           attrs =
-                                               [{<<"var">>,
-                                                 <<"pubsub#allow">>},
-                                                {<<"type">>, <<"boolean">>},
-                                                {<<"label">>,
-                                                 translate:translate(Lang,
-                                                                     <<"Allow this Jabber ID to subscribe to "
-                                                                       "this pubsub node?">>)}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata,
-                                                             <<"false">>}]}]}]}]},
-    lists:foreach(fun (Owner) ->
-                         ejabberd_router:route(service_jid(Host),
-                                               jlib:make_jid(Owner), Stanza)
-                 end,
-                 node_owners(Host, Type, NodeId)).
-
-find_authorization_response(Packet) ->
-    #xmlel{children = Els} = Packet,
-    XData1 = lists:map(fun (#xmlel{name = <<"x">>,
-                                  attrs = XAttrs} =
-                               XEl) ->
-                              case xml:get_attr_s(<<"xmlns">>, XAttrs) of
-                                ?NS_XDATA ->
-                                    case xml:get_attr_s(<<"type">>, XAttrs) of
-                                      <<"cancel">> -> none;
-                                      _ -> jlib:parse_xdata_submit(XEl)
-                                    end;
-                                _ -> none
-                              end;
-                          (_) -> none
-                      end,
-                      xml:remove_cdata(Els)),
-    XData = lists:filter(fun (E) -> E /= none end, XData1),
-    case XData of
-      [invalid] -> invalid;
-      [] -> none;
-      [XFields] when is_list(XFields) ->
-         ?DEBUG("XFields: ~p", [XFields]),
-         case lists:keysearch(<<"FORM_TYPE">>, 1, XFields) of
-           {value, {_, [?NS_PUBSUB_SUB_AUTH]}} -> XFields;
-           _ -> invalid
-         end
-    end.
-%% @spec (Host, JID, Node, Subscription) -> void
-%%      Host = mod_pubsub:host()
-%%      JID = jlib:jid()
-%%      SNode = string()
-%%      Subscription = atom() | {atom(), mod_pubsub:subid()}
-%% @doc Send a message to JID with the supplied Subscription
-%% TODO : ask Christophe's opinion
-send_authorization_approval(Host, JID, SNode, Subscription) ->
-    SubAttrs = case Subscription of
-%               {S, SID} ->
-%                   [{<<"subscription">>, subscription_to_string(S)},
-%                    {<<"subid">>, SID}];
-                S -> [{<<"subscription">>, subscription_to_string(S)}]
-              end,
-    Stanza = event_stanza([#xmlel{name = <<"subscription">>,
-                                 attrs =
-                                     [{<<"jid">>, jlib:jid_to_string(JID)}
-                                      | nodeAttr(SNode)]
-                                       ++ SubAttrs,
-                                 children = []}]),
-    ejabberd_router:route(service_jid(Host), JID, Stanza).
-
-handle_authorization_response(Host, From, To, Packet, XFields) ->
-    case {lists:keysearch(<<"pubsub#node">>, 1, XFields),
-         lists:keysearch(<<"pubsub#subscriber_jid">>, 1, XFields),
-         lists:keysearch(<<"pubsub#allow">>, 1, XFields)}
-       of
-      {{value, {_, [Node]}}, {value, {_, [SSubscriber]}},
-       {value, {_, [SAllow]}}} ->
-%        Node = string_to_node(SNode),
-         Subscriber = jlib:string_to_jid(SSubscriber),
-         Allow = case SAllow of
-                   <<"1">> -> true;
-                   <<"true">> -> true;
-                   _ -> false
-                 end,
-         Action = fun (#pubsub_node{type = Type,
-                                    id = NodeId}) ->
-                          IsApprover =
-                              lists:member(jlib:jid_tolower(jlib:jid_remove_resource(From)),
-                                           node_owners_call(Type, NodeId)),
-                          {result, Subscriptions} = node_call(Type,
-                                                              get_subscriptions,
-                                                              [NodeId,
-                                                               Subscriber]),
-                          if not IsApprover -> {error, ?ERR_FORBIDDEN};
-                             true ->
-                                 update_auth(Host, Node, Type, NodeId,
-                                             Subscriber, Allow, Subscriptions)
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {error, Error} ->
-               ejabberd_router:route(To, From,
-                                     jlib:make_error_reply(Packet, Error));
-           {result, {_, _NewSubscription}} ->
-               %% XXX: notify about subscription state change, section 12.11
-               ok;
-           _ ->
-               ejabberd_router:route(To, From,
-                                     jlib:make_error_reply(Packet,
-                                                           ?ERR_INTERNAL_SERVER_ERROR))
-         end;
-      _ ->
-         ejabberd_router:route(To, From,
-                               jlib:make_error_reply(Packet,
-                                                     ?ERR_NOT_ACCEPTABLE))
-    end.
-
-update_auth(Host, Node, Type, NodeId, Subscriber, Allow,
-           Subscriptions) ->
-    Subscription = lists:filter(fun ({pending, _}) -> true;
-                                   (_) -> false
-                               end,
-                               Subscriptions),
-    case Subscription of
-      [{pending, SubID}] ->
-         NewSubscription = case Allow of
-                             true -> subscribed;
-                             false -> none
-                           end,
-         node_call(Type, set_subscriptions,
-                   [NodeId, Subscriber, NewSubscription, SubID]),
-         send_authorization_approval(Host, Subscriber, Node,
-                                     NewSubscription),
-         {result, ok};
-      _ -> {error, ?ERR_UNEXPECTED_REQUEST}
-    end.
-
--define(XFIELD(Type, Label, Var, Val),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, Type},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, Val}]}]}).
-
--define(BOOLXFIELD(Label, Var, Val),
-       ?XFIELD(<<"boolean">>, Label, Var,
-               case Val of
-                 true -> <<"1">>;
-                 _ -> <<"0">>
-               end)).
-
--define(STRINGXFIELD(Label, Var, Val),
-       ?XFIELD(<<"text-single">>, Label, Var, Val)).
-
--define(STRINGMXFIELD(Label, Var, Vals),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, <<"text-multi">>},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, V}]}
-                   || V <- Vals]}).
-
--define(XFIELDOPT(Type, Label, Var, Val, Opts),
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, Type},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  lists:map(fun (Opt) ->
-                                    #xmlel{name = <<"option">>, attrs = [],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Opt}]}]}
-                            end,
-                            Opts)
-                    ++
-                    [#xmlel{name = <<"value">>, attrs = [],
-                            children = [{xmlcdata, Val}]}]}).
-
--define(LISTXFIELD(Label, Var, Val, Opts),
-       ?XFIELDOPT(<<"list-single">>, Label, Var, Val, Opts)).
-
--define(LISTMXFIELD(Label, Var, Vals, Opts),
-%% @spec (Host::host(), ServerHost::host(), Node::pubsubNode(), Owner::jid(), NodeType::nodeType()) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
-%% @doc <p>Create new pubsub nodes</p>
-%%<p>In addition to method-specific error conditions, there are several general reasons why the node creation request might fail:</p>
-%%<ul>
-%%<li>The service does not support node creation.</li>
-%%<li>Only entities that are registered with the service are allowed to create nodes but the requesting entity is not registered.</li>
-%%<li>The requesting entity does not have sufficient privileges to create nodes.</li>
-%%<li>The requested NodeID already exists.</li>
-%%<li>The request did not include a NodeID and "instant nodes" are not supported.</li>
-%%</ul>
-%%<p>ote: node creation is a particular case, error return code is evaluated at many places:</p>
-%%<ul>
-%%<li>iq_pubsub checks if service supports node creation (type exists)</li>
-%%<li>create_node checks if instant nodes are supported</li>
-%%<li>create_node asks node plugin if entity have sufficient privilege</li>
-%%<li>nodetree create_node checks if nodeid already exists</li>
-%%<li>node plugin create_node just sets default affiliation/subscription</li>
-%%</ul>
-       #xmlel{name = <<"field">>,
-              attrs =
-                  [{<<"type">>, <<"list-multi">>},
-                   {<<"label">>, translate:translate(Lang, Label)},
-                   {<<"var">>, Var}],
-              children =
-                  lists:map(fun (Opt) ->
-                                    #xmlel{name = <<"option">>, attrs = [],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, Opt}]}]}
-                            end,
-                            Opts)
-                    ++
-                    lists:map(fun (Val) ->
-                                      #xmlel{name = <<"value">>, attrs = [],
-                                             children = [{xmlcdata, Val}]}
-                              end,
-                              Vals)}).
-
--spec(create_node/5 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  Node       :: <<>> | mod_pubsub:nodeId(),
-  Owner      :: jid(),
-  Type       :: binary())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-
-create_node(Host, ServerHost, Node, Owner, Type) ->
-    create_node(Host, ServerHost, Node, Owner, Type, all, []).
-
--spec(create_node/7 ::
-(
-  Host          :: mod_pubsub:host(),
-  ServerHost    :: binary(),
-  Node          :: <<>> | mod_pubsub:nodeId(),
-  Owner         :: jid(),
-  Type          :: binary(),
-  Access        :: atom(),
-  Configuration :: [xmlel()])
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
-    case lists:member(<<"instant-nodes">>, features(Type)) of
-      true ->
-         NewNode = randoms:get_string(),
-         case create_node(Host, ServerHost, NewNode, Owner, Type,
-                        Access, Configuration)
-             of
-           {result, _} ->
-               {result,
-                [#xmlel{name = <<"pubsub">>,
-                        attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                        children =
-                            [#xmlel{name = <<"create">>,
-                                    attrs = nodeAttr(NewNode),
-                                    children = []}]}]};
-           Error -> Error
-         end;
-      false ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"nodeid-required">>)}
-    end;
-create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
-    Type = select_type(ServerHost, Host, Node, GivenType),
-    ParseOptions = case xml:remove_cdata(Configuration) of
-                    [] -> {result, node_options(Type)};
-                    [#xmlel{name = <<"x">>} = XEl] ->
-                        case jlib:parse_xdata_submit(XEl) of
-                          invalid -> {error, ?ERR_BAD_REQUEST};
-                          XData ->
-                              case set_xoption(Host, XData, node_options(Type))
-                                  of
-                                NewOpts when is_list(NewOpts) ->
-                                    {result, NewOpts};
-                                Err -> Err
-                              end
-                        end;
-                    _ ->
-                        ?INFO_MSG("Node ~p; bad configuration: ~p",
-                                  [Node, Configuration]),
-                        {error, ?ERR_BAD_REQUEST}
-                  end,
-    case ParseOptions of
-       {result, NodeOptions} ->
-           CreateNode =
-               fun() ->
-                       Parent = case node_call(Type, node_to_path, [Node]) of
-                           {result, [Node]} -> <<>>;
-                           {result, Path} -> element(2, node_call(Type, path_to_node, [lists:sublist(Path, length(Path)-1)]))
-                       end,
-                       Parents = case Parent of
-                           <<>> -> [];
-                           _ -> [Parent]
-                       end,
-                       case node_call(Type, create_node_permission, [Host, ServerHost, Node, Parent, Owner, Access]) of
-                           {result, true} ->
-                               case tree_call(Host, create_node, [Host, Node, Type, Owner, NodeOptions, Parents]) of
-                                   {ok, NodeId} ->
-                                       ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, Owner]),
-                                       SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
-                                       case node_call(Type, create_node, [NodeId, Owner]) of
-                                           {result, Result} -> {result, {NodeId, SubsByDepth, Result}};
-                                           Error -> Error
-                                       end;
-                                   {error, {virtual, NodeId}} ->
-                                       case node_call(Type, create_node, [NodeId, Owner]) of
-                                           {result, Result} -> {result, {NodeId, [], Result}};
-                                           Error -> Error
-                                       end;
-                                   Error ->
-                                       Error
-                               end;
-                           _ ->
-                               {error, ?ERR_FORBIDDEN}
-                       end
-               end,
-           Reply = [#xmlel{name = <<"pubsub">>,
-                           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                           children = [#xmlel{name = <<"create">>,
-                                   attrs = nodeAttr(Node),
-                                   children = []}]}],
-           case transaction(Host, CreateNode, transaction) of
-               {result, {NodeId, SubsByDepth, {Result, broadcast}}} ->
-                   broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth),
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
-                   case Result of
-                       default -> {result, Reply};
-                       _ -> {result, Result}
-                   end;
-               {result, {NodeId, _SubsByDepth, default}} ->
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
-                   {result, Reply};
-               {result, {NodeId, _SubsByDepth, _Result}} ->
-                   ejabberd_hooks:run(pubsub_create_node, ServerHost, [ServerHost, Host, Node, NodeId, NodeOptions]),
-                   {result, Reply};
-               Error ->
-                   %% in case we change transaction to sync_dirty...
-                   %%  node_call(Type, delete_node, [Host, Node]),
-                   %%  tree_call(Host, delete_node, [Host, Node]),
-                   Error
-           end;
-       Error ->
-           Error
-    end.
-
-%% @spec (Host, Node, Owner) ->
-%%                     {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      Owner = jid()
-%%      Reason = stanzaError()
--spec(delete_node/3 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  Owner :: jid())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-%% @doc <p>Delete specified node and all childs.</p>
-%%<p>There are several reasons why the node deletion request might fail:</p>
-%%<ul>
-%%<li>The requesting entity does not have sufficient privileges to delete the node.</li>
-%%<li>The node is the root collection node, which cannot be deleted.</li>
-%%<li>The specified node does not exist.</li>
-%%</ul>
-delete_node(_Host, <<>>, _Owner) ->
-    {error, ?ERR_NOT_ALLOWED};
-delete_node(Host, Node, Owner) ->
-    Action = fun(#pubsub_node{type = Type, id = NodeId}) ->
-                   case node_call(Type, get_affiliation, [NodeId, Owner]) of
-                       {result, owner} ->
-                           ParentTree = tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]),
-                           SubsByDepth = [{Depth, [{N, get_node_subs(N)} || N <- Nodes]} || {Depth, Nodes} <- ParentTree],
-                           Removed = tree_call(Host, delete_node, [Host, Node]),
-                           case node_call(Type, delete_node, [Removed]) of
-                               {result, Res} -> {result, {SubsByDepth, Res}};
-                               Error -> Error
-                           end;
-                       _ ->
-                           %% Entity is not an owner
-                           {error, ?ERR_FORBIDDEN}
-                   end
-           end,
-    Reply = [],
-    ServerHost = get(server_host),
-    case transaction(Host, Node, Action, transaction) of
-       {result, {_TNode, {SubsByDepth, {Result, broadcast, Removed}}}} ->
-           lists:foreach(fun({RNode, _RSubscriptions}) ->
-               {RH, RN} = RNode#pubsub_node.nodeid,
-               NodeId = RNode#pubsub_node.id,
-               Type = RNode#pubsub_node.type,
-               Options = RNode#pubsub_node.options,
-               broadcast_removed_node(RH, RN, NodeId, Type, Options, SubsByDepth),
-               ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, NodeId])
-           end, Removed),
-           case Result of
-               default -> {result, Reply};
-               _ -> {result, Result}
-           end;
-       {result, {_TNode, {_, {Result, Removed}}}} ->
-           lists:foreach(fun({RNode, _RSubscriptions}) ->
-               {RH, RN} = RNode#pubsub_node.nodeid,
-               NodeId = RNode#pubsub_node.id,
-               ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, RH, RN, NodeId])
-           end, Removed),
-           case Result of
-               default -> {result, Reply};
-               _ -> {result, Result}
-           end;
-       {result, {TNode, {_, default}}} ->
-           NodeId = TNode#pubsub_node.id,
-           ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, NodeId]),
-           {result, Reply};
-       {result, {TNode, {_, Result}}} ->
-           NodeId = TNode#pubsub_node.id,
-           ejabberd_hooks:run(pubsub_delete_node, ServerHost, [ServerHost, Host, Node, NodeId]),
-           {result, Result};
-       Error ->
-           Error
-    end.
-
-%% @spec (Host, Node, From, JID, Configuration) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      From = jid()
-%%      JID = jid()
--spec(subscribe_node/5 ::
-(
-  Host          :: mod_pubsub:host(),
-  Node          :: mod_pubsub:nodeId(),
-  From          :: jid(),
-  JID           :: binary(),
-  Configuration :: [xmlel()])
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-%% @see node_hometree:subscribe_node/5
-%% @doc <p>Accepts or rejects subcription requests on a PubSub node.</p>
-%%<p>There are several reasons why the subscription request might fail:</p>
-%%<ul>
-%%<li>The bare JID portions of the JIDs do not match.</li>
-%%<li>The node has an access model of "presence" and the requesting entity is not subscribed to the owner's presence.</li>
-%%<li>The node has an access model of "roster" and the requesting entity is not in one of the authorized roster groups.</li>
-%%<li>The node has an access model of "whitelist" and the requesting entity is not on the whitelist.</li>
-%%<li>The service requires payment for subscriptions to the node.</li>
-%%<li>The requesting entity is anonymous and the service does not allow anonymous entities to subscribe.</li>
-%%<li>The requesting entity has a pending subscription.</li>
-%%<li>The requesting entity is blocked from subscribing (e.g., because having an affiliation of outcast).</li>
-%%<li>The node does not support subscriptions.</li>
-%%<li>The node does not exist.</li>
-%%</ul>
-subscribe_node(Host, Node, From, JID, Configuration) ->
-    SubOpts = case
-               pubsub_subscription_odbc:parse_options_xform(Configuration)
-                 of
-               {result, GoodSubOpts} -> GoodSubOpts;
-               _ -> invalid
-             end,
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J ->
-                   case jlib:jid_tolower(J) of
-                     error -> {<<"">>, <<"">>, <<"">>};
-                     J1 -> J1
-                   end
-                end,
-    Action = fun (#pubsub_node{options = Options,
-                              type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    SubscribeFeature = lists:member(<<"subscribe">>, Features),
-                    OptionsFeature = lists:member(<<"subscription-options">>, Features),
-                    HasOptions = not (SubOpts == []),
-                    SubscribeConfig = get_option(Options, subscribe),
-                    AccessModel = get_option(Options, access_model),
-                    SendLast = get_option(Options, send_last_published_item),
-                    AllowedGroups = get_option(Options, roster_groups_allowed, []),
-                    Owners = node_owners_call(Type, NodeId),
-                    {PresenceSubscription, RosterGroup} =
-                           get_presence_and_roster_permissions(Host, Subscriber,
-                                       Owners, AccessModel, AllowedGroups),
-                    if not SubscribeFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"subscribe">>)};
-                       not SubscribeConfig ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"subscribe">>)};
-                       HasOptions andalso not OptionsFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"subscription-options">>)};
-                       SubOpts == invalid ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST,
-                                           <<"invalid-options">>)};
-                       true ->
-                           node_call(Type, subscribe_node,
-                                     [NodeId, From, Subscriber, AccessModel,
-                                      SendLast, PresenceSubscription,
-                                      RosterGroup, SubOpts])
-                    end
-            end,
-    Reply = fun (Subscription) ->
-                   SubAttrs = case Subscription of
-                                {subscribed, SubId} ->
-                                    [{<<"subscription">>,
-                                      subscription_to_string(subscribed)},
-                                     {<<"subid">>, SubId}, {<<"node">>, Node}];
-                                Other ->
-                                    [{<<"subscription">>,
-                                      subscription_to_string(Other)},
-                                     {<<"node">>, Node}]
-                              end,
-                   Fields = [{<<"jid">>, jlib:jid_to_string(Subscriber)}
-                             | SubAttrs],
-                   [#xmlel{name = <<"pubsub">>,
-                           attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                           children =
-                               [#xmlel{name = <<"subscription">>,
-                                       attrs = Fields, children = []}]}]
-           end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result,
-       {TNode, {Result, subscribed, SubId, send_last}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         send_items(Host, Node, NodeId, Type, Options, Subscriber, last),
-         case Result of
-           default -> {result, Reply({subscribed, SubId})};
-           _ -> {result, Result}
-         end;
-      {result, {_TNode, {default, subscribed, SubId}}} ->
-         {result, Reply({subscribed, SubId})};
-      {result, {_TNode, {Result, subscribed, _SubId}}} ->
-         {result, Result};
-      {result, {TNode, {default, pending, _SubId}}} ->
-         send_authorization_request(TNode, Subscriber),
-         {result, Reply(pending)};
-      {result, {TNode, {Result, pending}}} ->
-         send_authorization_request(TNode, Subscriber),
-         {result, Result};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
-    end.
-
-%% @spec (Host, Noce, From, JID, SubId) -> {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      From = jid()
-%%      JID = string()
-%%      SubId = string()
-%%      Reason = stanzaError()
-%% @doc <p>Unsubscribe <tt>JID</tt> from the <tt>Node</tt>.</p>
-%%<p>There are several reasons why the unsubscribe request might fail:</p>
-%%<ul>
-%%<li>The requesting entity has multiple subscriptions to the node but does not specify a subscription ID.</li>
-%%<li>The request does not specify an existing subscriber.</li>
-%%<li>The requesting entity does not have sufficient privileges to unsubscribe the specified JID.</li>
-%%<li>The node does not exist.</li>
-%%<li>The request specifies a subscription ID that is not valid or current.</li>
-%%</ul>
--spec(unsubscribe_node/5 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  From  :: jid(),
-  JID   :: binary() | ljid(),
-  SubId :: mod_pubsub:subId())
-    -> {result, []}
-    %%%
-     | {error, xmlel()}
-).
-unsubscribe_node(Host, Node, From, JID, SubId)
-    when is_binary(JID) ->
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J ->
-                   case jlib:jid_tolower(J) of
-                       error -> {<<"">>, <<"">>, <<"">>};
-                       J1 -> J1
-                   end
-                end,
-    unsubscribe_node(Host, Node, From, Subscriber, SubId);
-unsubscribe_node(Host, Node, From, Subscriber, SubId) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, unsubscribe_node,
-                              [NodeId, From, Subscriber, SubId])
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, default}} -> {result, []};
-%      {result, {_, Result}} -> {result, Result};
-      Error -> Error
-    end.
-
-%% @spec (Host::host(), ServerHost::host(), JID::jid(), Node::pubsubNode(), ItemId::string(), Payload::term())  ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
-%% @doc <p>Publish item to a PubSub node.</p>
-%% <p>The permission to publish an item must be verified by the plugin implementation.</p>
-%%<p>There are several reasons why the publish request might fail:</p>
-%%<ul>
-%%<li>The requesting entity does not have sufficient privileges to publish.</li>
-%%<li>The node does not support item publication.</li>
-%%<li>The node does not exist.</li>
-%%<li>The payload size exceeds a service-defined limit.</li>
-%%<li>The item contains more than one payload element or the namespace of the root payload element does not match the configured namespace for the node.</li>
-%%<li>The request does not match the node configuration.</li>
-%%</ul>
--spec(publish_item/6 ::
-(
-  Host       :: mod_pubsub:host(),
-  ServerHost :: binary(),
-  Node       :: mod_pubsub:nodeId(),
-  Publisher  :: jid(),
-  ItemId     :: <<>> | mod_pubsub:itemId(),
-  Payload    :: mod_pubsub:payload())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload) ->
-       publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, all).
-publish_item(Host, ServerHost, Node, Publisher, <<>>, Payload, Access) ->
-       publish_item(Host, ServerHost, Node, Publisher, uniqid(), Payload, Access);
-publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, Access) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PublishFeature = lists:member(<<"publish">>, Features),
-                    PublishModel = get_option(Options, publish_model),
-                    DeliverPayloads = get_option(Options, deliver_payloads),
-                    PersistItems = get_option(Options, persist_items),
-                    MaxItems = max_items(Host, Options),
-                    PayloadCount = payload_xmlelements(Payload),
-                    PayloadSize = byte_size(term_to_binary(Payload)) - 2,
-                    PayloadMaxSize = get_option(Options, max_payload_size),
-                    if not PublishFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                       unsupported, <<"publish">>)};
-                       PayloadSize > PayloadMaxSize ->
-                           {error,
-                            extended_error(?ERR_NOT_ACCEPTABLE, <<"payload-too-big">>)};
-                       (PayloadCount == 0) and (Payload == []) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"payload-required">>)};
-                       (PayloadCount > 1) or (PayloadCount == 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"invalid-payload">>)};
-                       (DeliverPayloads == false) and (PersistItems == false) and
-                         (PayloadSize > 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"item-forbidden">>)};
-                       ((DeliverPayloads == true) or (PersistItems == true)) and
-                         (PayloadSize == 0) ->
-                           {error,
-                            extended_error(?ERR_BAD_REQUEST, <<"item-required">>)};
-                       true ->
-                           node_call(Type, publish_item, [NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload])
-                   end
-           end,
-    ejabberd_hooks:run(pubsub_publish_item, ServerHost, [ServerHost, Node, Publisher, service_jid(Host), ItemId, Payload]),
-    Reply = [#xmlel{name = <<"pubsub">>,
-                   attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                   children =
-                       [#xmlel{name = <<"publish">>, attrs = nodeAttr(Node),
-                               children =
-                                   [#xmlel{name = <<"item">>,
-                                           attrs = itemAttr(ItemId),
-                                           children = []}]}]}],
-    case transaction(Host, Node, Action, sync_dirty) of
-       {result, {TNode, {Result, Broadcast, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
-           Type = TNode#pubsub_node.type,
-           Options = TNode#pubsub_node.options,
-           case get_option(Options, deliver_notifications) of
-                       true ->
-                               BroadcastPayload = case Broadcast of
-                                       default -> Payload;
-                                       broadcast -> Payload;
-                                       PluginPayload -> PluginPayload
-                               end,
-                               broadcast_publish_item(Host, Node, NodeId, Type, Options,
-                                       Removed, ItemId, jlib:jid_tolower(Publisher),
-                                       BroadcastPayload);
-                       false ->
-                               ok
-               end,
-           set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
-           case Result of
-               default -> {result, Reply};
-               _ -> {result, Result}
-           end;
-       {result, {TNode, {default, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
-           Type = TNode#pubsub_node.type,
-           Options = TNode#pubsub_node.options,
-           broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
-           set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
-           {result, Reply};
-       {result, {TNode, {Result, Removed}}} ->
-           NodeId = TNode#pubsub_node.id,
-           Type = TNode#pubsub_node.type,
-           Options = TNode#pubsub_node.options,
-           broadcast_retract_items(Host, Node, NodeId, Type, Options, Removed),
-           set_cached_item(Host, NodeId, ItemId, Publisher, Payload),
-           {result, Result};
-       {result, {_, default}} ->
-           {result, Reply};
-       {result, {_, Result}} ->
-           {result, Result};
-       {error, ?ERR_ITEM_NOT_FOUND} ->
-           %% handles auto-create feature
-           %% for automatic node creation. we'll take the default node type:
-           %% first listed into the plugins configuration option, or pep
-           Type = select_type(ServerHost, Host, Node),
-           case lists:member(<<"auto-create">>, features(Type)) of
-               true ->
-                   case create_node(Host, ServerHost, Node, Publisher, Type, Access, []) of
-                       {result, [#xmlel{name = <<"pubsub">>,
-                          attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                          children =
-                              [#xmlel{name = <<"create">>,
-                                      attrs = [{<<"node">>, NewNode}],
-                                      children = []}]}]} ->
-                           publish_item(Host, ServerHost,  NewNode,
-                                   Publisher, ItemId, Payload);
-                       _ ->
-                           {error, ?ERR_ITEM_NOT_FOUND}
-                   end;
-               false ->
-                   {error, ?ERR_ITEM_NOT_FOUND}
-           end;
-       Error ->
-           Error
-    end.
-
-%% @spec (Host::host(), JID::jid(), Node::pubsubNode(), ItemId::string()) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
--spec(delete_item/4 ::
-(
-  Host      :: mod_pubsub:host(),
-  Node      :: mod_pubsub:nodeId(),
-  Publisher :: jid(),
-  ItemId    :: mod_pubsub:itemId())
-    -> {result, []}
-    %%%
-     | {error, xmlel()}
-).
-%% @doc <p>Delete item from a PubSub node.</p>
-%% <p>The permission to delete an item must be verified by the plugin implementation.</p>
-%%<p>There are several reasons why the item retraction request might fail:</p>
-%%<ul>
-%%<li>The publisher does not have sufficient privileges to delete the requested item.</li>
-%%<li>The node or item does not exist.</li>
-%%<li>The request does not specify a node.</li>
-%%<li>The request does not include an <item/> element or the <item/> element does not specify an ItemId.</li>
-%%<li>The node does not support persistent items.</li>
-%%<li>The service does not support the deletion of items.</li>
-%%</ul>
-delete_item(Host, Node, Publisher, ItemId) ->
-    delete_item(Host, Node, Publisher, ItemId, false).
-
-
-delete_item(_, <<"">>, _, _, _) ->
-    {error,
-     extended_error(?ERR_BAD_REQUEST, <<"node-required">>)};
-delete_item(Host, Node, Publisher, ItemId, ForceNotify) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                    DeleteFeature = lists:member(<<"delete-items">>, Features),
-                    PublishModel = get_option(Options, publish_model),
-                    if %%->   iq_pubsub just does that matchs
-                       %%      %% Request does not specify an item
-                       %%      {error, extended_error(?ERR_BAD_REQUEST, "item-required")};
-                       not PersistentFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       not DeleteFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"delete-items">>)};
-                       true ->
-                           node_call(Type, delete_item,
-                                     [NodeId, Publisher, PublishModel, ItemId])
-                    end
-            end,
-    Reply = [],
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {TNode, {Result, broadcast}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         broadcast_retract_items(Host, Node, NodeId, Type,
-                                 Options, [ItemId], ForceNotify),
-         case get_cached_item(Host, NodeId) of
-           #pubsub_item{itemid = {ItemId, NodeId}} ->
-               unset_cached_item(Host, NodeId);
-           _ -> ok
-         end,
-         case Result of
-           default -> {result, Reply};
-           _ -> {result, Result}
-         end;
-      {result, {_, default}} -> {result, Reply};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
-    end.
-
-%% @spec (Host, JID, Node) ->
-%%                     {error, Reason} | {result, []}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      JID = jid()
-%%      Reason = stanzaError()
-%% @doc <p>Delete all items of specified node owned by JID.</p>
-%%<p>There are several reasons why the node purge request might fail:</p>
-%%<ul>
-%%<li>The node or service does not support node purging.</li>
-%%<li>The requesting entity does not have sufficient privileges to purge the node.</li>
-%%<li>The node is not configured to persist items.</li>
-%%<li>The specified node does not exist.</li>
-%%</ul>
--spec(purge_node/3 ::
-(
-  Host  :: mod_pubsub:host(),
-  Node  :: mod_pubsub:nodeId(),
-  Owner :: jid())
-    -> {result, []}
-    %%%
-     | {error, xmlel()}
-).
-purge_node(Host, Node, Owner) ->
-    Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    PurgeFeature = lists:member(<<"purge-nodes">>, Features),
-                    PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                    PersistentConfig = get_option(Options, persist_items),
-                    if not PurgeFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported, <<"purge-nodes">>)};
-                       not PersistentFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       not PersistentConfig ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"persistent-items">>)};
-                       true -> node_call(Type, purge_node, [NodeId, Owner])
-                    end
-            end,
-    Reply = [],
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {TNode, {Result, broadcast}}} ->
-         NodeId = TNode#pubsub_node.id,
-         Type = TNode#pubsub_node.type,
-         Options = TNode#pubsub_node.options,
-         broadcast_purge_node(Host, Node, NodeId, Type, Options),
-         unset_cached_item(Host, NodeId),
-         case Result of
-           default -> {result, Reply};
-           _ -> {result, Result}
-         end;
-      {result, {_, default}} -> {result, Reply};
-      {result, {_, Result}} -> {result, Result};
-      Error -> Error
-    end.
-
-%% @doc <p>Return the items of a given node.</p>
-%% <p>The number of items to return is limited by MaxItems.</p>
-%% <p>The permission are not checked in this function.</p>
-%% @todo We probably need to check that the user doing the query has the right
-%% to read the items.
--spec(get_items/7 ::
-(
-  Host      :: mod_pubsub:host(),
-  Node      :: mod_pubsub:nodeId(),
-  From      :: jid(),
-  SubId     :: mod_pubsub:subId(),
-  SMaxItems :: binary(),
-  ItemIDs   :: [mod_pubsub:itemId()],
-  Rsm       :: any())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-get_items(Host, Node, From, SubId, SMaxItems, ItemIDs, RSM) ->
-    MaxItems = if SMaxItems == <<"">> ->
-                     get_max_items_node(Host);
-                 true ->
-                     case catch jlib:binary_to_integer(SMaxItems) of
-                       {'EXIT', _} -> {error, ?ERR_BAD_REQUEST};
-                       Val -> Val
-                     end
-              end,
-    case MaxItems of
-      {error, Error} -> {error, Error};
-      _ ->
-         Action = fun (#pubsub_node{options = Options, type = Type, id = NodeId}) ->
-                          Features = features(Type),
-                          RetreiveFeature = lists:member(<<"retrieve-items">>, Features),
-                          PersistentFeature = lists:member(<<"persistent-items">>, Features),
-                          AccessModel = get_option(Options, access_model),
-                          AllowedGroups = get_option(Options, roster_groups_allowed, []),
-                          Owners = node_owners_call(Type, NodeId),
-                          {PresenceSubscription, RosterGroup} =
-                              get_presence_and_roster_permissions(Host, From, Owners,
-                                           AccessModel, AllowedGroups),
-                          if not RetreiveFeature ->
-                                 {error,
-                                  extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                 unsupported,
-                                                 <<"retrieve-items">>)};
-                             not PersistentFeature ->
-                                 {error,
-                                  extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                 unsupported,
-                                                 <<"persistent-items">>)};
-                             true ->
-                                 node_call(Type, get_items,
-                                           [NodeId, From, AccessModel,
-                                            PresenceSubscription, RosterGroup,
-                                            SubId, RSM])
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, {Items, RSMOut}}} ->
-               SendItems = case ItemIDs of
-                             [] -> Items;
-                             _ ->
-                                 lists:filter(fun (#pubsub_item{itemid =
-                                                                    {ItemId,
-                                                                     _}}) ->
-                                                      lists:member(ItemId,
-                                                                   ItemIDs)
-                                              end,
-                                              Items)
-                           end,
-               {result,
-                [#xmlel{name = <<"pubsub">>,
-                        attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                        children =
-                            [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                                    children =
-                                        itemsEls(lists:sublist(SendItems, MaxItems))}
-                                       | jlib:rsm_encode(RSMOut)]}]};
-           Error -> Error
-         end
-    end.
-
-get_items(Host, Node) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, get_items, [NodeId, service_jid(Host)])
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Items}} -> Items;
-      Error -> Error
-    end.
-
-get_item(Host, Node, ItemId) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    node_call(Type, get_item, [NodeId, ItemId])
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Items}} -> Items;
-      Error -> Error
-    end.
-
-get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners) ->
-    case get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, none) of
-       {result, {I, _}} -> {result, I};
-       Error -> Error
-    end.
-get_allowed_items_call(Host, NodeIdx, From, Type, Options, Owners, RSM) ->
-    AccessModel = get_option(Options, access_model),
-    AllowedGroups = get_option(Options, roster_groups_allowed, []),
-    {PresenceSubscription, RosterGroup} =
-           get_presence_and_roster_permissions(Host, From, Owners, AccessModel,
-               AllowedGroups),
-    node_call(Type, get_items,
-             [NodeIdx, From, AccessModel, PresenceSubscription, RosterGroup, undefined, RSM]).
-
-%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
-%%      Host = pubsubHost()
-%%      Node = pubsubNode()
-%%      NodeId = pubsubNodeId()
-%%      Type = pubsubNodeType()
-%%      Options = mod_pubsubnodeOptions()
-%%      LJID = {U, S, []}
-%%      Number = last | integer()
-%% @doc <p>Resend the items of a node to the user.</p>
-%% @todo use cache-last-item feature
-send_items(Host, Node, NodeId, Type, Options, LJID, last) ->
-    Stanza = case get_cached_item(Host, NodeId) of
-       undefined ->
-           % special ODBC optimization, works only with node_hometree_odbc, node_flat_odbc and node_pep_odbc
-           case node_action(Host, Type, get_last_items, [NodeId, LJID, 1]) of
-               {result, [LastItem]} ->
-                   {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
-                   event_stanza_with_delay(
-                       [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                         children = itemsEls([LastItem])}], ModifNow, ModifUSR);
-               _ ->
-                   event_stanza(
-                       [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                         children = itemsEls([])}])
-           end;
-      LastItem ->
-         {ModifNow, ModifUSR} =
-             LastItem#pubsub_item.modification,
-         event_stanza_with_delay([#xmlel{name =
-                                                      <<"items">>,
-                                                  attrs = nodeAttr(Node),
-                                                  children =
-                                                      itemsEls([LastItem])}],
-                                          ModifNow, ModifUSR)
-    end,
-    dispatch_items(Host, LJID, Node, Options, Stanza);
-send_items(Host, Node, NodeId, Type, Options, LJID, Number) ->
-    ToSend = case node_action(Host, Type, get_items,
-                             [NodeId, LJID])
-                of
-              {result, []} -> [];
-              {result, Items} ->
-                  case Number of
-                    N when N > 0 -> lists:sublist(Items, N);
-                    _ -> Items
-                  end;
-              _ -> []
-            end,
-    Stanza = case ToSend of
-              [] ->
-                  undefined;
-              [LastItem] ->
-                  {ModifNow, ModifUSR} =
-                      LastItem#pubsub_item.modification,
-                  event_stanza_with_delay([#xmlel{name = <<"items">>,
-                                                  attrs = nodeAttr(Node),
-                                                  children =
-                                                      itemsEls(ToSend)}],
-                                          ModifNow, ModifUSR);
-              _ ->
-                  event_stanza([#xmlel{name = <<"items">>,
-                                       attrs = nodeAttr(Node),
-                                       children = itemsEls(ToSend)}])
-            end,
-    dispatch_items(Host, LJID, Node, Options, Stanza).
-
--spec(dispatch_items/5 ::
-(
-  From    :: mod_pubsub:host(),
-  To      :: jid(),
-  Node    :: mod_pubsub:nodeId(),
-  Options :: mod_pubsub:nodeOptions(),
-  Stanza  :: xmlel() | undefined)
-    -> any()
-).
-
-dispatch_items(_From, _To, _Node, _Options, _Stanza = undefined) -> ok;
-dispatch_items({FromU, FromS, FromR} = From, {ToU, ToS, ToR} = To, Node,
-              Options, BaseStanza) ->
-    NotificationType = get_option(Options, notification_type, headline),
-    Stanza = add_message_type(BaseStanza, NotificationType),
-    C2SPid = case ejabberd_sm:get_session_pid(ToU, ToS, ToR) of
-              ToPid when is_pid(ToPid) -> ToPid;
-              _ ->
-                  R = user_resource(FromU, FromS, FromR),
-                  case ejabberd_sm:get_session_pid(FromU, FromS, R) of
-                    FromPid when is_pid(FromPid) -> FromPid;
-                    _ -> undefined
-                  end
-            end,
-    if C2SPid == undefined -> ok;
-       true ->
-          ejabberd_c2s:send_filtered(C2SPid,
-                                     {pep_message, <<Node/binary, "+notify">>},
-                                     service_jid(From), jlib:make_jid(To),
-                                     Stanza)
-    end;
-dispatch_items(From, To, _Node, Options, BaseStanza) ->
-    NotificationType = get_option(Options, notification_type, headline),
-    Stanza = add_message_type(BaseStanza, NotificationType),
-    ejabberd_router:route(service_jid(From), jlib:make_jid(To), Stanza).
-
-%% @spec (Host, JID, Plugins) -> {error, Reason} | {result, Response}
-%%      Host = host()
-%%      JID = jid()
-%%      Plugins = [Plugin::string()]
-%%      Reason = stanzaError()
-%%      Response = [pubsubIQResponse()]
-%% @doc <p>Return the list of affiliations as an XMPP response.</p>
--spec(get_affiliations/4 ::
-(
-  Host    :: mod_pubsub:host(),
-  Node    :: mod_pubsub:nodeId(),
-  JID     :: jid(),
-  Plugins :: [binary()])
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-get_affiliations(Host, <<>>, JID, Plugins)
-    when is_list(Plugins) ->
-    Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                Features = features(Type),
-                                RetrieveFeature =
-                                    lists:member(<<"retrieve-affiliations">>, Features),
-                                if not RetrieveFeature ->
-                                       {{error,
-                                         extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                        unsupported,
-                                                        <<"retrieve-affiliations">>)},
-                                        Acc};
-                                   true ->
-                                       {result, Affiliations} =
-                                           node_action(Host, Type,
-                                                       get_entity_affiliations,
-                                                       [Host, JID]),
-                                       {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
-    case Result of
-      {ok, Affiliations} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, Node}},
-                                        Affiliation}) ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}
-                                                       | nodeAttr(Node)],
-                                                  children = []}]
-                                  end,
-                                  lists:usort(lists:flatten(Affiliations))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
-    end;
-get_affiliations(Host, NodeId, JID, Plugins)
-    when is_list(Plugins) ->
-    Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                Features = features(Type),
-                                RetrieveFeature =
-                                    lists:member(<<"retrieve-affiliations">>,
-                                                 Features),
-                                if not RetrieveFeature ->
-                                       {{error,
-                                         extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                        unsupported,
-                                                        <<"retrieve-affiliations">>)},
-                                        Acc};
-                                   true ->
-                                       {result, Affiliations} =
-                                           node_action(Host, Type,
-                                                       get_entity_affiliations,
-                                                       [Host, JID]),
-                                       {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
-    case Result of
-      {ok, Affiliations} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, Node}},
-                                        Affiliation})
-                                          when NodeId == Node ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}
-                                                       | nodeAttr(Node)],
-                                                  children = []}];
-                                      (_) -> []
-                                  end,
-                                  lists:usort(lists:flatten(Affiliations))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
-    end.
-
--spec(get_affiliations/3 ::
-(
-  Host :: mod_pubsub:host(),
-  Node :: mod_pubsub:nodeId(),
-  JID  :: jid())
-    -> {result, [xmlel(),...]}
-    %%%
-     | {error, xmlel()}
-).
-get_affiliations(Host, Node, JID) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    RetrieveFeature =
-                        lists:member(<<"modify-affiliations">>, Features),
-                    {result, Affiliation} = node_call(Type, get_affiliation,
-                                                      [NodeId, JID]),
-                    if not RetrieveFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"modify-affiliations">>)};
-                       Affiliation /= owner -> {error, ?ERR_FORBIDDEN};
-                       true -> node_call(Type, get_node_affiliations, [NodeId])
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, []}} -> {error, ?ERR_ITEM_NOT_FOUND};
-      {result, {_, Affiliations}} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({AJID, Affiliation}) ->
-                                          [#xmlel{name = <<"affiliation">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"affiliation">>,
-                                                        affiliation_to_string(Affiliation)}],
-                                                  children = []}]
-                                  end,
-                                  Affiliations),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                  children =
-                      [#xmlel{name = <<"affiliations">>,
-                              attrs = nodeAttr(Node), children = Entities}]}]};
-      Error -> Error
-    end.
-
--spec(set_affiliations/4 ::
-(
-  Host        :: mod_pubsub:host(),
-  Node        :: mod_pubsub:nodeId(),
-  From        :: jid(),
-  EntitiesEls :: [xmlel()])
-    -> {result, []}
-    %%%
-     | {error, xmlel()}
-).
-set_affiliations(Host, Node, From, EntitiesEls) ->
-    Owner = jlib:jid_tolower(jlib:jid_remove_resource(From)),
-    Entities = lists:foldl(fun (El, Acc) ->
-                                  case Acc of
-                                    error -> error;
-                                    _ ->
-                                        case El of
-                                          #xmlel{name = <<"affiliation">>,
-                                                 attrs = Attrs} ->
-                                              JID =
-                                                  jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
-                                                                                    Attrs)),
-                                              Affiliation =
-                                                  string_to_affiliation(xml:get_attr_s(<<"affiliation">>,
-                                                                                       Attrs)),
-                                              if (JID == error) or
-                                                   (Affiliation == false) ->
-                                                     error;
-                                                 true ->
-                                                     [{jlib:jid_tolower(JID),
-                                                       Affiliation}
-                                                      | Acc]
-                                              end
-                                        end
-                                  end
-                          end,
-                          [], EntitiesEls),
-    case Entities of
-      error -> {error, ?ERR_BAD_REQUEST};
-      _ ->
-         Action = fun (#pubsub_node{type = Type,
-                                    id = NodeId}) ->
-                          Owners = node_owners_call(Type, NodeId),
-                          case lists:member(Owner, Owners) of
-                            true ->
-                                OwnerJID = jlib:make_jid(Owner),
-                                FilteredEntities = case Owners of
-                                                     [Owner] ->
-                                                         [E
-                                                          || E <- Entities,
-                                                             element(1, E) =/=
-                                                               OwnerJID];
-                                                     _ -> Entities
-                                                   end,
-                                lists:foreach(fun ({JID, Affiliation}) ->
-                                               node_call(Type, set_affiliation, [NodeId, JID, Affiliation])
-                                              end,
-                                              FilteredEntities),
-                                {result, []};
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
-    end.
-
-get_options(Host, Node, JID, SubID, Lang) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
-                    case lists:member(<<"subscription-options">>, features(Type)) of
-                      true ->
-                          get_options_helper(JID, Lang, Node, NodeID, SubID, Type);
-                      false ->
-                          {error,
-                           extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                          unsupported,
-                                          <<"subscription-options">>)}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_Node, XForm}} -> {result, [XForm]};
-      Error -> Error
-    end.
-
-get_options_helper(JID, Lang, Node, NodeID, SubID, Type) ->
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J -> case jlib:jid_tolower(J) of
-                   error -> {<<"">>, <<"">>, <<"">>};
-                   J1 -> J1
-                  end
-                end,
-    {result, Subs} = node_call(Type, get_subscriptions,
-                              [NodeID, Subscriber]),
-    SubIDs = lists:foldl(fun ({subscribed, SID}, Acc) ->
-                                [SID | Acc];
-                            (_, Acc) -> Acc
-                        end,
-                        [], Subs),
-    case {SubID, SubIDs} of
-      {_, []} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE, <<"not-subscribed">>)};
-      {<<>>, [SID]} ->
-         read_sub(Subscriber, Node, NodeID, SID, Lang);
-      {<<>>, _} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE, <<"subid-required">>)};
-      {_, _} ->
-         ValidSubId = lists:member(SubID, SubIDs),
-         if ValidSubId ->
-                read_sub(Subscriber, Node, NodeID, SubID, Lang);
-            true ->
-                {error,
-                 extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)}
-          end
-    end.
-
-read_sub(Subscriber, Node, NodeID, SubID, Lang) ->
-    Children = case pubsub_subscription_odbc:get_subscription(Subscriber, NodeID, SubID) of
-       {error, notfound} ->
-           [];
-       {result, #pubsub_subscription{options = Options}} ->
-           {result, XdataEl} = pubsub_subscription_odbc:get_options_xform(Lang, Options),
-           [XdataEl]
-       end,
-    OptionsEl = #xmlel{name = <<"options">>,
-                       attrs =
-                           [{<<"jid">>, jlib:jid_to_string(Subscriber)},
-                           {<<"subid">>, SubID}
-                           | nodeAttr(Node)],
-                       children = Children},
-    PubsubEl = #xmlel{name = <<"pubsub">>,
-                       attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                       children = [OptionsEl]},
-    {result, PubsubEl}.
-
-set_options(Host, Node, JID, SubID, Configuration) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeID}) ->
-                    case lists:member(<<"subscription-options">>,
-                                      features(Type))
-                        of
-                      true ->
-                          set_options_helper(Configuration, JID, NodeID, SubID,
-                                             Type);
-                      false ->
-                          {error,
-                           extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                          unsupported,
-                                          <<"subscription-options">>)}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_Node, Result}} -> {result, Result};
-      Error -> Error
-    end.
-
-set_options_helper(Configuration, JID, NodeID, SubID, Type) ->
-    SubOpts = case pubsub_subscription_odbc:parse_options_xform(Configuration) of
-               {result, GoodSubOpts} -> GoodSubOpts;
-               _ -> invalid
-             end,
-    Subscriber = case jlib:string_to_jid(JID) of
-                  error -> {<<"">>, <<"">>, <<"">>};
-                  J -> jlib:jid_tolower(J)
-                end,
-    {result, Subs} = node_call(Type, get_subscriptions,
-                              [NodeID, Subscriber]),
-    SubIDs = lists:foldl(fun ({subscribed, SID}, Acc) ->
-                                [SID | Acc];
-                            (_, Acc) -> Acc
-                        end,
-                        [], Subs),
-    case {SubID, SubIDs} of
-      {_, []} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"not-subscribed">>)};
-      {<<>>, [SID]} ->
-         write_sub(Subscriber, NodeID, SID, SubOpts);
-      {<<>>, _} ->
-         {error,
-          extended_error(?ERR_NOT_ACCEPTABLE,
-                         <<"subid-required">>)};
-      {_, _} -> write_sub(Subscriber, NodeID, SubID, SubOpts)
-    end.
-
-write_sub(_Subscriber, _NodeID, _SubID, invalid) ->
-    {error, extended_error(?ERR_BAD_REQUEST, <<"invalid-options">>)};
-write_sub(Subscriber, NodeID, SubID, Options) ->
-    case pubsub_subscription_odbc:set_subscription(Subscriber, NodeID, SubID, Options) of
-       {error, notfound} ->
-           {error, extended_error(?ERR_NOT_ACCEPTABLE, <<"invalid-subid">>)};
-       {result, _} ->
-           {result, []}
-    end.
-
-%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
-%%      Host = host()
-%%      Node = pubsubNode()
-%%      JID = jid()
-%%      Plugins = [Plugin::string()]
-%%      Reason = stanzaError()
-%%      Response = [pubsubIQResponse()]
-%% @doc <p>Return the list of subscriptions as an XMPP response.</p>
-get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
-    Result = lists:foldl(
-              fun(Type, {Status, Acc}) ->
-                      Features = features(Type),
-                      RetrieveFeature = lists:member(<<"retrieve-subscriptions">>, Features),
-                      if
-                          not RetrieveFeature ->
-                              %% Service does not support retreive subscriptions
-                              {{error, extended_error(?ERR_FEATURE_NOT_IMPLEMENTED, unsupported, <<"retrieve-subscriptions">>)}, Acc};
-                          true ->
-                              Subscriber = jlib:jid_remove_resource(JID),
-                              {result, Subscriptions} = node_action(Host, Type, get_entity_subscriptions, [Host, Subscriber]),
-                              {Status, [Subscriptions|Acc]}
-                      end
-              end, {ok, []}, Plugins),
-    case Result of
-      {ok, Subscriptions} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end;
-                                      ({_, none, _}) -> [];
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription, SubID, SubJID}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subid">>,
-                                                              SubID},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subid">>,
-                                                              SubID},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end;
-                                      ({#pubsub_node{nodeid = {_, SubsNode}},
-                                        Subscription, SubJID}) ->
-                                          case Node of
-                                            <<>> ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}
-                                                             | nodeAttr(SubsNode)],
-                                                        children = []}];
-                                            SubsNode ->
-                                                [#xmlel{name =
-                                                            <<"subscription">>,
-                                                        attrs =
-                                                            [{<<"jid">>,
-                                                              jlib:jid_to_string(SubJID)},
-                                                             {<<"subscription">>,
-                                                              subscription_to_string(Subscription)}],
-                                                        children = []}];
-                                            _ -> []
-                                          end
-                                  end,
-                                  lists:usort(lists:flatten(Subscriptions))),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB}],
-                  children =
-                      [#xmlel{name = <<"subscriptions">>, attrs = [],
-                              children = Entities}]}]};
-      {Error, _} -> Error
-    end.
-
-get_subscriptions(Host, Node, JID) ->
-    Action = fun (#pubsub_node{type = Type, id = NodeId}) ->
-                    Features = features(Type),
-                    RetrieveFeature =
-                        lists:member(<<"manage-subscriptions">>, Features),
-                    {result, Affiliation} = node_call(Type, get_affiliation,
-                                                      [NodeId, JID]),
-                    if not RetrieveFeature ->
-                           {error,
-                            extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                           unsupported,
-                                           <<"manage-subscriptions">>)};
-                       Affiliation /= owner -> {error, ?ERR_FORBIDDEN};
-                       true ->
-                           node_call(Type, get_node_subscriptions, [NodeId])
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Subscriptions}} ->
-         Entities = lists:flatmap(fun ({_, none}) -> [];
-                                      ({_, pending, _}) -> [];
-                                      ({AJID, Subscription}) ->
-                                          [#xmlel{name = <<"subscription">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"subscription">>,
-                                                        subscription_to_string(Subscription)}],
-                                                  children = []}];
-                                      ({AJID, Subscription, SubId}) ->
-                                          [#xmlel{name = <<"subscription">>,
-                                                  attrs =
-                                                      [{<<"jid">>,
-                                                        jlib:jid_to_string(AJID)},
-                                                       {<<"subscription">>,
-                                                        subscription_to_string(Subscription)},
-                                                       {<<"subid">>, SubId}],
-                                                  children = []}]
-                                  end,
-                                  Subscriptions),
-         {result,
-          [#xmlel{name = <<"pubsub">>,
-                  attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                  children =
-                      [#xmlel{name = <<"subscriptions">>,
-                              attrs = nodeAttr(Node), children = Entities}]}]};
-      Error -> Error
-    end.
-
-set_subscriptions(Host, Node, From, EntitiesEls) ->
-    Owner =
-       jlib:jid_tolower(jlib:jid_remove_resource(From)),
-    Entities = lists:foldl(fun (El, Acc) ->
-                                  case Acc of
-                                    error -> error;
-                                    _ ->
-                                        case El of
-                                          #xmlel{name = <<"subscription">>,
-                                                 attrs = Attrs} ->
-                                              JID =
-                                                  jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
-                                                                                    Attrs)),
-                                              Subscription =
-                                                  string_to_subscription(xml:get_attr_s(<<"subscription">>,
-                                                                                        Attrs)),
-                                              SubId =
-                                                  xml:get_attr_s(<<"subid">>,
-                                                                 Attrs),
-                                              if (JID == error) or
-                                                   (Subscription == false) ->
-                                                     error;
-                                                 true ->
-                                                     [{jlib:jid_tolower(JID),
-                                                       Subscription, SubId}
-                                                      | Acc]
-                                              end
-                                        end
-                                  end
-                          end,
-                          [], EntitiesEls),
-    case Entities of
-      error -> {error, ?ERR_BAD_REQUEST};
-      _ ->
-         Notify = fun (JID, Sub, _SubId) ->
-                          Stanza = #xmlel{name = <<"message">>, attrs = [],
-                                          children =
-                                              [#xmlel{name = <<"pubsub">>,
-                                                      attrs =
-                                                          [{<<"xmlns">>,
-                                                            ?NS_PUBSUB}],
-                                                      children =
-                                                          [#xmlel{name =
-                                                                      <<"subscription">>,
-                                                                  attrs =
-                                                                      [{<<"jid">>,
-                                                                        jlib:jid_to_string(JID)},
-                                                                       {<<"subscription">>,
-                                                                        subscription_to_string(Sub)}
-                                                                       | nodeAttr(Node)],
-                                                                  children =
-                                                                      []}]}]},
-                          ejabberd_router:route(service_jid(Host),
-                                                jlib:make_jid(JID), Stanza)
-                  end,
-         Action = fun (#pubsub_node{type = Type,
-                                    id = NodeId}) ->
-                          case lists:member(Owner, node_owners_call(Type, NodeId)) of
-                            true ->
-                                Result = lists:foldl(fun ({JID, Subscription,
-                                                           SubId},
-                                                          Acc) ->
-                                                             case
-                                                               node_call(Type,
-                                                                         set_subscriptions,
-                                                                         [NodeId,
-                                                                          JID,
-                                                                          Subscription,
-                                                                          SubId])
-                                                                 of
-                                                               {error, Err} ->
-                                                                   [{error,
-                                                                     Err}
-                                                                    | Acc];
-                                                               _ ->
-                                                                   Notify(JID,
-                                                                          Subscription,
-                                                                          SubId),
-                                                                   Acc
-                                                             end
-                                                     end,
-                                                     [], Entities),
-                                case Result of
-                                  [] -> {result, []};
-                                  _ -> {error, ?ERR_NOT_ACCEPTABLE}
-                                end;
-                            _ -> {error, ?ERR_FORBIDDEN}
-                          end
-                  end,
-         case transaction(Host, Node, Action, sync_dirty) of
-           {result, {_, Result}} -> {result, Result};
-           Other -> Other
-         end
-    end.
-
--spec(get_presence_and_roster_permissions/5 ::
-(
-  Host          :: mod_pubsub:host(),
-  From          :: ljid(),
-  Owners        :: [ljid(),...],
-  AccessModel   :: mod_pubsub:accessModel(),
-  AllowedGroups :: [binary()])
-    -> {PresenceSubscription::boolean(), RosterGroup::boolean()}
-).
-
-get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
-    if (AccessModel == presence) or (AccessModel == roster) ->
-          case Host of
-            {User, Server, _} ->
-                get_roster_info(User, Server, From, AllowedGroups);
-            _ ->
-                [{OUser, OServer, _} | _] = Owners,
-                get_roster_info(OUser, OServer, From, AllowedGroups)
-          end;
-       true -> {true, true}
-    end.
-
-%% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
-%%    -> {PresenceSubscription, RosterGroup}
-get_roster_info(_, _, {<<"">>, <<"">>, _}, _) ->
-    {false, false};
-get_roster_info(OwnerUser, OwnerServer,
-               {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
-    {Subscription, Groups} =
-       ejabberd_hooks:run_fold(roster_get_jid_info,
-                               OwnerServer, {none, []},
-                               [OwnerUser, OwnerServer,
-                                {SubscriberUser, SubscriberServer, <<"">>}]),
-    PresenceSubscription = Subscription == both orelse
-                            Subscription == from orelse
-                              {OwnerUser, OwnerServer} ==
-                                {SubscriberUser, SubscriberServer},
-    RosterGroup = lists:any(fun (Group) ->
-                                   lists:member(Group, AllowedGroups)
-                           end,
-                           Groups),
-    {PresenceSubscription, RosterGroup};
-get_roster_info(OwnerUser, OwnerServer, JID,
-               AllowedGroups) ->
-    get_roster_info(OwnerUser, OwnerServer,
-                   jlib:jid_tolower(JID), AllowedGroups).
-
-string_to_affiliation(<<"owner">>) -> owner;
-string_to_affiliation(<<"publisher">>) -> publisher;
-string_to_affiliation(<<"member">>) -> member;
-string_to_affiliation(<<"outcast">>) -> outcast;
-string_to_affiliation(<<"none">>) -> none;
-string_to_affiliation(_) -> false.
-
-string_to_subscription(<<"subscribed">>) -> subscribed;
-string_to_subscription(<<"pending">>) -> pending;
-string_to_subscription(<<"unconfigured">>) ->
-    unconfigured;
-string_to_subscription(<<"none">>) -> none;
-string_to_subscription(_) -> false.
-
-affiliation_to_string(owner) -> <<"owner">>;
-affiliation_to_string(publisher) -> <<"publisher">>;
-affiliation_to_string(member) -> <<"member">>;
-affiliation_to_string(outcast) -> <<"outcast">>;
-affiliation_to_string(_) -> <<"none">>.
-
-subscription_to_string(subscribed) -> <<"subscribed">>;
-subscription_to_string(pending) -> <<"pending">>;
-subscription_to_string(unconfigured) -> <<"unconfigured">>;
-subscription_to_string(_) -> <<"none">>.
-
--spec(service_jid/1 ::
-(
-  Host :: mod_pubsub:host())
-    -> jid()
-).
-service_jid(Host) ->
-    case Host of
-      {U, S, _} -> {jid, U, S, <<"">>, U, S, <<"">>};
-      _ -> {jid, <<"">>, Host, <<"">>, <<"">>, Host, <<"">>}
-    end.
-
-%% @spec (LJID, NotifyType, Depth, NodeOptions, SubOptions) -> boolean()
-%%     LJID = jid()
-%%     NotifyType = items | nodes
-%%     Depth = integer()
-%%     NodeOptions = [{atom(), term()}]
-%%     SubOptions = [{atom(), term()}]
-%% @doc <p>Check if a notification must be delivered or not based on
-%% node and subscription options.</p>
-is_to_deliver(LJID, NotifyType, Depth, NodeOptions,
-             SubOptions) ->
-    sub_to_deliver(LJID, NotifyType, Depth, SubOptions)
-      andalso node_to_deliver(LJID, NodeOptions).
-
-sub_to_deliver(_LJID, NotifyType, Depth, SubOptions) ->
-    lists:all(fun (Option) ->
-                     sub_option_can_deliver(NotifyType, Depth, Option)
-             end,
-             SubOptions).
-
-sub_option_can_deliver(items, _, {subscription_type, nodes}) -> false;
-sub_option_can_deliver(nodes, _, {subscription_type, items}) -> false;
-sub_option_can_deliver(_, _, {subscription_depth, all})      -> true;
-sub_option_can_deliver(_, Depth, {subscription_depth, D})    -> Depth =< D;
-sub_option_can_deliver(_, _, {deliver, false})        -> false;
-sub_option_can_deliver(_, _, {expire, When})            -> now() < When;
-sub_option_can_deliver(_, _, _)                              -> true.
-
-node_to_deliver(LJID, NodeOptions) ->
-    PresenceDelivery = get_option(NodeOptions, presence_based_delivery),
-    presence_can_deliver(LJID, PresenceDelivery).
-
--spec(presence_can_deliver/2 ::
-(
-  Entity :: ljid(),
-  _      :: boolean())
-    -> boolean()
-).
-presence_can_deliver(_, false) -> true;
-presence_can_deliver({User, Server, Resource}, true) ->
-    case mnesia:dirty_match_object({session, '_', '_', {User, Server}, '_', '_'}) of
-    [] -> false;
-    Ss ->
-       lists:foldl(fun(_, true) -> true;
-                      ({session, _, _ , _, undefined, _}, _Acc) -> false;
-                      ({session, _, {_, _, R}, _, _Priority, _}, _Acc) ->
-                          case Resource of
-                              [] -> true;
-                              R -> true;
-                              _ -> false
-                          end
-       end, false, Ss)
-    end.
-
--spec(state_can_deliver/2 ::
-(
-  Entity::ljid(),
-  SubOptions :: mod_pubsub:subOptions() | [])
-    -> [ljid()]
-).
-state_can_deliver({U, S, R}, []) -> [{U, S, R}];
-state_can_deliver({U, S, R}, SubOptions) ->
-    %% Check SubOptions for 'show_values'
-    case lists:keysearch('show_values', 1, SubOptions) of
-  %% If not in suboptions, item can be delivered, case doesn't apply
-  false -> [{U, S, R}];
-  %% If in a suboptions ...
-  {_, {_, ShowValues}} ->
-      %% Get subscriber resources
-      Resources = case R of
-    %% If the subscriber JID is a bare one, get all its resources
-    <<>> -> user_resources(U, S);
-    %% If the subscriber JID is a full one, use its resource
-    R  -> [R]
-      end,
-      %% For each resource, test if the item is allowed to be delivered
-      %% based on resource state
-      lists:foldl(
-        fun(Resource, Acc) ->
-          get_resource_state({U, S, Resource}, ShowValues, Acc)
-        end, [], Resources)
-    end.
-
--spec(get_resource_state/3 ::
-(
-  Entity     :: ljid(),
-  ShowValues :: [binary()],
-  JIDs       :: [ljid()])
-    -> [ljid()]
-).
-get_resource_state({U, S, R}, ShowValues, JIDs) ->
-    case ejabberd_sm:get_session_pid(U, S, R) of
-  %% If no PID, item can be delivered
-  none -> lists:append([{U, S, R}], JIDs);
-  %% If PID ...
-  Pid ->
-      %% Get user resource state
-      %% TODO : add a catch clause
-      Show = case ejabberd_c2s:get_presence(Pid) of
-    {_, _, <<"available">>, _} -> <<"online">>;
-    {_, _, State, _}           -> State
-      end,
-      %% Is current resource state listed in 'show-values' suboption ?
-      case lists:member(Show, ShowValues) of %andalso Show =/= "online" of
-    %% If yes, item can be delivered
-    true  -> lists:append([{U, S, R}], JIDs);
-    %% If no, item can't be delivered
-    false -> JIDs
-      end
-    end.
-
--spec(payload_xmlelements/1 ::
-(
-  Payload :: mod_pubsub:payload())
-    -> Count :: non_neg_integer()
-).
-%% @spec (Payload) -> int()
-%%     Payload = term()
-%% @doc <p>Count occurence of XML elements in payload.</p>
-payload_xmlelements(Payload) -> payload_xmlelements(Payload, 0).
-payload_xmlelements([], Count) -> Count;
-payload_xmlelements([#xmlel{} | Tail], Count) ->
-    payload_xmlelements(Tail, Count + 1);
-payload_xmlelements([_ | Tail], Count) ->
-    payload_xmlelements(Tail, Count).
-
-%% @spec (Els) -> stanza()
-%%     Els = [xmlelement()]
-%% @doc <p>Build pubsub event stanza</p>
-event_stanza(Els) ->
-    #xmlel{name = <<"message">>, attrs = [],
-          children =
-              [#xmlel{name = <<"event">>,
-                      attrs = [{<<"xmlns">>, ?NS_PUBSUB_EVENT}],
-                      children = Els}]}.
-
-event_stanza_with_delay(Els, ModifNow, ModifUSR) ->
-    jlib:add_delay_info(event_stanza(Els), ModifUSR, ModifNow).
-
-%%%%%% broadcast functions
-
-broadcast_publish_item(Host, Node, NodeId, Type, NodeOptions, Removed, ItemId, From, Payload) ->
-    case get_collection_subscriptions(Host, Node) of
-       SubsByDepth when is_list(SubsByDepth) ->
-           Content = case get_option(NodeOptions, deliver_payloads) of
-               true -> Payload;
-               false -> []
-           end,
-           Stanza = event_stanza(
-               [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                       children = [#xmlel{name = <<"item">>, attrs = itemAttr(ItemId),
-                                          children = Content}]}]),
-           broadcast_stanza(Host, From, Node, NodeId, Type,
-                            NodeOptions, SubsByDepth, items, Stanza, true),
-           case Removed of
-               [] ->
-                   ok;
-               _ ->
-                   case get_option(NodeOptions, notify_retract) of
-                       true ->
-                           RetractStanza = event_stanza(
-                               [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                                       children = [#xmlel{name = <<"retract">>, attrs = itemAttr(RId)} || RId <- Removed]}]),
-                           broadcast_stanza(Host, Node, NodeId, Type,
-                                            NodeOptions, SubsByDepth,
-                                            items, RetractStanza, true);
-                       _ ->
-                           ok
-                   end
-           end,
-           {result, true};
-       _ ->
-           {result, false}
-    end.
-
-broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds) ->
-    broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, false).
-broadcast_retract_items(_Host, _Node, _NodeId, _Type, _NodeOptions, [], _ForceNotify) ->
-    {result, false};
-broadcast_retract_items(Host, Node, NodeId, Type, NodeOptions, ItemIds, ForceNotify) ->
-    case (get_option(NodeOptions, notify_retract) or ForceNotify) of
-       true ->
-           case get_collection_subscriptions(Host, Node) of
-               SubsByDepth when is_list(SubsByDepth) ->
-                   Stanza = event_stanza(
-                       [#xmlel{name = <<"items">>, attrs = nodeAttr(Node),
-                               children = [#xmlel{name = <<"retract">>, attrs = itemAttr(ItemId)} || ItemId <- ItemIds]}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, items, Stanza, true),
-                   {result, true};
-               _ ->
-                   {result, false}
-           end;
-       _ ->
-           {result, false}
-    end.
-
-broadcast_purge_node(Host, Node, NodeId, Type, NodeOptions) ->
-    case get_option(NodeOptions, notify_retract) of
-       true ->
-           case get_collection_subscriptions(Host, Node) of
-               SubsByDepth when is_list(SubsByDepth) ->
-                   Stanza = event_stanza(
-                       [#xmlel{name = <<"purge">>, attrs = nodeAttr(Node)}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
-                   {result, true};
-               _ -> 
-                   {result, false}
-           end;
-       _ ->
-           {result, false}
-    end.
-
-broadcast_removed_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
-    case get_option(NodeOptions, notify_delete) of
-       true ->
-           case SubsByDepth of
-               [] ->
-                   {result, false};
-               _ ->
-                   Stanza = event_stanza(
-                       [#xmlel{name = <<"delete">>, attrs = nodeAttr(Node)}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
-                   {result, true}
-           end;
-       _ ->
-           {result, false}
-    end.
-
-broadcast_created_node(_, _, _, _, _, []) ->
-    {result, false};
-broadcast_created_node(Host, Node, NodeId, Type, NodeOptions, SubsByDepth) ->
-    Stanza = event_stanza([#xmlel{name = <<"create">>, attrs = nodeAttr(Node)}]),
-    broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, nodes, Stanza, true),
-    {result, true}.
-
-broadcast_config_notification(Host, Node, NodeId, Type, NodeOptions, Lang) ->
-    case get_option(NodeOptions, notify_config) of
-       true ->
-           case get_collection_subscriptions(Host, Node) of
-               SubsByDepth when is_list(SubsByDepth) ->
-                   Content = case get_option(NodeOptions, deliver_payloads) of
-                       true ->
-                           [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
-                                   children = get_configure_xfields(Type, NodeOptions, Lang, [])}];
-                       false ->
-                           []
-                   end,
-                   Stanza = event_stanza(
-                       [#xmlel{name = <<"configuration">>, attrs = nodeAttr(Node), children = Content}]),
-                   broadcast_stanza(Host, Node, NodeId, Type,
-                                    NodeOptions, SubsByDepth, nodes, Stanza, false),
-                   {result, true};
-               _ ->
-                   {result, false}
-           end;
-       _ ->
-           {result, false}
-    end.
-
-get_collection_subscriptions(Host, Node) ->
-    Action = fun() ->
-           {result, lists:map(fun({Depth, Nodes}) ->
-                       {Depth, [{N, get_node_subs(N)} || N <- Nodes]}
-           end, tree_call(Host, get_parentnodes_tree, [Host, Node, service_jid(Host)]))}
-       end,
-    case transaction(Host, Action, sync_dirty) of
-       {result, CollSubs} -> CollSubs;
-       _ -> []
-    end.
-
-get_node_subs(#pubsub_node{type   = Type,
-                          id     = NodeID}) ->
-    case node_call(Type, get_node_subscriptions, [NodeID]) of
-       {result, Subs} -> get_options_for_subs(NodeID, Subs);
-       Other -> Other
-    end.
-
-get_options_for_subs(NodeID, Subs) ->
-    lists:foldl(fun({JID, subscribed, SubID}, Acc) ->
-                       case pubsub_subscription_odbc:get_subscription(JID, NodeID, SubID) of
-                           {error, notfound} -> [{JID, SubID, []} | Acc];
-                           {result, #pubsub_subscription{options = Options}} -> [{JID, SubID, Options} | Acc];
-                           _ -> Acc
-                       end;
-                   (_, Acc) ->
-                       Acc
-               end, [], Subs).
-
-broadcast_stanza(Host, _Node, _NodeId, _Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
-    NotificationType = get_option(NodeOptions, notification_type, headline),
-    BroadcastAll = get_option(NodeOptions, broadcast_all_resources), %% XXX this is not standard, but usefull
-    From = service_jid(Host),
-    Stanza = add_message_type(BaseStanza, NotificationType),
-    %% Handles explicit subscriptions
-    SubIDsByJID = subscribed_nodes_by_jid(NotifyType, SubsByDepth),
-    lists:foreach(fun ({LJID, NodeName, SubIDs}) ->
-                         LJIDs = case BroadcastAll of
-                                     true ->
-                                         {U, S, _} = LJID,
-                                         [{U, S, R} || R <- user_resources(U, S)];
-                                     false ->
-                                         [LJID]
-                                 end,
-        %% Determine if the stanza should have SHIM ('SubID' and 'name') headers
-             StanzaToSend = case {SHIM, SubIDs} of
-                                                {false, _} ->
-                                                  Stanza;
-                                                %% If there's only one SubID, don't add it
-                                                {true, [_]} ->
-                                                  add_shim_headers(Stanza, collection_shim(NodeName));
-                                                {true, SubIDs} ->
-                                                  add_shim_headers(Stanza, lists:append(collection_shim(NodeName), subid_shim(SubIDs)))
-                                  end,
-                         lists:foreach(fun(To) ->
-                                       ejabberd_router:route(From, jlib:make_jid(To), StanzaToSend)
-                               end, LJIDs)
-               end, SubIDsByJID).
-
-broadcast_stanza({LUser, LServer, LResource}, Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
-    broadcast_stanza({LUser, LServer, LResource}, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM),
-    %% Handles implicit presence subscriptions
-    SenderResource = user_resource(LUser, LServer, LResource),
-    case ejabberd_sm:get_session_pid(LUser, LServer, SenderResource) of
-       C2SPid when is_pid(C2SPid) ->
-           NotificationType = get_option(NodeOptions, notification_type, headline),
-           Stanza = add_message_type(BaseStanza, NotificationType),
-           %% set the from address on the notification to the bare JID of the account owner
-           %% Also, add "replyto" if entity has presence subscription to the account owner
-           %% See XEP-0163 1.1 section 4.3.1
-           ejabberd_c2s:broadcast(C2SPid,
-               {pep_message, <<((Node))/binary, "+notify">>},
-               _Sender = jlib:make_jid(LUser, LServer, <<"">>),
-               _StanzaToSend = add_extended_headers(Stanza,
-                   _ReplyTo = extended_headers([jlib:jid_to_string(Publisher)])));
-       _ ->
-           ?DEBUG("~p@~p has no session; can't deliver ~p to contacts", [LUser, LServer, BaseStanza])
-    end;
-broadcast_stanza(Host, _Publisher, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM) ->
-    broadcast_stanza(Host, Node, NodeId, Type, NodeOptions, SubsByDepth, NotifyType, BaseStanza, SHIM).
-
-subscribed_nodes_by_jid(NotifyType, SubsByDepth) ->
-    NodesToDeliver = fun(Depth, Node, Subs, Acc) ->
-           NodeName = case Node#pubsub_node.nodeid of
-               {_, N} -> N;
-               Other -> Other
-           end,
-           NodeOptions = Node#pubsub_node.options,
-           lists:foldl(fun({LJID, SubID, SubOptions}, {JIDs, Recipients}) ->
-               case is_to_deliver(LJID, NotifyType, Depth, NodeOptions, SubOptions) of
-       true  ->
-                 %% If is to deliver :
-                 case state_can_deliver(LJID, SubOptions) of
-               []            -> {JIDs, Recipients};
-               JIDsToDeliver ->
-                   lists:foldl(
-                     fun(JIDToDeliver, {JIDsAcc, RecipientsAcc}) ->
-                   case lists:member(JIDToDeliver, JIDs) of
-                   %% check if the JIDs co-accumulator contains the Subscription Jid,
-                 false ->
-                       %%  - if not,
-                       %%  - add the Jid to JIDs list co-accumulator ;
-                       %%  - create a tuple of the Jid, NodeId, and SubID (as list),
-                       %%    and add the tuple to the Recipients list co-accumulator
-                           {[JIDToDeliver | JIDsAcc], [{JIDToDeliver, NodeName, [SubID]} | RecipientsAcc]};
-                 true ->
-                       %% - if the JIDs co-accumulator contains the Jid
-                       %%   get the tuple containing the Jid from the Recipient list co-accumulator
-                           {_, {JIDToDeliver, NodeName1, SubIDs}} = lists:keysearch(JIDToDeliver, 1, RecipientsAcc),
-                       %%   delete the tuple from the Recipients list
-                       % v1 : Recipients1 = lists:keydelete(LJID, 1, Recipients),
-                       % v2 : Recipients1 = lists:keyreplace(LJID, 1, Recipients, {LJID, NodeId1, [SubID | SubIDs]}),
-                       %%   add the SubID to the SubIDs list in the tuple,
-                       %%   and add the tuple back to the Recipients list co-accumulator
-                       % v1.1 : {JIDs, lists:append(Recipients1, [{LJID, NodeId1, lists:append(SubIDs, [SubID])}])}
-                       % v1.2 : {JIDs, [{LJID, NodeId1, [SubID | SubIDs]} | Recipients1]}
-                       % v2: {JIDs, Recipients1}
-                           {JIDsAcc, lists:keyreplace(JIDToDeliver, 1, RecipientsAcc, {JIDToDeliver, NodeName1, [SubID | SubIDs]})}
-                   end
-                     end, {JIDs, Recipients}, JIDsToDeliver)
-                 end;
-               false ->
-                   {JIDs, Recipients}
-               end
-           end, Acc, Subs)
-       end,
-    DepthsToDeliver = fun({Depth, SubsByNode}, Acc1) ->
-           lists:foldl(fun({Node, Subs}, Acc2) ->
-                   NodesToDeliver(Depth, Node, Subs, Acc2)
-           end, Acc1, SubsByNode)
-       end,
-    {_, JIDSubs} = lists:foldl(DepthsToDeliver, {[], []}, SubsByDepth),
-    JIDSubs.
-
-user_resources(User, Server) ->
-    ejabberd_sm:get_user_resources(User, Server).
-
-user_resource(User, Server, <<>>) ->
-    case user_resources(User, Server) of
-       [R | _] -> R;
-       _ -> <<>>
-    end;
-user_resource(_, _, Resource) -> Resource.
-
-%%%%%%% Configuration handling
-
-%%<p>There are several reasons why the default node configuration options request might fail:</p>
-%%<ul>
-%%<li>The service does not support node configuration.</li>
-%%<li>The service does not support retrieval of default node configuration.</li>
-%%</ul>
-get_configure(Host, ServerHost, Node, From, Lang) ->
-    Action = fun (#pubsub_node{options = Options,
-                              type = Type, id = NodeId}) ->
-                    case node_call(Type, get_affiliation, [NodeId, From]) of
-                      {result, owner} ->
-                          Groups = ejabberd_hooks:run_fold(roster_groups,
-                                                           ServerHost, [],
-                                                           [ServerHost]),
-                          {result,
-                           [#xmlel{name = <<"pubsub">>,
-                                   attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-                                   children =
-                                       [#xmlel{name = <<"configure">>,
-                                               attrs = nodeAttr(Node),
-                                               children =
-                                                   [#xmlel{name = <<"x">>,
-                                                           attrs =
-                                                               [{<<"xmlns">>,
-                                                                 ?NS_XDATA},
-                                                                {<<"type">>,
-                                                                 <<"form">>}],
-                                                           children =
-                                                               get_configure_xfields(Type,
-                                                                                     Options,
-                                                                                     Lang,
-                                                                                     Groups)}]}]}]};
-                      _ -> {error, ?ERR_FORBIDDEN}
-                    end
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, {_, Result}} -> {result, Result};
-      Other -> Other
-    end.
-
-get_default(Host, Node, _From, Lang) ->
-    Type = select_type(Host, Host, Node),
-    Options = node_options(Type),
-%% Get node option
-%% The result depend of the node type plugin system.
-    {result,
-     [#xmlel{name = <<"pubsub">>,
-            attrs = [{<<"xmlns">>, ?NS_PUBSUB_OWNER}],
-            children =
-                [#xmlel{name = <<"default">>, attrs = [],
-                        children =
-                            [#xmlel{name = <<"x">>,
-                                    attrs =
-                                        [{<<"xmlns">>, ?NS_XDATA},
-                                         {<<"type">>, <<"form">>}],
-                                    children =
-                                        get_configure_xfields(Type, Options,
-                                                              Lang, [])}]}]}]}.
-
-get_option([], _) -> false;
-get_option(Options, Var) ->
-    get_option(Options, Var, false).
-
-get_option(Options, Var, Def) ->
-    case lists:keysearch(Var, 1, Options) of
-      {value, {_Val, Ret}} -> Ret;
-      _ -> Def
-    end.
-
-%% Get default options from the module plugin.
-node_options(Type) ->
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary,
-                               (?ODBC_SUFFIX)/binary>>),
-    case catch Module:options() of
-      {'EXIT', {undef, _}} ->
-         DefaultModule =
-             jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                                     (?STDNODE)/binary,
-                                     (?ODBC_SUFFIX)/binary>>),
-         DefaultModule:options();
-      Result -> Result
-    end.
-
-%% @spec (Host, Options) -> MaxItems
-%%      Host = host()
-%%      Options = [Option]
-%%      Option = {Key::atom(), Value::term()}
-%%      MaxItems = integer() | unlimited
-%% @doc <p>Return the maximum number of items for a given node.</p>
-%% <p>Unlimited means that there is no limit in the number of items that can
-%% be stored.</p>
-%% @todo In practice, the current data structure means that we cannot manage
-%% millions of items on a given node. This should be addressed in a new
-%% version.
-max_items(Host, Options) ->
-    case get_option(Options, persist_items) of
-      true ->
-         case get_option(Options, max_items) of
-           false -> unlimited;
-           Result when Result < 0 -> 0;
-           Result -> Result
-         end;
-      false ->
-         case get_option(Options, send_last_published_item) of
-           never -> 0;
-           _ ->
-               case is_last_item_cache_enabled(Host) of
-                 true -> 0;
-                 false -> 1
-               end
-         end
-    end.
-
--define(BOOL_CONFIG_FIELD(Label, Var),
-       ?BOOLXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (get_option(Options, Var)))).
-
--define(STRING_CONFIG_FIELD(Label, Var),
-       ?STRINGXFIELD(Label,
-                     <<"pubsub#",
-                       (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                     (get_option(Options, Var, <<"">>)))).
-
--define(INTEGER_CONFIG_FIELD(Label, Var),
-       ?STRINGXFIELD(Label,
-                     <<"pubsub#",
-                       (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                     (iolist_to_binary(integer_to_list(get_option(Options,
-                                                                  Var)))))).
-
--define(JLIST_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (jlib:jid_to_string(get_option(Options, Var))),
-                   [jlib:jid_to_string(O) || O <- Opts])).
-
--define(ALIST_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTXFIELD(Label,
-                   <<"pubsub#",
-                     (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                   (iolist_to_binary(atom_to_list(get_option(Options,
-                                                             Var)))),
-                   [iolist_to_binary(atom_to_list(O)) || O <- Opts])).
-
--define(LISTM_CONFIG_FIELD(Label, Var, Opts),
-       ?LISTMXFIELD(Label,
-                    <<"pubsub#",
-                      (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                    (get_option(Options, Var)), Opts)).
-
--define(NLIST_CONFIG_FIELD(Label, Var),
-       ?STRINGMXFIELD(Label,
-                      <<"pubsub#",
-                        (iolist_to_binary(atom_to_list(Var)))/binary>>,
-                      get_option(Options, Var, []))).
-
-get_configure_xfields(_Type, Options, Lang, Groups) ->
-    [?XFIELD(<<"hidden">>, <<"">>, <<"FORM_TYPE">>,
-            (?NS_PUBSUB_NODE_CONFIG)),
-     ?BOOL_CONFIG_FIELD(<<"Deliver payloads with event notifications">>,
-                       deliver_payloads),
-     ?BOOL_CONFIG_FIELD(<<"Deliver event notifications">>,
-                       deliver_notifications),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node configuratio"
-                         "n changes">>,
-                       notify_config),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when the node is "
-                         "deleted">>,
-                       notify_delete),
-     ?BOOL_CONFIG_FIELD(<<"Notify subscribers when items are removed "
-                         "from the node">>,
-                       notify_retract),
-     ?BOOL_CONFIG_FIELD(<<"Persist items to storage">>,
-                       persist_items),
-     ?STRING_CONFIG_FIELD(<<"A friendly name for the node">>,
-                         title),
-     ?INTEGER_CONFIG_FIELD(<<"Max # of items to persist">>,
-                          max_items),
-     ?BOOL_CONFIG_FIELD(<<"Whether to allow subscriptions">>,
-                       subscribe),
-     ?ALIST_CONFIG_FIELD(<<"Specify the access model">>,
-                        access_model,
-                        [open, authorize, presence, roster, whitelist]),
-     ?LISTM_CONFIG_FIELD(<<"Roster groups allowed to subscribe">>,
-                        roster_groups_allowed, Groups),
-     ?ALIST_CONFIG_FIELD(<<"Specify the publisher model">>,
-                        publish_model, [publishers, subscribers, open]),
-     ?BOOL_CONFIG_FIELD(<<"Purge all items when the relevant publisher "
-                         "goes offline">>,
-                       purge_offline),
-     ?ALIST_CONFIG_FIELD(<<"Specify the event message type">>,
-                        notification_type, [headline, normal]),
-     ?INTEGER_CONFIG_FIELD(<<"Max payload size in bytes">>,
-                          max_payload_size),
-     ?ALIST_CONFIG_FIELD(<<"When to send the last published item">>,
-                        send_last_published_item,
-                        [never, on_sub, on_sub_and_presence]),
-     ?BOOL_CONFIG_FIELD(<<"Only deliver notifications to available "
-                         "users">>,
-                       presence_based_delivery),
-     ?NLIST_CONFIG_FIELD(<<"The collections with which a node is "
-                          "affiliated">>,
-                        collection)].
-
-%%<p>There are several reasons why the node configuration request might fail:</p>
-%%<ul>
-%%<li>The service does not support node configuration.</li>
-%%<li>The requesting entity does not have sufficient privileges to configure the node.</li>
-%%<li>The request did not specify a node.</li>
-%%<li>The node has no configuration options.</li>
-%%<li>The specified node does not exist.</li>
-%%</ul>
-set_configure(Host, Node, From, Els, Lang) ->
-    case xml:remove_cdata(Els) of
-      [#xmlel{name = <<"x">>} = XEl] ->
-         case {xml:get_tag_attr_s(<<"xmlns">>, XEl),
-               xml:get_tag_attr_s(<<"type">>, XEl)}
-             of
-           {?NS_XDATA, <<"cancel">>} -> {result, []};
-           {?NS_XDATA, <<"submit">>} ->
-               Action = fun (#pubsub_node{options = Options,
-                                          type = Type, id = NodeId} =
-                                 N) ->
-                                case node_call(Type, get_affiliation,
-                                               [NodeId, From])
-                                    of
-                                  {result, owner} ->
-                                      case jlib:parse_xdata_submit(XEl) of
-                                        invalid -> {error, ?ERR_BAD_REQUEST};
-                                        XData ->
-                                            OldOpts = case Options of
-                                                        [] ->
-                                                            node_options(Type);
-                                                        _ -> Options
-                                                      end,
-                                            case set_xoption(Host, XData,
-                                                             OldOpts)
-                                                of
-                                              NewOpts
-                                                  when is_list(NewOpts) ->
-                                                  case tree_call(Host,
-                                                                 set_node,
-                                                                 [N#pubsub_node{options
-                                                                                    =
-                                                                                    NewOpts}])
-                                                      of
-                                                    ok -> {result, ok};
-                                                    Err -> Err
-                                                  end;
-                                              Err -> Err
-                                            end
-                                      end;
-                                  _ -> {error, ?ERR_FORBIDDEN}
-                                end
-                        end,
-               case transaction(Host, Node, Action, transaction) of
-                 {result, {TNode, ok}} ->
-                     NodeId = TNode#pubsub_node.id,
-                     Type = TNode#pubsub_node.type,
-                     Options = TNode#pubsub_node.options,
-                     broadcast_config_notification(Host, Node, NodeId, Type,
-                                                   Options, Lang),
-                     {result, []};
-                 Other -> Other
-               end;
-           _ -> {error, ?ERR_BAD_REQUEST}
-         end;
-      _ -> {error, ?ERR_BAD_REQUEST}
-    end.
-
-add_opt(Key, Value, Opts) ->
-    Opts1 = lists:keydelete(Key, 1, Opts),
-    [{Key, Value} | Opts1].
-
--define(SET_BOOL_XOPT(Opt, Val),
-       BoolVal = case Val of
-                   <<"0">> -> false;
-                   <<"1">> -> true;
-                   <<"false">> -> false;
-                   <<"true">> -> true;
-                   _ -> error
-                 end,
-       case BoolVal of
-         error -> {error, ?ERR_NOT_ACCEPTABLE};
-         _ ->
-             set_xoption(Host, Opts, add_opt(Opt, BoolVal, NewOpts))
-       end).
-
--define(SET_STRING_XOPT(Opt, Val),
-       set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
-
--define(SET_INTEGER_XOPT(Opt, Val, Min, Max),
-       case catch jlib:binary_to_integer(Val) of
-         IVal when is_integer(IVal), IVal >= Min, IVal =< Max ->
-             set_xoption(Host, Opts, add_opt(Opt, IVal, NewOpts));
-         _ -> {error, ?ERR_NOT_ACCEPTABLE}
-       end).
-
--define(SET_ALIST_XOPT(Opt, Val, Vals),
-       case lists:member(Val,
-                         [iolist_to_binary(atom_to_list(V)) || V <- Vals])
-           of
-         true ->
-             set_xoption(Host, Opts,
-                         add_opt(Opt, jlib:binary_to_atom(Val), NewOpts));
-         false -> {error, ?ERR_NOT_ACCEPTABLE}
-       end).
-
--define(SET_LIST_XOPT(Opt, Val),
-       set_xoption(Host, Opts, add_opt(Opt, Val, NewOpts))).
-
-set_xoption(_Host, [], NewOpts) -> NewOpts;
-set_xoption(Host, [{<<"FORM_TYPE">>, _} | Opts],
-           NewOpts) ->
-    set_xoption(Host, Opts, NewOpts);
-set_xoption(Host,
-           [{<<"pubsub#roster_groups_allowed">>, Value} | Opts],
-           NewOpts) ->
-    ?SET_LIST_XOPT(roster_groups_allowed, Value);
-set_xoption(Host,
-           [{<<"pubsub#deliver_payloads">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(deliver_payloads, Val);
-set_xoption(Host,
-           [{<<"pubsub#deliver_notifications">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(deliver_notifications, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_config">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(notify_config, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_delete">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(notify_delete, Val);
-set_xoption(Host,
-           [{<<"pubsub#notify_retract">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(notify_retract, Val);
-set_xoption(Host,
-           [{<<"pubsub#persist_items">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(persist_items, Val);
-set_xoption(Host,
-           [{<<"pubsub#max_items">>, [Val]} | Opts], NewOpts) ->
-    MaxItems = get_max_items_node(Host),
-    ?SET_INTEGER_XOPT(max_items, Val, 0, MaxItems);
-set_xoption(Host,
-           [{<<"pubsub#subscribe">>, [Val]} | Opts], NewOpts) ->
-    ?SET_BOOL_XOPT(subscribe, Val);
-set_xoption(Host,
-           [{<<"pubsub#access_model">>, [Val]} | Opts], NewOpts) ->
-    ?SET_ALIST_XOPT(access_model, Val,
-                   [open, authorize, presence, roster, whitelist]);
-set_xoption(Host,
-           [{<<"pubsub#publish_model">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(publish_model, Val,
-                   [publishers, subscribers, open]);
-set_xoption(Host,
-           [{<<"pubsub#notification_type">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(notification_type, Val,
-                   [headline, normal]);
-set_xoption(Host,
-           [{<<"pubsub#node_type">>, [Val]} | Opts], NewOpts) ->
-    ?SET_ALIST_XOPT(node_type, Val, [leaf, collection]);
-set_xoption(Host,
-           [{<<"pubsub#max_payload_size">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_INTEGER_XOPT(max_payload_size, Val, 0,
-                     (?MAX_PAYLOAD_SIZE));
-set_xoption(Host,
-           [{<<"pubsub#send_last_published_item">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_ALIST_XOPT(send_last_published_item, Val,
-                   [never, on_sub, on_sub_and_presence]);
-set_xoption(Host,
-           [{<<"pubsub#presence_based_delivery">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(presence_based_delivery, Val);
-set_xoption(Host,
-           [{<<"pubsub#purge_offline">>, [Val]} | Opts],
-           NewOpts) ->
-    ?SET_BOOL_XOPT(purge_offline, Val);
-set_xoption(Host, [{<<"pubsub#title">>, Value} | Opts],
-           NewOpts) ->
-    ?SET_STRING_XOPT(title, Value);
-set_xoption(Host, [{<<"pubsub#type">>, Value} | Opts],
-           NewOpts) ->
-    ?SET_STRING_XOPT(type, Value);
-set_xoption(Host,
-           [{<<"pubsub#body_xslt">>, Value} | Opts], NewOpts) ->
-    ?SET_STRING_XOPT(body_xslt, Value);
-set_xoption(Host,
-           [{<<"pubsub#collection">>, Value} | Opts], NewOpts) ->
-%    NewValue = [string_to_node(V) || V <- Value],
-    ?SET_LIST_XOPT(collection, Value);
-set_xoption(Host, [{<<"pubsub#node">>, [Value]} | Opts],
-           NewOpts) ->
-%    NewValue = string_to_node(Value),
-    ?SET_LIST_XOPT(node, Value);
-set_xoption(Host, [_ | Opts], NewOpts) ->
-    set_xoption(Host, Opts, NewOpts).
-
-get_max_items_node({_, ServerHost, _}) ->
-    get_max_items_node(ServerHost);
-get_max_items_node(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         max_items_node)
-       of
-      [{max_items_node, Integer}] -> Integer;
-      _ -> ?MAXITEMS
-    end.
-
-%%%% last item cache handling
-
-is_last_item_cache_enabled({_, ServerHost, _}) ->
-    is_last_item_cache_enabled(ServerHost);
-is_last_item_cache_enabled(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         last_item_cache)
-       of
-      [{last_item_cache, true}] -> true;
-      _ -> false
-    end.
-
-set_cached_item({_, ServerHost, _}, NodeId, ItemId,
-               Publisher, Payload) ->
-    set_cached_item(ServerHost, NodeId, ItemId, Publisher,
-                   Payload);
-set_cached_item(Host, NodeId, ItemId, Publisher,
-               Payload) ->
-    case is_last_item_cache_enabled(Host) of
-      true ->
-         mnesia:dirty_write({pubsub_last_item, NodeId, ItemId,
-                             {now(),
-                              jlib:jid_tolower(jlib:jid_remove_resource(Publisher))},
-                             Payload});
-      _ -> ok
-    end.
-
-unset_cached_item({_, ServerHost, _}, NodeId) ->
-    unset_cached_item(ServerHost, NodeId);
-unset_cached_item(Host, NodeId) ->
-    case is_last_item_cache_enabled(Host) of
-      true -> mnesia:dirty_delete({pubsub_last_item, NodeId});
-      _ -> ok
-    end.
-
--spec(get_cached_item/2 ::
-(
-  Host    :: mod_pubsub:host(),
-  NodeIdx :: mod_pubsub:nodeIdx())
-    -> undefined | mod_pubsub:pubsubItem()
-).
-get_cached_item({_, ServerHost, _}, NodeId) ->
-    get_cached_item(ServerHost, NodeId);
-get_cached_item(Host, NodeIdx) ->
-    case is_last_item_cache_enabled(Host) of
-      true ->
-         case mnesia:dirty_read({pubsub_last_item, NodeIdx}) of
-           [#pubsub_last_item{itemid = ItemId, creation = Creation, payload = Payload}] ->
-%          [{pubsub_last_item, NodeId, ItemId, Creation,
-%            Payload}] ->
-               #pubsub_item{itemid = {ItemId, NodeIdx},
-                            payload = Payload, creation = Creation,
-                            modification = Creation};
-           _ -> undefined
-         end;
-      _ -> undefined
-    end.
-
-%%%% plugin handling
-
-host(ServerHost) ->
-    case catch
-          ets:lookup(gen_mod:get_module_proc(ServerHost, config),
-                     host)
-       of
-      [{host, Host}] -> Host;
-      _ -> <<"pubsub.", ServerHost/binary>>
-    end.
-
-plugins(Host) ->
-    case catch ets:lookup(gen_mod:get_module_proc(Host,
-                                                 config),
-                         plugins)
-       of
-      [{plugins, []}] -> [?STDNODE];
-      [{plugins, PL}] -> PL;
-      _ -> [?STDNODE]
-    end.
-
-select_type(ServerHost, Host, Node, Type) ->
-    SelectedType = case Host of
-                    {_User, _Server, _Resource} ->
-                        case catch
-                               ets:lookup(gen_mod:get_module_proc(ServerHost,
-                                                                  config),
-                                          pep_mapping)
-                            of
-                          [{pep_mapping, PM}] ->
-                              proplists:get_value(Node, PM, ?PEPNODE);
-                          _ -> ?PEPNODE
-                        end;
-                    _ -> Type
-                  end,
-    ConfiguredTypes = plugins(ServerHost),
-    case lists:member(SelectedType, ConfiguredTypes) of
-      true -> SelectedType;
-      false -> hd(ConfiguredTypes)
-    end.
-
-select_type(ServerHost, Host, Node) ->
-    select_type(ServerHost, Host, Node,
-               hd(plugins(ServerHost))).
-
-features() ->
-    [% see plugin "access-authorize",   % OPTIONAL
-     <<"access-open">>,   % OPTIONAL this relates to access_model option in node_hometree
-     <<"access-presence">>,   % OPTIONAL this relates to access_model option in node_pep
-     <<"access-whitelist">>,   % OPTIONAL
-     <<"collections">>,   % RECOMMENDED
-     <<"config-node">>,   % RECOMMENDED
-     <<"create-and-configure">>,   % RECOMMENDED
-     <<"item-ids">>,   % RECOMMENDED
-     <<"last-published">>,   % RECOMMENDED
-     <<"member-affiliation">>,   % RECOMMENDED
-     <<"presence-notifications">>,   % OPTIONAL
-     <<"presence-subscribe">>,   % RECOMMENDED
-     <<"publisher-affiliation">>,   % RECOMMENDED
-     <<"retrieve-default">>].
-
-         % see plugin "retrieve-items",   % RECOMMENDED
-        % see plugin "retrieve-subscriptions",   % RECOMMENDED
-        %TODO "shim", % OPTIONAL
-        % see plugin "subscribe",   % REQUIRED
-        % see plugin "subscription-options",   % OPTIONAL
-        % see plugin "subscription-notifications"   % OPTIONAL
-
-features(Type) ->
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary,
-                               (?ODBC_SUFFIX)/binary>>),
-    features() ++
-      case catch Module:features() of
-       {'EXIT', {undef, _}} -> [];
-       Result -> Result
-      end.
-
-features(Host, <<>>) ->
-    lists:usort(lists:foldl(fun (Plugin, Acc) ->
-                                   Acc ++ features(Plugin)
-                           end,
-                           [], plugins(Host)));
-features(Host, Node) ->
-    Action = fun (#pubsub_node{type = Type}) ->
-                    {result, features(Type)}
-            end,
-    case transaction(Host, Node, Action, sync_dirty) of
-      {result, Features} ->
-         lists:usort(features() ++ Features);
-      _ -> features()
-    end.
-
-%% @spec (Host, Type, NodeId) -> [ljid()]
-%%    NodeId = pubsubNodeId()
-%% @doc <p>Return list of node owners.</p>
-node_owners(Host, Type, NodeId) ->
-    case node_action(Host, Type, get_node_affiliations, [NodeId]) of
-       {result, Affiliations} ->
-           lists:foldl(
-               fun({LJID, owner}, Acc) -> [LJID|Acc];
-                  (_, Acc) -> Acc
-           end, [], Affiliations);
-       _ ->
-           []
-    end.
-node_owners_call(Type, NodeId) ->
-    case node_call(Type, get_node_affiliations, [NodeId]) of
-       {result, Affiliations} ->
-           lists:foldl(
-               fun({LJID, owner}, Acc) -> [LJID|Acc];
-                  (_, Acc) -> Acc
-           end, [], Affiliations);
-       _ ->
-           []
-    end.
-
-%% @doc <p>node tree plugin call.</p>
-tree_call({_User, Server, _Resource}, Function, Args) ->
-    tree_call(Server, Function, Args);
-tree_call(Host, Function, Args) ->
-    ?DEBUG("tree_call ~p ~p ~p", [Host, Function, Args]),
-    Module = case catch
-                   ets:lookup(gen_mod:get_module_proc(Host, config),
-                              nodetree)
-                of
-              [{nodetree, N}] -> N;
-              _ ->
-                  jlib:binary_to_atom(<<(?TREE_PREFIX)/binary,
-                                          (?STDTREE)/binary,
-                                          (?ODBC_SUFFIX)/binary>>)
-            end,
-    catch apply(Module, Function, Args).
-
-tree_action(Host, Function, Args) ->
-    ?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]),
-    Fun = fun () -> tree_call(Host, Function, Args) end,
-    case catch ejabberd_odbc:sql_bloc(odbc_conn(Host), Fun) of
-    {atomic, Result} -> 
-       Result;
-    {aborted, Reason} -> 
-       ?ERROR_MSG("transaction return internal error: ~p~n",[{aborted, Reason}]),
-       {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-%% @doc <p>node plugin call.</p>
-node_call(Type, Function, Args) ->
-    ?DEBUG("node_call ~p ~p ~p", [Type, Function, Args]),
-    Module =
-       jlib:binary_to_atom(<<(?PLUGIN_PREFIX)/binary,
-                               Type/binary,
-                               (?ODBC_SUFFIX)/binary>>),
-    case apply(Module, Function, Args) of
-      {result, Result} -> {result, Result};
-      {error, Error} -> {error, Error};
-      {'EXIT', {undef, Undefined}} ->
-         case Type of
-           ?STDNODE -> {error, {undef, Undefined}};
-           _ -> node_call(?STDNODE, Function, Args)
-         end;
-      {'EXIT', Reason} -> {error, Reason};
-      Result ->
-         {result,
-          Result} %% any other return value is forced as result
-    end.
-
-node_action(Host, Type, Function, Args) ->
-    ?DEBUG("node_action ~p ~p ~p ~p",
-          [Host, Type, Function, Args]),
-    transaction(Host, fun () -> node_call(Type, Function, Args) end,
-               sync_dirty).
-
-%% @doc <p>plugin transaction handling.</p>
-transaction(Host, Node, Action, Trans) ->
-    transaction(Host, fun () ->
-                       case tree_call(Host, get_node, [Host, Node]) of
-                         N when is_record(N, pubsub_node) ->
-                             case Action(N) of
-                               {result, Result} -> {result, {N, Result}};
-                               {atomic, {result, Result}} ->
-                                   {result, {N, Result}};
-                               Other -> Other
-                             end;
-                         Error -> Error
-                       end
-               end,
-               Trans).
-
-transaction_on_nodes(Host, Action, Trans) ->
-    transaction(Host, fun () ->
-                       {result,
-                        lists:foldl(Action, [],
-                                    tree_call(Host, get_nodes, [Host]))}
-               end,
-               Trans).
-
-transaction(Host, Fun, Trans) ->
-    transaction_retry(Host, Fun, Trans, 2).
-transaction_retry(Host, Fun, Trans, Count) ->
-    SqlFun = case Trans of
-           transaction -> sql_transaction;
-           _ -> sql_bloc
-       end,
-    case catch ejabberd_odbc:SqlFun(odbc_conn(Host), Fun) of
-      {result, Result} -> {result, Result};
-      {error, Error} -> {error, Error};
-      {atomic, {result, Result}} -> {result, Result};
-      {atomic, {error, Error}} -> {error, Error};
-      {aborted, Reason} ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [{aborted, Reason}]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      {'EXIT', {timeout, _} = Reason} ->
-           case Count of
-               0 ->
-                   ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
-                   {error, ?ERR_INTERNAL_SERVER_ERROR};
-               N ->
-                   erlang:yield(),
-                   transaction_retry(Host, Fun, Trans, N-1)
-           end;
-      {'EXIT', Reason} ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [{'EXIT', Reason}]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      Other ->
-         ?ERROR_MSG("transaction return internal error: ~p~n",
-                    [Other]),
-         {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
-odbc_conn({_U, Host, _R})-> Host;
-odbc_conn(<<$., Host/binary>>) -> Host;
-odbc_conn(<<_, Host/binary>>) -> odbc_conn(Host).
-
-%% escape value for database storage
-escape({_U, _H, _R}=JID)->
-    ejabberd_odbc:escape(jlib:jid_to_string(JID));
-escape(Value)->
-    ejabberd_odbc:escape(Value).
-
-%%%% helpers
-
-%% Add pubsub-specific error element
-extended_error(Error, Ext) ->
-    extended_error(Error, Ext,
-                  [{<<"xmlns">>, ?NS_PUBSUB_ERRORS}]).
-
-extended_error(Error, unsupported, Feature) ->
-%% Give a uniq identifier
-    extended_error(Error, <<"unsupported">>,
-                  [{<<"xmlns">>, ?NS_PUBSUB_ERRORS},
-                   {<<"feature">>, Feature}]);
-extended_error(#xmlel{name = Error, attrs = Attrs,
-                     children = SubEls},
-              Ext, ExtAttrs) ->
-    #xmlel{name = Error, attrs = Attrs,
-          children =
-              lists:reverse([#xmlel{name = Ext, attrs = ExtAttrs,
-                                    children = []}
-                             | SubEls])}.
-
--spec(uniqid/0 :: () -> mod_pubsub:itemId()).
-uniqid() ->
-    {T1, T2, T3} = now(),
-    iolist_to_binary(io_lib:fwrite("~.16B~.16B~.16B", [T1, T2, T3])).
-
-nodeAttr(Node) -> [{<<"node">>, Node}].
-
-itemAttr([]) -> [];
-itemAttr(ItemId) -> [{<<"id">>, ItemId}].
-
-itemsEls(Items) ->
-    lists:map(fun (#pubsub_item{itemid = {ItemId, _}, payload = Payload}) ->
-               #xmlel{name = <<"item">>, attrs = itemAttr(ItemId), children = Payload}
-       end, Items).
-
--spec(add_message_type/2 ::
-(
-  Message :: xmlel(),
-  Type    :: atom())
-    -> xmlel()
-).
-
-add_message_type(Message, normal) -> Message;
-add_message_type(#xmlel{name = <<"message">>, attrs = Attrs, children = Els},
-  Type) ->
-    #xmlel{name = <<"message">>,
-           attrs = [{<<"type">>, jlib:atom_to_binary(Type)} | Attrs],
-           children = Els};
-add_message_type(XmlEl, _Type) -> XmlEl.
-
-%% Place of <headers/> changed at the bottom of the stanza
-%% cf. http://xmpp.org/extensions/xep-0060.html#publisher-publish-success-subid
-%%
-%% "[SHIM Headers] SHOULD be included after the event notification information
-%% (i.e., as the last child of the <message/> stanza)".
-
-add_shim_headers(Stanza, HeaderEls) ->
-    add_headers(Stanza, <<"headers">>, ?NS_SHIM, HeaderEls).
-
-add_extended_headers(Stanza, HeaderEls) ->
-    add_headers(Stanza, <<"addresses">>, ?NS_ADDRESS,
-               HeaderEls).
-
-add_headers(#xmlel{name = Name, attrs = Attrs, children = Els},
-       HeaderName, HeaderNS, HeaderEls) ->
-    HeaderEl = #xmlel{name = HeaderName,
-                     attrs = [{<<"xmlns">>, HeaderNS}],
-                     children = HeaderEls},
-    #xmlel{name = Name, attrs = Attrs,
-          children = lists:append(Els, [HeaderEl])}.
-
-%% Removed multiple <header name=Collection>Foo</header/> elements
-%% Didn't seem compliant, but not sure. Confirmation required.
-%% cf. http://xmpp.org/extensions/xep-0248.html#notify
-%%
-%% "If an item is published to a node which is also included by a collection,
-%%  and an entity is subscribed to that collection with a subscription type of
-%%  "items" (Is there a way to check that currently ?), then the notifications
-%%  generated by the service MUST contain additional information. The <items/>
-%%  element contained in the notification message MUST specify the node
-%%  identifier of the node that generated the notification (not the collection)
-%%  and the <item/> element MUST contain a SHIM header that specifies the node
-%%  identifier of the collection".
-
-collection_shim(Node) ->
-    [#xmlel{name = <<"header">>,
-           attrs = [{<<"name">>, <<"Collection">>}],
-           children = [{xmlcdata, Node}]}].
-
-subid_shim(SubIDs) ->
-    [#xmlel{name = <<"header">>,
-           attrs = [{<<"name">>, <<"SubID">>}],
-           children = [{xmlcdata, SubID}]}
-     || SubID <- SubIDs].
-
-%% The argument is a list of Jids because this function could be used
-%% with the 'pubsub#replyto' (type=jid-multi) node configuration.
-
-extended_headers(Jids) ->
-    [#xmlel{name = <<"address">>,
-           attrs = [{<<"type">>, <<"replyto">>}, {<<"jid">>, Jid}],
-           children = []}
-     || Jid <- Jids].
-
-on_user_offline(_, JID, _) ->
-    {User, Server, Resource} = jlib:jid_tolower(JID),
-    case ejabberd_sm:get_user_resources(User, Server) of
-       [] -> purge_offline({User, Server, Resource});
-       _  -> true
-    end.
-
-purge_offline({User, Server, _} = LJID) ->
-    Host = host(element(2, LJID)),
-    Plugins = plugins(Host),
-    Result = lists:foldl(fun (Type, {Status, Acc}) ->
-                                case lists:member(<<"retrieve-affiliations">>,
-                                                  features(Type))
-                                    of
-                                  false ->
-                                      {{error,
-                                        extended_error(?ERR_FEATURE_NOT_IMPLEMENTED,
-                                                       unsupported,
-                                                       <<"retrieve-affiliations">>)},
-                                       Acc};
-                                  true ->
-                                      {result, Affiliations} =
-                                          node_action(Host, Type,
-                                                      get_entity_affiliations,
-                                                      [Host, LJID]),
-                                      {Status, [Affiliations | Acc]}
-                                end
-                        end,
-                        {ok, []}, Plugins),
-    case Result of
-      {ok, Affiliations} ->
-         lists:foreach(fun ({#pubsub_node{nodeid = {_, NodeId},
-                                          options = Options, type = Type},
-                             Affiliation})
-                               when Affiliation == owner orelse
-                                      Affiliation == publisher ->
-                               Action = fun (#pubsub_node{type = NType,
-                                                          id = NodeIdx}) ->
-                                                node_call(NType, get_items,
-                                                          [NodeIdx,
-                                                           service_jid(Host)])
-                                        end,
-                               case transaction(Host, NodeId, Action,
-                                                sync_dirty)
-                                   of
-                                 {result, {_, []}} -> true;
-                                 {result, {_, Items}} ->
-                                     Features = features(Type),
-                                     case {lists:member(<<"retract-items">>,
-                                                        Features),
-                                           lists:member(<<"persistent-items">>,
-                                                        Features),
-                                           get_option(Options, persist_items),
-                                           get_option(Options, purge_offline)}
-                                         of
-                                       {true, true, true, true} ->
-                                           ForceNotify = get_option(Options,
-                                                                    notify_retract),
-                                           lists:foreach(fun
-                                                           (#pubsub_item{itemid
-                                                                             =
-                                                                             {ItemId,
-                                                                              _},
-                                                                         modification
-                                                                             =
-                                                                             {_,
-                                                                              Modification}}) ->
-                                                               case
-                                                                 Modification
-                                                                   of
-                                                                 {User, Server,
-                                                                  _} ->
-                                                                     delete_item(Host,
-                                                                                 NodeId,
-                                                                                 LJID,
-                                                                                 ItemId,
-                                                                                 ForceNotify);
-                                                                 _ -> true
-                                                               end;
-                                                           (_) -> true
-                                                         end,
-                                                         Items);
-                                       _ -> true
-                                     end;
-                                 Error -> Error
-                               end;
-                           (_) -> true
-                       end,
-                       lists:usort(lists:flatten(Affiliations)));
-      {Error, _} -> ?DEBUG("on_user_offline ~p", [Error])
-    end.
diff --git a/src/node.template b/src/node.template
deleted file mode 100644 (file)
index 89b4a31..0000000
+++ /dev/null
@@ -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).
index e7ab2799cca8bf5c0a522b58aad894a7d99f6983..a6ff0d4525b4f301ed36517960f1d702136fc32a 100644 (file)
@@ -4,58 +4,45 @@
 %%% 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>
+%%% @author Christophe Romain <christophe.romain@process-one.net>
 %%%   [http://www.process-one.net/]
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_buddy).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -64,121 +51,127 @@ 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, presence}, {roster_groups_allowed, []},
-     {publish_model, publishers},
-     {notification_type, headline},
-     {max_payload_size, ?MAX_PAYLOAD_SIZE},
-     {send_last_published_item, never},
-     {deliver_notifications, true},
-     {presence_based_delivery, false}].
+    [{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, presence},
+       {roster_groups_allowed, []},
+       {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() ->
-    [<<"create-nodes">>, <<"delete-nodes">>,
-     <<"delete-items">>, <<"instant-nodes">>, <<"item-ids">>,
-     <<"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).
+    [<<"create-nodes">>,
+       <<"delete-nodes">>,
+       <<"delete-items">>,
+       <<"instant-nodes">>,
+       <<"item-ids">>,
+       <<"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(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree:unsubscribe_node(NodeId, Sender,
-                                  Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree:publish_item(NodeId, Publisher, Model,
-                              MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree:remove_extra_items(NodeId, MaxItems,
-                                    ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree:delete_item(NodeId, Publisher,
-                             PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index f91723030a03886561932b9796e135bbb0f98869..6917312f2d4d095a5805034ff2c047d03705dc5a 100644 (file)
@@ -1,61 +1,48 @@
-%%% ====================================================================
+%% ====================================================================
 %%% ``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>
+%%% @author Christophe Romain <christophe.romain@process-one.net>
 %%%   [http://www.process-one.net/]
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_club).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -64,121 +51,126 @@ 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, authorize}, {roster_groups_allowed, []},
-     {publish_model, publishers},
-     {notification_type, headline},
-     {max_payload_size, ?MAX_PAYLOAD_SIZE},
-     {send_last_published_item, never},
-     {deliver_notifications, true},
-     {presence_based_delivery, false}].
+    [{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, authorize},
+       {roster_groups_allowed, []},
+       {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() ->
-    [<<"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).
+    [<<"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(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree:unsubscribe_node(NodeId, Sender,
-                                  Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree:publish_item(NodeId, Publisher, Model,
-                              MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree:remove_extra_items(NodeId, MaxItems,
-                                    ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree:delete_item(NodeId, Publisher,
-                             PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index 9a36a4c4a4c61f5d089f9d697a47846d0bbf3921..f0cbf17b8ba86990258275686c12e794fb6b0902 100644 (file)
@@ -5,39 +5,37 @@
 %%% 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.
 %%%
+%%%
 %%% @author Brian Cully <bjc@kublai.com>
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_dag).
-
+-behaviour(gen_pubsub_node).
 -author('bjc@kublai.com').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
-
-%% 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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -51,113 +49,108 @@ options() ->
 features() ->
     [<<"multi-collection">> | node_hometree:features()].
 
-create_node_permission(_Host, _ServerHost, _Node,
-                      _ParentNode, _Owner, _Access) ->
+create_node_permission(_Host, _ServerHost, _Node, _ParentNode, _Owner, _Access) ->
     {result, true}.
 
-create_node(NodeID, Owner) ->
-    node_hometree:create_node(NodeID, Owner).
+create_node(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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) ->
-    case nodetree_dag:get_node(NodeID) of
-      #pubsub_node{options = Options} ->
-         case find_opt(node_type, Options) of
-           collection ->
-               {error,
-                ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)};
-           _ ->
-               node_hometree:publish_item(NodeID, Publisher, Model,
-                                          MaxItems, ItemID, Payload)
-         end;
-      Err -> Err
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
+
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
+
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    case nodetree_dag:get_node(Nidx) of
+       #pubsub_node{options = Options} ->
+           case find_opt(node_type, Options) of
+               collection ->
+                   {error,
+                       ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"publish">>)};
+               _ ->
+                   node_hometree:publish_item(Nidx, Publisher, Model,
+                       MaxItems, ItemId, Payload)
+           end;
+       Err -> Err
     end.
 
 find_opt(_, []) -> false;
 find_opt(Option, [{Option, Value} | _]) -> Value;
 find_opt(Option, [_ | T]) -> find_opt(Option, T).
 
-remove_extra_items(NodeID, MaxItems, ItemIDs) ->
-    node_hometree:remove_extra_items(NodeID, MaxItems,
-                                    ItemIDs).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeID, Publisher, PublishModel, ItemID) ->
-    node_hometree:delete_item(NodeID, Publisher,
-                             PublishModel, ItemID).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeID, Owner) ->
-    node_hometree:purge_node(NodeID, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeID) ->
-    node_hometree:get_node_affiliations(NodeID).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeID, Owner) ->
-    node_hometree:get_affiliation(NodeID, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeID, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeID, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeID, Owner) ->
-    node_hometree:get_subscriptions(NodeID, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeID, Owner, Subscription, SubID) ->
-    node_hometree:set_subscriptions(NodeID, Owner,
-                                   Subscription, SubID).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeID, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeID, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeID, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubID) ->
-    node_hometree:get_items(NodeID, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubID).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeID, ItemID) ->
-    node_hometree:get_item(NodeID, ItemID).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeID, ItemID, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubID) ->
-    node_hometree:get_item(NodeID, ItemID, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubID).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
-    node_hometree:get_item_name(Host, Node, ID).
+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).
index 3bf20cc6303dc9c86c140b9c1886c27cc555b1ae..7a12318400ab1da2b55ee5286c317e3c6711c01c 100644 (file)
@@ -4,56 +4,51 @@
 %%% 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>
+%%% @author Christophe Romain <christophe.romain@process-one.net>
 %%%   [http://www.process-one.net/]
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_dispatch).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
-
 %%% @doc <p>The <strong>{@module}</strong> module is a PubSub plugin whose
 %%% 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
 %%% its children.</p>
 %%% This module can not work with virtual nodetree
 
-%% 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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -62,115 +57,129 @@ 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, never},
-     {deliver_notifications, true},
-     {presence_based_delivery, false}].
+    [{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, presence},
+       {roster_groups_allowed, []},
+       {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() ->
-    [<<"create-nodes">>, <<"delete-nodes">>,
-     <<"instant-nodes">>, <<"outcast-affiliation">>,
-     <<"persistent-items">>, <<"publish">>,
-     <<"retrieve-items">>].
+    [<<"create-nodes">>,
+       <<"delete-nodes">>,
+       <<"instant-nodes">>,
+       <<"outcast-affiliation">>,
+       <<"persistent-items">>,
+       <<"publish">>,
+       <<"retrieve-items">>].
 
-create_node_permission(Host, ServerHost, Node,
-                      ParentNode, Owner, Access) ->
-    node_hometree:create_node_permission(Host, ServerHost,
-                                        Node, ParentNode, Owner, Access).
+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).
+create_node(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, Owner).
 
-delete_node(Removed) ->
-    node_hometree:delete_node(Removed).
+delete_node(Nodes) ->
+    node_hometree:delete_node(Nodes).
 
-subscribe_node(_NodeId, _Sender, _Subscriber,
-              _AccessModel, _SendLast, _PresenceSubscription,
-              _RosterGroup, _Options) ->
+subscribe_node(_Nidx, _Sender, _Subscriber, _AccessModel, _SendLast, _PresenceSubscription,
+           _RosterGroup, _Options) ->
     {error, ?ERR_FORBIDDEN}.
 
-unsubscribe_node(_NodeId, _Sender, _Subscriber,
-                _SubID) ->
+unsubscribe_node(_Nidx, _Sender, _Subscriber, _SubId) ->
     {error, ?ERR_FORBIDDEN}.
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    lists:foreach(fun (SubNode) ->
-                         node_hometree:publish_item(SubNode#pubsub_node.id,
-                                                    Publisher, Model, MaxItems,
-                                                    ItemId, Payload)
-                 end,
-                 nodetree_tree:get_subnodes(NodeId, Publisher,
-                                            Publisher)).
-
-remove_extra_items(_NodeId, _MaxItems, ItemIds) ->
+publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
+    case nodetree_tree:get_node(Nidx) of
+       #pubsub_node{nodeid = {Host, Node}} ->
+           lists:foreach(fun (SubNode) ->
+                       node_hometree:publish_item(SubNode#pubsub_node.id,
+                           Publisher, PublishModel, MaxItems,
+                           ItemId, Payload)
+               end,
+               nodetree_tree:get_subnodes(Host, Node, Publisher)),
+           {result, {default, broadcast, []}};
+       Error ->
+           Error
+    end.
+
+remove_extra_items(_Nidx, _MaxItems, ItemIds) ->
     {result, {ItemIds, []}}.
 
-delete_item(_NodeId, _Publisher, _PublishModel,
-           _ItemId) ->
+delete_item(_Nidx, _Publisher, _PublishModel, _ItemId) ->
     {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) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, Owner, Affiliation).
 
-get_entity_subscriptions(_Host, _Owner) -> {result, []}.
+get_entity_subscriptions(_Host, _Owner) ->
+    {result, []}.
 
-get_node_subscriptions(NodeId) ->
-    node_hometree:get_node_subscriptions(NodeId).
+get_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(_NodeId, _Owner) -> {result, []}.
+get_subscriptions(_Nidx, _Owner) ->
+    {result, []}.
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index ff37f13e573eec0d04f383e25ea1af279027b028..e261543801c5eb3b176e47435a3e80616a60a51a 100644 (file)
@@ -4,13 +4,13 @@
 %%% 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
 %%% ====================================================================
 
 -module(node_flat).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
-
-%% 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, node_to_path/1,
-        path_to_node/1]).
-
-init(Host, ServerHost, Opts) ->
-    node_hometree:init(Host, ServerHost, Opts).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
+
+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) ->
     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() -> node_hometree:features().
+    node_hometree:options().
+
+features() ->
+    node_hometree:features().
 
 %% use same code as node_hometree, but do not limite node to
 %% the home/localhost/user/... hierarchy
 %% any node is allowed
-create_node_permission(Host, ServerHost, _Node,
-                      _ParentNode, Owner, Access) ->
+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,
+       {<<"">>, Host, <<"">>} ->
+           true; % pubsub service always allowed
+       _ ->
+           acl:match_rule(ServerHost, Access, LOwner) =:= allow
+    end,
     {result, Allowed}.
 
-create_node(NodeId, Owner) ->
-    node_hometree:create_node(NodeId, Owner).
+create_node(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber,
+       AccessModel, SendLast, PresenceSubscription,
+       RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree:unsubscribe_node(NodeId, Sender,
-                                  Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree:publish_item(NodeId, Publisher, Model,
-                              MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree:remove_extra_items(NodeId, MaxItems,
-                                    ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree:delete_item(NodeId, Publisher,
-                             PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     node_hometree:get_item_name(Host, Node, Id).
 
-node_to_path(Node) -> [(Node)].
+node_to_path(Node) ->
+    [(Node)].
 
 path_to_node(Path) ->
     case Path of
-      % default slot
-      [Node] -> iolist_to_binary(Node);
-      % handle old possible entries, used when migrating database content to new format
-      [Node | _] when is_binary(Node) ->
-         iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
-      % default case (used by PEP for example)
-      _ -> iolist_to_binary(Path)
+       % default slot
+       [Node] -> iolist_to_binary(Node);
+       % handle old possible entries, used when migrating database content to new format
+       [Node | _] when is_binary(Node) ->
+           iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
+       % default case (used by PEP for example)
+       _ -> iolist_to_binary(Path)
     end.
index 1baf38e71448624d138dbc8281b4c9603339eb3c..9ac2643f8c1e282437ec25bf0a4a5d47300204f8 100644 (file)
@@ -4,13 +4,13 @@
 %%% 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
 %%% ====================================================================
 
 -module(node_flat_odbc).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
-
-%% 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_entity_subscriptions_for_send_last/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/7, get_items/6,
-        get_items/3, get_items/2, get_item/7, get_item/2,
-        set_item/1, get_item_name/3, get_last_items/3,
-        node_to_path/1, path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1,
+    get_entity_subscriptions_for_send_last/2, get_last_items/3]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree_odbc:init(Host, ServerHost, Opts).
@@ -57,157 +52,115 @@ terminate(Host, ServerHost) ->
     node_hometree_odbc: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}, {odbc, true},
-     {rsm, true}].
-
-features() -> node_hometree_odbc:features().
+    [{odbc, true}, {rsm, true} | node_flat:options()].
+
+features() ->
+    [<<"rsm">> | node_flat:features()].
 
 %% use same code as node_hometree_odbc, but do not limite node to
 %% the home/localhost/user/... hierarchy
 %% any node is allowed
-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) ->
-    node_hometree_odbc:create_node(NodeId, Owner).
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+    node_flat:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+    node_hometree_odbc:create_node(Nidx, Owner).
 
 delete_node(Removed) ->
     node_hometree_odbc:delete_node(Removed).
 
-subscribe_node(NodeId, Sender, Subscriber, AccessModel,
-              SendLast, PresenceSubscription, RosterGroup, Options) ->
-    node_hometree_odbc:subscribe_node(NodeId, Sender,
-                                     Subscriber, AccessModel, SendLast,
-                                     PresenceSubscription, RosterGroup,
-                                     Options).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree_odbc:unsubscribe_node(NodeId, Sender,
-                                       Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree_odbc:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree_odbc:publish_item(NodeId, Publisher,
-                                   Model, MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree_odbc:remove_extra_items(NodeId, MaxItems,
-                                         ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree_odbc:delete_item(NodeId, Publisher,
-                                  PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree_odbc:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree_odbc:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree_odbc:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree_odbc:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree_odbc:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree_odbc:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree_odbc:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree_odbc:set_affiliation(NodeId, Owner,
-                                      Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
 
 get_entity_subscriptions(Host, Owner) ->
-    node_hometree_odbc:get_entity_subscriptions(Host,
-                                               Owner).
+    node_hometree_odbc:get_entity_subscriptions(Host, Owner).
 
 get_entity_subscriptions_for_send_last(Host, Owner) ->
-    node_hometree_odbc:get_entity_subscriptions_for_send_last(Host,
-                                                             Owner).
+    node_hometree_odbc:get_entity_subscriptions_for_send_last(Host, Owner).
 
-get_node_subscriptions(NodeId) ->
-    node_hometree_odbc:get_node_subscriptions(NodeId).
+get_node_subscriptions(Nidx) ->
+    node_hometree_odbc:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree_odbc:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree_odbc:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree_odbc:set_subscriptions(NodeId, Owner,
-                                        Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 get_pending_nodes(Host, Owner) ->
     node_hometree_odbc:get_pending_nodes(Host, Owner).
 
-get_states(NodeId) ->
-    node_hometree_odbc:get_states(NodeId).
-
-get_state(NodeId, JID) ->
-    node_hometree_odbc:get_state(NodeId, JID).
-
-set_state(State) -> node_hometree_odbc:set_state(State).
+get_states(Nidx) ->
+    node_hometree_odbc:get_states(Nidx).
 
-get_items(NodeId, From) ->
-    node_hometree_odbc:get_items(NodeId, From).
+get_state(Nidx, JID) ->
+    node_hometree_odbc:get_state(Nidx, JID).
 
-get_items(NodeId, From, RSM) ->
-    node_hometree_odbc:get_items(NodeId, From, RSM).
+set_state(State) ->
+    node_hometree_odbc:set_state(State).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    get_items(NodeId, JID, AccessModel,
-             PresenceSubscription, RosterGroup, SubId, none).
+get_items(Nidx, From, RSM) ->
+    node_hometree_odbc:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId, RSM) ->
-    node_hometree_odbc:get_items(NodeId, JID, AccessModel,
-                                PresenceSubscription, RosterGroup, SubId, RSM).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree_odbc:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree_odbc:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree_odbc:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree_odbc:get_item(NodeId, ItemId, JID,
-                               AccessModel, PresenceSubscription, RosterGroup,
-                               SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree_odbc:get_item(Nidx, ItemId, JID,
+       AccessModel, PresenceSubscription, RosterGroup, SubId).
 
-set_item(Item) -> node_hometree_odbc:set_item(Item).
+set_item(Item) ->
+    node_hometree_odbc:set_item(Item).
 
 get_item_name(Host, Node, Id) ->
     node_hometree_odbc:get_item_name(Host, Node, Id).
 
-get_last_items(NodeId, From, Count) ->
-    node_hometree_odbc:get_last_items(NodeId, From, Count).
+get_last_items(Nidx, 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) ->
     case Path of
-      % default slot
-      [Node] -> iolist_to_binary(Node);
-      % handle old possible entries, used when migrating database content to new format
-      [Node | _] when is_binary(Node) ->
-         iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
-      % default case (used by PEP for example)
-      _ -> iolist_to_binary(Path)
+       % default slot
+       [Node] -> iolist_to_binary(Node);
+       % handle old possible entries, used when migrating database content to new format
+       [Node | _] when is_binary(Node) ->
+           iolist_to_binary(str:join([<<"">> | Path], <<"/">>));
+       % default case (used by PEP for example)
+       _ -> iolist_to_binary(Path)
     end.
index 6f3c4de74720290bbc8a1b34a6664e9ca425573c..2840c1850b1498d20c841f081a5facdd7745b66a 100644 (file)
@@ -4,20 +4,19 @@
 %%% 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/]
 %%% improvements.</p>
 
 -module(node_hometree).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
-
-%% 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, node_to_path/1,
-        path_to_node/1]).
-
-%% ================
-%% 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) ->
+    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_items/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
+
+init(Host, ServerHost, _Opts) ->
     pubsub_subscription:init(),
     mnesia:create_table(pubsub_state,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, 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)}]),
+       [{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)
+       ItemsFields -> ok;
+       _ -> mnesia:transform_table(pubsub_item, ignore, ItemsFields)
     end,
+    Owner = mod_pubsub:service_jid(Host),
+    mod_pubsub:create_node(Host, ServerHost, <<"/home">>, Owner, <<"hometree">>),
+    mod_pubsub:create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, Owner, <<"hometree">>),
     ok.
 
-%% @spec (Host, ServerHost) -> ok
-%%      Host       = string()
-%%      ServerHost = string()
-%% @doc <p>Called during pubsub modules termination. Any pubsub plugin must
-%% implement this function. It can return anything.</p>
-terminate(_Host, _ServerHost) -> ok.
-
--spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
+terminate(_Host, _ServerHost) ->
+    ok.
 
-%% @spec () -> Options
-%%      Options = [mod_pubsub:nodeOption()]
-%% @doc Returns the default pubsub node options.
-%% <p>Example of function return value:</p>
-%%     ```
-%%      [{deliver_payloads, true},
-%%       {notify_config, false},
-%%       {notify_delete, false},
-%%       {notify_retract, true},
-%%       {persist_items, true},
-%%       {max_items, 10},
-%%       {subscribe, true},
-%%       {access_model, open},
-%%       {publish_model, publishers},
-%%       {max_payload_size, 100000},
-%%       {send_last_published_item, never},
-%%       {presence_based_delivery, false}]'''
 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}].
+    [{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}].
 
-%% @spec () -> Features
-%%      Features = [string()]
-%% @doc Returns the node features
--spec(features/0 :: () -> Features::[binary(),...]).
 features() ->
-    [<<"create-nodes">>, <<"auto-create">>,
-     <<"access-authorize">>, <<"delete-nodes">>,
-     <<"delete-items">>, <<"get-pending">>,
-     <<"instant-nodes">>, <<"manage-subscriptions">>,
-     <<"modify-affiliations">>, <<"multi-subscribe">>,
-     <<"outcast-affiliation">>, <<"persistent-items">>,
-     <<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
-     <<"retrieve-affiliations">>, <<"retrieve-items">>,
-     <<"retrieve-subscriptions">>, <<"subscribe">>,
-     <<"subscription-notifications">>,
-     <<"subscription-options">>].
+    [<<"create-nodes">>,
+       <<"auto-create">>,
+       <<"access-authorize">>,
+       <<"delete-nodes">>,
+       <<"delete-items">>,
+       <<"get-pending">>,
+       <<"instant-nodes">>,
+       <<"manage-subscriptions">>,
+       <<"modify-affiliations">>,
+       <<"multi-subscribe">>,
+       <<"outcast-affiliation">>,
+       <<"persistent-items">>,
+       <<"publish">>,
+       <<"purge-nodes">>,
+       <<"retract-items">>,
+       <<"retrieve-affiliations">>,
+       <<"retrieve-items">>,
+       <<"retrieve-subscriptions">>,
+       <<"subscribe">>,
+       <<"subscription-notifications">>,
+       <<"subscription-options">>].
 
-%% @spec (Host, ServerHost, NodeId, ParentNodeId, Owner, Access) -> {result, Allowed}
-%%      Host         = mod_pubsub:hostPubsub()
-%%      ServerHost   = string()
-%%      NodeId       = mod_pubsub:nodeId()
-%%      ParentNodeId = mod_pubsub:nodeId()
-%%      Owner        = mod_pubsub:jid()
-%%      Access       = all | atom()
-%%   Allowed      = boolean()
 %% @doc Checks if the current user has the permission to create the requested node
 %% <p>In {@link node_default}, the permission is decided by the place in the
 %% hierarchy where the user is creating the node. The access parameter is also
@@ -164,99 +130,44 @@ features() ->
 %% <tt>access_createnode</tt> ACL value in ejabberd config file.</p>
 %% <p>This function also check that node can be created a a children of its
 %% parent node</p>
-%% <p>PubSub plugins can redefine the PubSub node creation rights as they
-%% which. They can simply delegate this check to the {@link node_default}
-%% module by implementing this function like this:
-%% ```check_create_user_permission(Host, ServerHost, NodeId, ParentNodeId, Owner, Access) ->
-%%        node_default:check_create_user_permission(Host, ServerHost, NodeId, ParentNodeId, Owner, Access).'''</p>
--spec(create_node_permission/6 ::
-(
-  Host          :: mod_pubsub:host(),
-  ServerHost    :: binary(),
-  NodeId        :: mod_pubsub:nodeId(),
-  _ParentNodeId :: mod_pubsub:nodeId(),
-  Owner         :: jid(),
-  Access        :: atom())
-    -> {result, boolean()}
-).
-
-create_node_permission(Host, ServerHost, NodeId, _ParentNodeId, Owner, Access) ->
+create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
     LOwner = jlib:jid_tolower(Owner),
     {User, Server, _Resource} = LOwner,
     Allowed = case LOwner of
-               {<<"">>, Host, <<"">>} ->
-                   true; % pubsub service always allowed
-               _ ->
-                   case acl:match_rule(ServerHost, Access, LOwner) of
-                     allow ->
-                         case node_to_path(NodeId) of
-                           [<<"home">>, Server, User | _] -> true;
-                           _ -> false
-                         end;
-                     _ -> false
-                   end
-             end,
+       {<<"">>, Host, <<"">>} ->
+           true; % pubsub service always allowed
+       _ ->
+           case acl:match_rule(ServerHost, Access, LOwner) of
+               allow ->
+                   case node_to_path(Node) of
+                       [<<"home">>, Server, User | _] -> true;
+                       _ -> false
+                   end;
+               _ -> false
+           end
+    end,
     {result, Allowed}.
 
-%% @spec (NodeIdx, Owner) -> {result, {default, broadcast}} 
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      Owner   = mod_pubsub:jid()
-%% @doc <p></p>
--spec(create_node/2 :: 
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, {default, broadcast}}
-).
-
-create_node(NodeIdx, Owner) ->
+create_node(Nidx, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    set_state(#pubsub_state{stateid = {OwnerKey, NodeIdx}, affiliation = owner}),
+    set_state(#pubsub_state{stateid = {OwnerKey, Nidx},
+           affiliation = owner}),
     {result, {default, broadcast}}.
 
-%% @spec (Nodes) -> {result, {default, broadcast, Reply}}
-%%      Nodes = [mod_pubsub:pubsubNode()]
-%%      Reply = [{mod_pubsub:pubsubNode(),
-%%            [{mod_pubsub:ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]}]}]
-%% @doc <p>purge items of deleted nodes after effective deletion.</p>
--spec(delete_node/1 ::
-(
-  Nodes :: [mod_pubsub:pubsubNode(),...])
-    -> {result,
-        {default, broadcast,
-         [{mod_pubsub:pubsubNode(),
-           [{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]},...]},...]
-         }
-        }
-).
 delete_node(Nodes) ->
     Tr = fun (#pubsub_state{stateid = {J, _}, subscriptions = Ss}) ->
-                lists:map(fun (S) -> {J, S} end, Ss)
-       end,
-    Reply = lists:map(fun (#pubsub_node{id = NodeIdx} = PubsubNode) ->
-               {result, States} = get_states(NodeIdx),
-               lists:foreach(fun (#pubsub_state{stateid = {LJID, _}, items = Items}) ->
-                       del_items(NodeIdx, Items),
-                       del_state(NodeIdx, LJID)
-               end, States),
-               {PubsubNode, lists:flatmap(Tr, States)}
-       end, Nodes),
+           lists:map(fun (S) -> {J, S} end, Ss)
+    end,
+    Reply = lists:map(fun (#pubsub_node{id = Nidx} = PubsubNode) ->
+                   {result, States} = get_states(Nidx),
+                   lists:foreach(fun (#pubsub_state{stateid = {LJID, _}, items = Items}) ->
+                               del_items(Nidx, Items),
+                               del_state(Nidx, LJID)
+                       end, States),
+                   {PubsubNode, lists:flatmap(Tr, States)}
+           end, Nodes),
     {result, {default, broadcast, Reply}}.
 
-%% @spec (NodeIdx, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) -> {error, Reason} | {result, Result}
-%%      NodeIdx              = mod_pubsub:nodeIdx()
-%%      Sender               = mod_pubsub:jid()
-%%      Subscriber           = mod_pubsub:jid()
-%%      AccessModel          = mod_pubsub:accessModel()
-%%      SendLast             = atom()
-%%      PresenceSubscription = boolean()
-%%      RosterGroup          = boolean()
-%%      Options              = [mod_pubsub:nodeOption()]
-%%      Reason               = mod_pubsub:stanzaError()
-%%      Result               = {result, {default, subscribed, mod_pubsub:subId()}}
-%%                        | {result, {default, subscribed, mod_pubsub:subId(), send_last}}
-%%                        | {result, {default, pending,    mod_pubsub:subId()}}
-%%
 %% @doc <p>Accepts or rejects subcription requests on a PubSub node.</p>
 %% <p>The mechanism works as follow:
 %% <ul>
@@ -288,199 +199,138 @@ delete_node(Nodes) ->
 %%   to completly disable persistance.</li></ul>
 %% </p>
 %% <p>In the default plugin module, the record is unchanged.</p>
--spec(subscribe_node/8 ::
-(
-  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) ->
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
     SubKey = jlib:jid_tolower(Subscriber),
     GenKey = jlib:jid_remove_resource(SubKey),
-    Authorized =
-       jlib:jid_tolower(jlib:jid_remove_resource(Sender)) ==
-         GenKey,
-    GenState = get_state(NodeIdx, GenKey),
+    Authorized = jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == GenKey,
+    GenState = get_state(Nidx, GenKey),
     SubState = case SubKey of
-                GenKey -> GenState;
-                _ -> get_state(NodeIdx, SubKey)
-              end,
+       GenKey -> GenState;
+       _ -> get_state(Nidx, SubKey)
+    end,
     Affiliation = GenState#pubsub_state.affiliation,
     Subscriptions = SubState#pubsub_state.subscriptions,
-    Whitelisted = lists:member(Affiliation,
-                              [member, publisher, owner]),
-    PendingSubscription = lists:any(fun ({pending, _}) ->
-                                           true;
-                                       (_) -> false
-                                   end,
-                                   Subscriptions),
+    Whitelisted = lists:member(Affiliation, [member, publisher, owner]),
+    PendingSubscription = lists:any(fun
+               ({pending, _}) -> true;
+               (_) -> false
+           end,
+           Subscriptions),
     if not Authorized ->
-          {error,
-           ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)};
-       Affiliation == outcast -> {error, ?ERR_FORBIDDEN};
-       PendingSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"pending-subscription">>)};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       %%ForbiddenAnonymous ->
-       %%      % Requesting entity is anonymous
-       %%      {error, ?ERR_FORBIDDEN};
-       true ->
-               SubId = pubsub_subscription:add_subscription(Subscriber, NodeIdx, Options),
-               NewSub = case AccessModel of
-                           authorize -> pending;
-                           _ -> subscribed
-                         end,
-                   set_state(SubState#pubsub_state{subscriptions =
-                                                    [{NewSub, SubId} | Subscriptions]}),
-                   case {NewSub, SendLast} of
-                       {subscribed, never} ->
-                           {result, {default, subscribed, SubId}};
-                       {subscribed, _} ->
-                           {result, {default, subscribed, SubId, send_last}};
-                       {_, _} -> {result, {default, pending, SubId}}
-                   end
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       PendingSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"pending-subscription">>)};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       %%ForbiddenAnonymous ->
+       %%        % Requesting entity is anonymous
+       %%        {error, ?ERR_FORBIDDEN};
+       true ->
+           SubId = pubsub_subscription:add_subscription(Subscriber, Nidx, Options),
+           NewSub = case AccessModel of
+               authorize -> pending;
+               _ -> subscribed
+           end,
+           set_state(SubState#pubsub_state{subscriptions =
+                   [{NewSub, SubId} | Subscriptions]}),
+           case {NewSub, SendLast} of
+               {subscribed, never} ->
+                   {result, {default, subscribed, SubId}};
+               {subscribed, _} ->
+                   {result, {default, subscribed, SubId, send_last}};
+               {_, _} ->
+                   {result, {default, pending, SubId}}
+           end
     end.
 
-%% @spec (NodeIdx, Sender, Subscriber, SubId) ->       {error, Reason} | {result, default}
-%%      NodeIdx    = mod_pubsub:nodeIdx()
-%%      Sender     = mod_pubsub:jid()
-%%      Subscriber = mod_pubsub:jid()
-%%      SubId      = mod_pubsub:subId()
-%%      Reason     = mod_pubsub:stanzaError()
 %% @doc <p>Unsubscribe the <tt>Subscriber</tt> from the <tt>Node</tt>.</p>
--spec(unsubscribe_node/4 ::
-(
-  NodeIdx    :: mod_pubsub:nodeIdx(),
-  Sender     :: jid(),
-  Subscriber :: ljid(),
-  SubId      :: subId())
-    -> {result, default}
-     %
-     | {error, xmlel()}
-).
-
-unsubscribe_node(NodeIdx, Sender, Subscriber, SubId) ->
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
     SubKey = jlib:jid_tolower(Subscriber),
     GenKey = jlib:jid_remove_resource(SubKey),
-    Authorized =
-       jlib:jid_tolower(jlib:jid_remove_resource(Sender)) ==
-         GenKey,
-    GenState = get_state(NodeIdx, GenKey),
+    Authorized = jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == GenKey,
+    GenState = get_state(Nidx, GenKey),
     SubState = case SubKey of
-                GenKey -> GenState;
-                _ -> get_state(NodeIdx, SubKey)
-              end,
-    Subscriptions = lists:filter(fun ({_Sub, _SubId}) ->
-                                        true;
-                                    (_SubId) -> false
-                                end,
-                                SubState#pubsub_state.subscriptions),
+       GenKey -> GenState;
+       _ -> get_state(Nidx, SubKey)
+    end,
+    Subscriptions = lists:filter(fun
+               ({_Sub, _SubId}) -> true;
+               (_SubId) -> false
+           end,
+           SubState#pubsub_state.subscriptions),
     SubIdExists = case SubId of
-                   <<>> -> false;
-                   Binary when is_binary(Binary) -> true;
-                   _ -> false
-                 end,
+       <<>> -> false;
+       Binary when is_binary(Binary) -> true;
+       _ -> false
+    end,
     if
-      %% Requesting entity is prohibited from unsubscribing entity
-      not Authorized -> {error, ?ERR_FORBIDDEN};
-      %% Entity did not specify SubId
-      %%SubId == "", ?? ->
-      %%       {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-      %% Invalid subscription identifier
-      %%InvalidSubId ->
-      %%       {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-      %% Requesting entity is not a subscriber
-      Subscriptions == [] ->
-         {error,
-          ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL),
-                        <<"not-subscribed">>)};
-      %% Subid supplied, so use that.
-      SubIdExists ->
-         Sub = first_in_list(fun (S) ->
-                                     case S of
-                                       {_Sub, SubId} -> true;
-                                       _ -> false
-                                     end
-                             end,
-                             SubState#pubsub_state.subscriptions),
-         case Sub of
-           {value, S} ->
-               delete_subscriptions(SubKey, NodeIdx, [S], SubState),
-               {result, default};
-           false ->
-               {error,
-                ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL),
-                              <<"not-subscribed">>)}
-         end;
-      %% Asking to remove all subscriptions to the given node
-      SubId == all ->
-         delete_subscriptions(SubKey, NodeIdx, Subscriptions, SubState),
-         {result, default};
-      %% No subid supplied, but there's only one matching subscription
-      length(Subscriptions) == 1 ->
-         delete_subscriptions(SubKey, NodeIdx, Subscriptions, SubState),
-         {result, default};
-      %% No subid and more than one possible subscription match.
-      true ->
-         {error,
-          ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}
+       %% Requesting entity is prohibited from unsubscribing entity
+       not Authorized ->
+           {error, ?ERR_FORBIDDEN};
+       %% Entity did not specify SubId
+       %%SubId == "", ?? ->
+       %%        {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %% Invalid subscription identifier
+       %%InvalidSubId ->
+       %%        {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       %% Requesting entity is not a subscriber
+       Subscriptions == [] ->
+           {error,
+               ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)};
+       %% Subid supplied, so use that.
+       SubIdExists ->
+           Sub = first_in_list(fun
+                       ({_, S}) when S == SubId -> true;
+                       (_) -> false
+                   end,
+                   SubState#pubsub_state.subscriptions),
+           case Sub of
+               {value, S} ->
+                   delete_subscriptions(SubKey, Nidx, [S], SubState),
+                   {result, default};
+               false ->
+                   {error,
+                       ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)}
+           end;
+       %% Asking to remove all subscriptions to the given node
+       SubId == all ->
+           delete_subscriptions(SubKey, Nidx, Subscriptions, SubState),
+           {result, default};
+       %% No subid supplied, but there's only one matching subscription
+       length(Subscriptions) == 1 ->
+           delete_subscriptions(SubKey, Nidx, Subscriptions, SubState),
+           {result, default};
+       %% No subid and more than one possible subscription match.
+       true ->
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}
     end.
 
--spec(delete_subscriptions/4 ::
-(
-  SubKey        :: ljid(),
-  NodeIdx       :: mod_pubsub:nodeIdx(),
-  Subscriptions :: [{mod_pubsub:subscription(), mod_pubsub:subId()}],
-  SubState      :: mod_pubsub:pubsubState())
-    -> ok
-).
-delete_subscriptions(SubKey, NodeIdx, Subscriptions, SubState) ->
+delete_subscriptions(SubKey, Nidx, Subscriptions, SubState) ->
     NewSubs = lists:foldl(fun ({Subscription, SubId}, Acc) ->
-                       pubsub_subscription:delete_subscription(SubKey, NodeIdx, SubId),
-                       Acc -- [{Subscription, SubId}]
-       end, SubState#pubsub_state.subscriptions, Subscriptions),
+                   pubsub_subscription:delete_subscription(SubKey, Nidx, SubId),
+                   Acc -- [{Subscription, SubId}]
+           end, SubState#pubsub_state.subscriptions, Subscriptions),
     case {SubState#pubsub_state.affiliation, NewSubs} of
-      {none, []} -> del_state(NodeIdx, SubKey);
-      _          -> set_state(SubState#pubsub_state{subscriptions = NewSubs})
+       {none, []} -> del_state(Nidx, SubKey);
+       _          -> set_state(SubState#pubsub_state{subscriptions = NewSubs})
     end.
 
-%% @spec (NodeIdx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
-%%              {result, {default, broadcast, ItemIds}} | {error, Reason}
-%%      NodeIdx      = mod_pubsub:nodeIdx()
-%%      Publisher    = mod_pubsub:jid()
-%%      PublishModel = atom()
-%%      MaxItems     = integer()
-%%      ItemId       = mod_pubsub:itemId()
-%%      Payload      = mod_pubsub:payload()
-%%      ItemIds      = [mod_pubsub:itemId()] | []
-%%      Reason       = mod_pubsub:stanzaError()
 %% @doc <p>Publishes the item passed as parameter.</p>
 %% <p>The mechanism works as follow:
 %% <ul>
@@ -511,70 +361,48 @@ delete_subscriptions(SubKey, NodeIdx, Subscriptions, SubState) ->
 %%   to completly disable persistance.</li></ul>
 %% </p>
 %% <p>In the default plugin module, the record is unchanged.</p>
--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, xmlel()}
-).
-
-publish_item(NodeIdx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
+publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
     SubKey = jlib:jid_tolower(Publisher),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     SubState = case SubKey of
-                GenKey -> GenState;
-                _ -> get_state(NodeIdx, SubKey)
-              end,
+       GenKey -> GenState;
+       _ -> get_state(Nidx, SubKey)
+    end,
     Affiliation = GenState#pubsub_state.affiliation,
     Subscribed = case PublishModel of
-                  subscribers ->
-                      is_subscribed(SubState#pubsub_state.subscriptions);
-                  _ -> undefined
-                end,
-    if not
-        ((PublishModel == open) or
-           (PublishModel == publishers) and
-             ((Affiliation == owner) or (Affiliation == publisher))
-           or (Subscribed == true)) ->
-          {error, ?ERR_FORBIDDEN};
-       true ->
-          if MaxItems > 0 ->
-                 Now = now(),
-                 PubId = {Now, SubKey},
-                 Item = case get_item(NodeIdx, ItemId) of
-                          {result, OldItem} ->
-                              OldItem#pubsub_item{modification = PubId,
-                                                  payload = Payload};
-                          _ ->
-                              #pubsub_item{itemid = {ItemId, NodeIdx},
-                                           creation = {Now, GenKey},
-                                           modification = PubId,
-                                           payload = Payload}
-                        end,
-                 Items = [ItemId | GenState#pubsub_state.items --
-                                     [ItemId]],
-                 {result, {NI, OI}} = remove_extra_items(NodeIdx,
-                                                         MaxItems, Items),
-                 set_item(Item),
-                 set_state(GenState#pubsub_state{items = NI}),
-                 {result, {default, broadcast, OI}};
-             true -> {result, {default, broadcast, []}}
-          end
+       subscribers -> is_subscribed(SubState#pubsub_state.subscriptions);
+       _ -> undefined
+    end,
+    if not ((PublishModel == open) or
+                   (PublishModel == publishers) and
+                   ((Affiliation == owner) or (Affiliation == publisher))
+                   or (Subscribed == true)) ->
+           {error, ?ERR_FORBIDDEN};
+       true ->
+           if MaxItems > 0 ->
+                   Now = now(),
+                   PubId = {Now, SubKey},
+                   Item = case get_item(Nidx, ItemId) of
+                       {result, OldItem} ->
+                           OldItem#pubsub_item{modification = PubId,
+                               payload = Payload};
+                       _ ->
+                           #pubsub_item{itemid = {ItemId, Nidx},
+                               creation = {Now, GenKey},
+                               modification = PubId,
+                               payload = Payload}
+                   end,
+                   Items = [ItemId | GenState#pubsub_state.items -- [ItemId]],
+                   {result, {NI, OI}} = remove_extra_items(Nidx, MaxItems, Items),
+                   set_item(Item),
+                   set_state(GenState#pubsub_state{items = NI}),
+                   {result, {default, broadcast, OI}};
+               true ->
+                   {result, {default, broadcast, []}}
+           end
     end.
 
-%% @spec (NodeIdx, MaxItems, ItemIds) -> {result, {NewItemIds,OldItemIds}}
-%%      NodeIdx    = mod_pubsub:nodeIdx()
-%%      MaxItems   = integer() | unlimited
-%%      ItemIds    = [mod_pubsub:itemId()]
-%%      NewItemIds = [mod_pubsub:itemId()]
-%%      OldItemIds = [mod_pubsub:itemId()] | []
 %% @doc <p>This function is used to remove extra items, most notably when the
 %% maximum number of items has been reached.</p>
 %% <p>This function is used internally by the core PubSub module, as no
@@ -583,119 +411,84 @@ publish_item(NodeIdx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
 %% rules can be used.</p>
 %% <p>If another PubSub plugin wants to delegate the item removal (and if the
 %% plugin is using the default pubsub storage), it can implements this function like this:
-%% ```remove_extra_items(NodeIdx, MaxItems, ItemIds) ->
-%%        node_default:remove_extra_items(NodeIdx, MaxItems, ItemIds).'''</p>
--spec(remove_extra_items/3 ::
-(
-  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(_NodeIdx, unlimited, ItemIds) ->
+%% ```remove_extra_items(Nidx, MaxItems, ItemIds) ->
+%%           node_default:remove_extra_items(Nidx, MaxItems, ItemIds).'''</p>
+remove_extra_items(_Nidx, unlimited, ItemIds) ->
     {result, {ItemIds, []}};
-remove_extra_items(NodeIdx, MaxItems, ItemIds) ->
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
     NewItems = lists:sublist(ItemIds, MaxItems),
     OldItems = lists:nthtail(length(NewItems), ItemIds),
-    del_items(NodeIdx, OldItems),
+    del_items(Nidx, OldItems),
     {result, {NewItems, OldItems}}.
 
-%% @spec (NodeIdx, Publisher, PublishModel, ItemId) ->
-%%     {result, {default, broadcast}} | {error, Reason}
-%%      NodeIdx      = mod_pubsub:nodeIdx()
-%%      Publisher    = mod_pubsub:jid()
-%%      PublishModel = atom()
-%%      ItemId       = mod_pubsub:itemId()
-%%   Reason       = mod_pubsub:stanzaError()
 %% @doc <p>Triggers item deletion.</p>
 %% <p>Default plugin: The user performing the deletion must be the node owner
 %% or a publisher, or PublishModel being open.</p>
--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) ->
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
     SubKey = jlib:jid_tolower(Publisher),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     #pubsub_state{affiliation = Affiliation, items = Items} = GenState,
     Allowed = Affiliation == publisher orelse
-               Affiliation == owner orelse
-                 PublishModel == open orelse
-                   case get_item(NodeIdx, ItemId) of
-                     {result, #pubsub_item{creation = {_, GenKey}}} -> true;
-                     _ -> false
-                   end,
-    if not Allowed -> {error, ?ERR_FORBIDDEN};
-       true ->
-          case lists:member(ItemId, Items) of
-            true ->
-                del_item(NodeIdx, ItemId),
-                set_state(GenState#pubsub_state{items = lists:delete(ItemId, Items)}),
-                {result, {default, broadcast}};
-            false ->
-                case Affiliation of
-                  owner ->
-                      {result, States} = get_states(NodeIdx),
-                      lists:foldl(fun (#pubsub_state{items = PI} = S, Res) ->
-                                          case lists:member(ItemId, PI) of
-                                            true ->
-                                                del_item(NodeIdx, ItemId),
-                                                set_state(S#pubsub_state{items
-                                                                             = lists:delete(ItemId, PI)}),
-                                                {result, {default, broadcast}};
-                                            false -> Res
-                                          end;
-                                      (_, Res) -> Res
-                                  end,
-                                  {error, ?ERR_ITEM_NOT_FOUND}, States);
-                  _ -> {error, ?ERR_ITEM_NOT_FOUND}
-                end
-          end
+       Affiliation == owner orelse
+       PublishModel == open orelse
+       case get_item(Nidx, ItemId) of
+       {result, #pubsub_item{creation = {_, GenKey}}} -> true;
+       _ -> false
+    end,
+    if not Allowed ->
+           {error, ?ERR_FORBIDDEN};
+       true ->
+           case lists:member(ItemId, Items) of
+               true ->
+                   del_item(Nidx, ItemId),
+                   set_state(GenState#pubsub_state{items = lists:delete(ItemId, Items)}),
+                   {result, {default, broadcast}};
+               false ->
+                   case Affiliation of
+                       owner ->
+                           {result, States} = get_states(Nidx),
+                           lists:foldl(fun
+                                   (#pubsub_state{items = PI} = S, Res) ->
+                                       case lists:member(ItemId, PI) of
+                                           true ->
+                                               Nitems = lists:delete(ItemId, PI),
+                                               del_item(Nidx, ItemId),
+                                               set_state(S#pubsub_state{items = Nitems}),
+                                               {result, {default, broadcast}};
+                                           false ->
+                                               Res
+                                       end;
+                                   (_, Res) ->
+                                       Res
+                               end,
+                               {error, ?ERR_ITEM_NOT_FOUND}, States);
+                       _ ->
+                           {error, ?ERR_ITEM_NOT_FOUND}
+                   end
+           end
     end.
 
-%% @spec (NodeIdx, Owner) -> {error, Reason} | {result, {default, broadcast}}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      Owner   = mod_pubsub:jid()
-%%   Reason  = mod_pubsub:stanzaError()
--spec(purge_node/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, {default, broadcast}}
-     | {error, xmlel()}
-).
-
-purge_node(NodeIdx, Owner) ->
+purge_node(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     case GenState of
-      #pubsub_state{affiliation = owner} ->
-         {result, States} = get_states(NodeIdx),
-         lists:foreach(fun (#pubsub_state{items = []}) -> ok;
-                           (#pubsub_state{items = Items} = S) ->
-                               del_items(NodeIdx, Items),
-                               set_state(S#pubsub_state{items = []})
-                       end,
-                       States),
-         {result, {default, broadcast}};
-      _ -> {error, ?ERR_FORBIDDEN}
+       #pubsub_state{affiliation = owner} ->
+           {result, States} = get_states(Nidx),
+           lists:foreach(fun
+                   (#pubsub_state{items = []}) ->
+                       ok;
+                   (#pubsub_state{items = Items} = S) ->
+                       del_items(Nidx, Items),
+                       set_state(S#pubsub_state{items = []})
+               end,
+               States),
+           {result, {default, broadcast}};
+       _ ->
+           {error, ?ERR_FORBIDDEN}
     end.
 
-%% @spec (Host, Owner) -> {result, Reply}
-%%      Host  = mod_pubsub:hostPubsub()
-%%      Owner = mod_pubsub:jid()
-%%      Reply = [] | [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]
 %% @doc <p>Return the current affiliations for the given user</p>
 %% <p>The default module reads affiliations in the main Mnesia
 %% <tt>pubsub_state</tt> table. If a plugin stores its data in the same
@@ -703,88 +496,40 @@ purge_node(NodeIdx, Owner) ->
 %% the default PubSub module. Otherwise, it should return its own affiliation,
 %% that will be added to the affiliation stored in the main
 %% <tt>pubsub_state</tt> table.</p>
--spec(get_entity_affiliations/2 ::
-(
-  Host  :: mod_pubsub:host(),
-  Owner :: jid())
-    -> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
-).
-
 get_entity_affiliations(Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
     States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(Host, config),
-                                nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree
-              end,
-    Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A},
-                            Acc) ->
-                               case NodeTree:get_node(N) of
-                                 #pubsub_node{nodeid = {Host, _}} = Node ->
-                                     [{Node, A} | Acc];
-                                 _ -> Acc
-                               end
-                       end,
-                       [], States),
+    NodeTree = mod_pubsub:tree(Host),
+    Reply = lists:foldl(fun (#pubsub_state{stateid = {_, N}, affiliation = A}, Acc) ->
+                   case NodeTree:get_node(N) of
+                       #pubsub_node{nodeid = {Host, _}} = Node -> [{Node, A} | Acc];
+                       _ -> Acc
+                   end
+           end,
+           [], States),
     {result, Reply}.
 
--spec(get_node_affiliations/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [{ljid(), mod_pubsub:affiliation()}]}
-).
-
-get_node_affiliations(NodeIdx) ->
-    {result, States} = get_states(NodeIdx),
-    Tr = fun (#pubsub_state{stateid = {J, _},
-                           affiliation = A}) ->
-                {J, A}
-        end,
+get_node_affiliations(Nidx) ->
+    {result, States} = get_states(Nidx),
+    Tr = fun (#pubsub_state{stateid = {J, _}, affiliation = A}) -> {J, A} end,
     {result, lists:map(Tr, States)}.
 
--spec(get_affiliation/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, mod_pubsub:affiliation()}
-).
-
-get_affiliation(NodeIdx, Owner) ->
+get_affiliation(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    #pubsub_state{affiliation = Affiliation} = get_state(NodeIdx, GenKey),
+    #pubsub_state{affiliation = Affiliation} = get_state(Nidx, GenKey),
     {result, Affiliation}.
 
--spec(set_affiliation/3 ::
-(
-  NodeIdx     :: mod_pubsub:nodeIdx(),
-  Owner       :: ljid(),
-  Affiliation :: mod_pubsub:affiliation())
-    -> ok
-).
-set_affiliation(NodeIdx, Owner, Affiliation) ->
+set_affiliation(Nidx, Owner, Affiliation) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     case {Affiliation, GenState#pubsub_state.subscriptions} of
-      {none, []} -> del_state(NodeIdx, GenKey);
-      _ -> set_state(GenState#pubsub_state{affiliation = Affiliation})
+       {none, []} -> del_state(Nidx, GenKey);
+       _ -> set_state(GenState#pubsub_state{affiliation = Affiliation})
     end.
 
-%% @spec (Host, Owner) ->
-%%      {'result', []
-%%               | [{Node, Subscription, SubId, Entity}]
-%%               | [{Node, Subscription, Entity}]}
-%%      Host         = mod_pubsub:hostPubsub()
-%%      Owner        = mod_pubsub:jid()
-%%      Node         = mod_pubsub:pubsubNode()
-%%      Subscription = mod_pubsub:subscription()
-%%      SubId        = mod_pubsub:subId()
-%%      Entity       = mod_pubsub:ljid()
 %% @doc <p>Return the current subscriptions for the given user</p>
 %% <p>The default module reads subscriptions in the main Mnesia
 %% <tt>pubsub_state</tt> table. If a plugin stores its data in the same
@@ -792,130 +537,79 @@ set_affiliation(NodeIdx, Owner, Affiliation) ->
 %% the default PubSub module. Otherwise, it should return its own affiliation,
 %% that will be added to the affiliation stored in the main
 %% <tt>pubsub_state</tt> table.</p>
--spec(get_entity_subscriptions/2 ::
-(
-  Host :: mod_pubsub:host(),
-  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),
     GenKey = jlib:jid_remove_resource(SubKey),
     States = case SubKey of
-              GenKey ->
-                  mnesia:match_object(#pubsub_state{stateid =
-                                                        {{U, D, '_'}, '_'},
-                                                    _ = '_'});
-              _ ->
-                  mnesia:match_object(#pubsub_state{stateid =
-                                                        {GenKey, '_'},
-                                                    _ = '_'})
-                    ++
-                    mnesia:match_object(#pubsub_state{stateid =
-                                                          {SubKey, '_'},
-                                                      _ = '_'})
-            end,
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(Host, config),
-                                nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree
-              end,
-    Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss},
-                            Acc) ->
-                               case NodeTree:get_node(N) of
-                                 #pubsub_node{nodeid = {Host, _}} = Node ->
-                                     lists:foldl(fun ({Sub, SubId}, Acc2) ->
-                                                         [{Node, Sub, SubId, J} | Acc2]
-                                                 end,
-                                                 Acc, Ss);
-                                 _ -> Acc
-                               end
-                       end,
-                       [], States),
+       GenKey ->
+           mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'});
+       _ ->
+           mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'})
+           ++
+           mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'})
+    end,
+    NodeTree = mod_pubsub:tree(Host),
+    Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
+                   case NodeTree:get_node(N) of
+                       #pubsub_node{nodeid = {Host, _}} = Node ->
+                           lists:foldl(fun ({Sub, SubId}, Acc2) ->
+                                       [{Node, Sub, SubId, J} | Acc2]
+                               end,
+                               Acc, Ss);
+                       _ ->
+                           Acc
+                   end
+           end,
+           [], States),
     {result, Reply}.
 
--spec(get_node_subscriptions/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result,
-        [{ljid(), mod_pubsub:subscription(), mod_pubsub:subId()}] |
-        [{ljid(), none},...]
-       }
-).
-get_node_subscriptions(NodeIdx) ->
-    {result, States} = get_states(NodeIdx),
-    Tr = fun (#pubsub_state{stateid = {J, _},
-                           subscriptions = Subscriptions}) ->
-                case Subscriptions of
-                  [_ | _] ->
-                      lists:foldl(fun ({S, SubId}, Acc) ->
-                                          [{J, S, SubId} | Acc]
-                                  end,
-                                  [], Subscriptions);
-                  [] -> [];
-                  _ -> [{J, none}]
-                end
-        end,
+get_node_subscriptions(Nidx) ->
+    {result, States} = get_states(Nidx),
+    Tr = fun (#pubsub_state{stateid = {J, _}, subscriptions = Subscriptions}) ->
+           case Subscriptions of
+               [_ | _] ->
+                   lists:foldl(fun ({S, SubId}, Acc) ->
+                               [{J, S, SubId} | Acc]
+                       end,
+                       [], Subscriptions);
+               [] ->
+                   [];
+               _ ->
+                   [{J, none}]
+           end
+    end,
     {result, lists:flatmap(Tr, States)}.
 
--spec(get_subscriptions/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: ljid())
-    -> {result, [{mod_pubsub:subscription(), mod_pubsub:subId()}]}
-).
-get_subscriptions(NodeIdx, Owner) ->
+get_subscriptions(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
-    SubState = get_state(NodeIdx, SubKey),
+    SubState = get_state(Nidx, SubKey),
     {result, SubState#pubsub_state.subscriptions}.
 
--spec(set_subscriptions/4 ::
-(
-  NodeIdx      :: mod_pubsub:nodeIdx(),
-  Owner        :: jid(),
-  Subscription :: mod_pubsub:subscription(),
-  SubId        :: mod_pubsub:subId())
-    -> ok
-    %%%
-    | {error, xmlel()}
-).
-
-set_subscriptions(NodeIdx, Owner, Subscription, SubId) ->
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
     SubKey = jlib:jid_tolower(Owner),
-    SubState = get_state(NodeIdx, SubKey),
+    SubState = get_state(Nidx, SubKey),
     case {SubId, SubState#pubsub_state.subscriptions} of
-      {_, []} ->
-         case Subscription of
-           none ->
-               {error,
-                ?ERR_EXTENDED((?ERR_BAD_REQUEST),
-                              <<"not-subscribed">>)};
-           _ ->
-               new_subscription(NodeIdx, Owner, Subscription, SubState)
-         end;
-      {<<>>, [{_, SID}]} ->
-         case Subscription of
-           none -> unsub_with_subid(NodeIdx, SID, SubState);
-           _ -> replace_subscription({Subscription, SID}, SubState)
-         end;
-      {<<>>, [_ | _]} ->
-         {error,
-          ?ERR_EXTENDED((?ERR_BAD_REQUEST),
-                        <<"subid-required">>)};
-      _ ->
-         case Subscription of
-           none -> unsub_with_subid(NodeIdx, SubId, SubState);
-           _ ->
-               replace_subscription({Subscription, SubId}, SubState)
-         end
+       {_, []} ->
+           case Subscription of
+               none ->
+                   {error,
+                       ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"not-subscribed">>)};
+               _ ->
+                   new_subscription(Nidx, Owner, Subscription, SubState)
+           end;
+       {<<>>, [{_, SID}]} ->
+           case Subscription of
+               none -> unsub_with_subid(Nidx, SID, SubState);
+               _ -> replace_subscription({Subscription, SID}, SubState)
+           end;
+       {<<>>, [_ | _]} ->
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)};
+       _ ->
+           case Subscription of
+               none -> unsub_with_subid(Nidx, SubId, SubState);
+               _ -> replace_subscription({Subscription, SubId}, SubState)
+           end
     end.
 
 replace_subscription(NewSub, SubState) ->
@@ -923,103 +617,64 @@ replace_subscription(NewSub, SubState) ->
     set_state(SubState#pubsub_state{subscriptions = NewSubs}).
 
 replace_subscription(_, [], Acc) -> Acc;
-replace_subscription({Sub, SubId}, [{_, SubID} | T], Acc) ->
-    replace_subscription({Sub, SubId}, T, [{Sub, SubID} | Acc]).
-
-new_subscription(NodeId, Owner, Subscription, SubState) ->
-    SubId = pubsub_subscription:add_subscription(Owner, NodeId, []),
-    Subscriptions = SubState#pubsub_state.subscriptions,
-    set_state(SubState#pubsub_state{subscriptions =
-                                       [{Subscription, SubId} | Subscriptions]}),
-    {Subscription, SubId}.
-
--spec(unsub_with_subid/3 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  SubId   :: mod_pubsub:subId(),
-  SubState :: mod_pubsub:pubsubState())
-    -> ok
-).
-unsub_with_subid(NodeIdx, SubId, #pubsub_state{stateid = {Entity, _}} = SubState) ->
-    pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid,
-                                           NodeIdx, SubId),
-    NewSubs = lists:filter(fun ({_, SID}) -> SubId =/= SID
-                          end,
-                          SubState#pubsub_state.subscriptions),
+replace_subscription({Sub, SubId}, [{_, SubId} | T], Acc) ->
+    replace_subscription({Sub, SubId}, T, [{Sub, SubId} | Acc]).
+
+new_subscription(Nidx, Owner, Sub, SubState) ->
+    SubId = pubsub_subscription:add_subscription(Owner, Nidx, []),
+    Subs = SubState#pubsub_state.subscriptions,
+    set_state(SubState#pubsub_state{subscriptions = [{Sub, SubId} | Subs]}),
+    {Sub, SubId}.
+
+unsub_with_subid(Nidx, SubId, #pubsub_state{stateid = {Entity, _}} = SubState) ->
+    pubsub_subscription:delete_subscription(SubState#pubsub_state.stateid, Nidx, SubId),
+    NewSubs = [{S, Sid}
+           || {S, Sid} <- SubState#pubsub_state.subscriptions,
+               SubId =/= Sid],
     case {NewSubs, SubState#pubsub_state.affiliation} of
-      {[], none} ->
-           del_state(NodeIdx, Entity);
-      _ ->
-         set_state(SubState#pubsub_state{subscriptions = NewSubs})
+       {[], none} -> del_state(Nidx, Entity);
+       _ -> set_state(SubState#pubsub_state{subscriptions = NewSubs})
     end.
 
-%% TODO : doc
-%% @spec (Host, Owner) -> {result, Reply} | {error, Reason} 
-%%      Host  = mod_pubsub:hostPubsub()
-%%   Owner = mod_pubsub:jid()
-%%   Reply = [] | [mod_pubsub:nodeId()]
 %% @doc <p>Returns a list of Owner's nodes on Host with pending
 %% subscriptions.</p>
--spec(get_pending_nodes/2 ::
-(
-  Host  :: mod_pubsub:host(),
-  Owner :: jid())
-    -> {result, [mod_pubsub:nodeId()]}
-).
-
 get_pending_nodes(Host, Owner) ->
     GenKey = jlib:jid_remove_resource(jlib:jid_tolower(Owner)),
-    States = mnesia:match_object(#pubsub_state{stateid =
-                                                  {GenKey, '_'},
-                                              affiliation = owner, _ = '_'}),
-    NodeIDs = [ID
-              || #pubsub_state{stateid = {_, ID}} <- States],
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(Host, config),
-                                nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree
-              end,
-    Reply = mnesia:foldl(fun (#pubsub_state{stateid = {_, NID}} = S,
-                             Acc) ->
-                                case lists:member(NID, NodeIDs) of
-                                  true ->
-                                      case get_nodes_helper(NodeTree, S) of
-                                        {value, Node} -> [Node | Acc];
-                                        false -> Acc
-                                      end;
-                                  false -> Acc
-                                end
-                        end,
-                        [], pubsub_state),
+    States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'},
+               affiliation = owner,
+               _ = '_'}),
+    NodeIdxs = [Nidx || #pubsub_state{stateid = {_, Nidx}} <- States],
+    NodeTree = mod_pubsub:tree(Host),
+    Reply = mnesia:foldl(fun (#pubsub_state{stateid = {_, Nidx}} = S, Acc) ->
+                   case lists:member(Nidx, NodeIdxs) of
+                       true ->
+                           case get_nodes_helper(NodeTree, S) of
+                               {value, Node} -> [Node | Acc];
+                               false -> Acc
+                           end;
+                       false ->
+                           Acc
+                   end
+           end,
+           [], pubsub_state),
     {result, Reply}.
 
--spec(get_nodes_helper/2 ::
-(
-  NodeTree     :: module(),
-  Pubsub_State :: mod_pubsub:pubsubState())
-    -> {value, NodeId::mod_pubsub:nodeId()}
-     | false
-    
-).
 get_nodes_helper(NodeTree, #pubsub_state{stateid = {_, N}, subscriptions = Subs}) ->
-    HasPending = fun ({pending, _}) -> true;
-                    (pending) -> true;
-                    (_) -> false
-                end,
+    HasPending = fun
+       ({pending, _}) -> true;
+       (pending) -> true;
+       (_) -> false
+    end,
     case lists:any(HasPending, Subs) of
-      true ->
-         case NodeTree:get_node(N) of
-           #pubsub_node{nodeid = {_, Node}} -> {value, Node};
-           _ -> false
-         end;
-      false -> false
+       true ->
+           case NodeTree:get_node(N) of
+               #pubsub_node{nodeid = {_, Node}} -> {value, Node};
+               _ -> false
+           end;
+       false ->
+           false
     end.
 
-%% @spec (NodeIdx) -> {result, States}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      States  = [] | [mod_pubsub:pubsubState()]
 %% @doc Returns the list of stored states for a given node.
 %% <p>For the default PubSub module, states are stored in Mnesia database.</p>
 %% <p>We can consider that the pubsub_state table have been created by the main
@@ -1028,72 +683,33 @@ get_nodes_helper(NodeTree, #pubsub_state{stateid = {_, N}, subscriptions = Subs}
 %% relational database).</p>
 %% <p>If a PubSub plugin wants to delegate the states storage to the default node,
 %% they can implement this function like this:
-%% ```get_states(NodeIdx) ->
-%%        node_default:get_states(NodeIdx).'''</p>
--spec(get_states/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [mod_pubsub:pubsubState()]}
-).
-
-get_states(NodeIdx) ->
+%% ```get_states(Nidx) ->
+%%           node_default:get_states(Nidx).'''</p>
+get_states(Nidx) ->
     States = case catch mnesia:match_object(
-              #pubsub_state{stateid = {'_', NodeIdx}, _ = '_'}) of
+           #pubsub_state{stateid = {'_', Nidx}, _ = '_'}) of
        List when is_list(List) -> List;
        _ -> []
     end,
     {result, States}.
 
-%% @spec (NodeIdx, JID) -> State
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      JID     = mod_pubsub:jid()
-%%      State   = mod_pubsub:pubsubState()
 %% @doc <p>Returns a state (one state list), given its reference.</p>
--spec(get_state/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  JID     :: ljid())
-    -> mod_pubsub:pubsubState()
-).
-
-get_state(NodeIdx, JID) ->
-    StateId = {JID, NodeIdx},
+get_state(Nidx, Key) ->
+    StateId = {Key, Nidx},
     case catch mnesia:read({pubsub_state, StateId}) of
        [State] when is_record(State, pubsub_state) -> State;
-       _ -> #pubsub_state{stateid=StateId}
+       _ -> #pubsub_state{stateid = StateId}
     end.
 
-%% @spec (State) -> ok | {error, Reason}
-%%      State  = mod_pubsub:pubsubState()
-%%      Reason = mod_pubsub:stanzaError()
 %% @doc <p>Write a state into database.</p>
--spec(set_state/1 ::
-(
-  State::mod_pubsub:pubsubState())
-    -> ok
-).
 set_state(State) when is_record(State, pubsub_state) ->
     mnesia:write(State).
 %set_state(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}.
 
-%% @spec (NodeIdx, JID) -> ok | {error, Reason}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      JID     = mod_pubsub:jid()
-%%      Reason  = mod_pubsub:stanzaError()
 %% @doc <p>Delete a state from database.</p>
--spec(del_state/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  JID     :: ljid())
-    -> ok
-).
-del_state(NodeIdx, JID) ->
-    mnesia:delete({pubsub_state, {JID, NodeIdx}}).
+del_state(Nidx, Key) ->
+    mnesia:delete({pubsub_state, {Key, Nidx}}).
 
-%% @spec (NodeIdx, From) -> {result, Items}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      From    = mod_pubsub:jid()
-%%      Items   = [] | [mod_pubsub:pubsubItem()]
 %% @doc Returns the list of stored items for a given node.
 %% <p>For the default PubSub module, items are stored in Mnesia database.</p>
 %% <p>We can consider that the pubsub_item table have been created by the main
@@ -1102,228 +718,134 @@ del_state(NodeIdx, JID) ->
 %% relational database), or they can even decide not to persist any items.</p>
 %% <p>If a PubSub plugin wants to delegate the item storage to the default node,
 %% they can implement this function like this:
-%% ```get_items(NodeIdx, From) ->
-%%        node_default:get_items(NodeIdx, From).'''</p>
--spec(get_items/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  _From   :: jid())
-    -> {result, [mod_pubsub:pubsubItem()]}
-).
-
-get_items(NodeIdx, _From) ->
-    Items = mnesia:match_object(#pubsub_item{itemid = {'_', NodeIdx}, _ = '_'}),
-    {result, lists:reverse(lists:keysort(#pubsub_item.modification, Items))}.
-
--spec(get_items/6 ::
-(
-  NodeIdx               :: mod_pubsub:nodeIdx(),
-  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) ->
+%% ```get_items(Nidx, From) ->
+%%           node_default:get_items(Nidx, From).'''</p>
+get_items(Nidx, From) ->
+    get_items(Nidx, From, none).
+get_items(Nidx, _From, _RSM) ->
+    Items = mnesia:match_object(#pubsub_item{itemid = {'_', Nidx}, _ = '_'}),
+    {result, {lists:reverse(lists:keysort(#pubsub_item.modification, Items)), none}}.
+
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, none).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, _RSM) ->
     SubKey = jlib:jid_tolower(JID),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
-    SubState = get_state(NodeIdx, SubKey),
+    GenState = get_state(Nidx, GenKey),
+    SubState = get_state(Nidx, SubKey),
     Affiliation = GenState#pubsub_state.affiliation,
     Subscriptions = SubState#pubsub_state.subscriptions,
     Whitelisted = can_fetch_item(Affiliation, Subscriptions),
     if %%SubId == "", ?? ->
-       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-       %%InvalidSubId ->
-       %% Entity is subscribed but specifies an invalid subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-       GenState#pubsub_state.affiliation == outcast ->
-          {error, ?ERR_FORBIDDEN};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       (AccessModel == authorize) and not Whitelisted ->
-          {error, ?ERR_FORBIDDEN};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       true -> get_items(NodeIdx, JID)
+       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %%InvalidSubId ->
+       %% Entity is subscribed but specifies an invalid subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       (AccessModel == authorize) and not Whitelisted ->
+           {error, ?ERR_FORBIDDEN};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       true ->
+           get_items(Nidx, JID)
     end.
 
-%% @spec (NodeIdx, ItemId) -> {result, Item} | {error, 'item-not-found'}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      ItemId  = mod_pubsub:itemId()
-%%      Item    = mod_pubsub:pubsubItem()
 %% @doc <p>Returns an item (one item list), given its reference.</p>
--spec(get_item/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  ItemId  :: mod_pubsub:itemId())
-    -> {result, mod_pubsub:pubsubItem()}
-     | {error, xmlel()}
-).
 
-get_item(NodeIdx, ItemId) ->
-    case mnesia:read({pubsub_item, {ItemId, NodeIdx}}) of
-        [Item] when is_record(Item, pubsub_item) -> {result, Item};
-        _ -> {error, ?ERR_ITEM_NOT_FOUND}
+get_item(Nidx, ItemId) ->
+    case mnesia:read({pubsub_item, {ItemId, Nidx}}) of
+       [Item] when is_record(Item, pubsub_item) -> {result, Item};
+       _ -> {error, ?ERR_ITEM_NOT_FOUND}
     end.
 
-%% @spec (NodeIdx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) -> {result, Item} | {error, Reason}
-%%      NodeIdx              = mod_pubsub:nodeIdx()
-%%      ItemId               = mod_pubsub:itemId()
-%%      JID                  = mod_pubsub:jid()
-%%      AccessModel          = mod_pubsub:accessModel()
-%%      PresenceSubscription = boolean()
-%%      RosterGroup          = boolean()
-%%      SubId                = mod_pubsub:subId()
-%%      Item                 = mod_pubsub:pubsubItem()
-%%      Reason               = mod_pubsub:stanzaError() | 'item-not-found'
--spec(get_item/7 ::
-(
-  NodeIdx              :: mod_pubsub:nodeIdx(),
-  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) ->
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
     SubKey = jlib:jid_tolower(JID),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     Affiliation = GenState#pubsub_state.affiliation,
     Subscriptions = GenState#pubsub_state.subscriptions,
     Whitelisted = can_fetch_item(Affiliation, Subscriptions),
     if %%SubId == "", ?? ->
-       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-       %%InvalidSubId ->
-       %% Entity is subscribed but specifies an invalid subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-       GenState#pubsub_state.affiliation == outcast ->
-          {error, ?ERR_FORBIDDEN};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       (AccessModel == authorize) and not Whitelisted ->
-          {error, ?ERR_FORBIDDEN};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       true -> get_item(NodeIdx, ItemId)
+       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %%InvalidSubId ->
+       %% Entity is subscribed but specifies an invalid subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       (AccessModel == authorize) and not Whitelisted ->
+           {error, ?ERR_FORBIDDEN};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       true ->
+           get_item(Nidx, ItemId)
     end.
 
-%% @spec (Item) -> ok | {error, Reason}
-%%      Item   = mod_pubsub:pubsubItem()
-%%      Reason = mod_pubsub:stanzaError()
 %% @doc <p>Write an item into database.</p>
--spec(set_item/1 ::
-(
-  Item::mod_pubsub:pubsubItem())
-    -> ok
-).
 set_item(Item) when is_record(Item, pubsub_item) ->
     mnesia:write(Item).
 %set_item(_) -> {error, ?ERR_INTERNAL_SERVER_ERROR}.
 
-%% @spec (NodeIdx, ItemId) -> ok | {error, Reason}
-%%      NodeIdx = mod_pubsub:nodeIdx()
-%%      ItemId  = mod_pubsub:itemId()
-%%      Reason  = mod_pubsub:stanzaError()
 %% @doc <p>Delete an item from database.</p>
--spec(del_item/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  ItemId  :: mod_pubsub:itemId())
-    -> ok
-).
-del_item(NodeIdx, ItemId) ->
-    mnesia:delete({pubsub_item, {ItemId, NodeIdx}}).
+del_item(Nidx, ItemId) ->
+    mnesia:delete({pubsub_item, {ItemId, Nidx}}).
 
--spec(del_items/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  ItemIds :: [mod_pubsub:pubsubItem(),...])
-    -> ok
-).
-
-del_items(NodeIdx, ItemIds) ->
-    lists:foreach(fun (ItemId) -> del_item(NodeIdx, ItemId)
-                 end,
-                 ItemIds).
+del_items(Nidx, ItemIds) ->
+    lists:foreach(fun (ItemId) -> del_item(Nidx, ItemId)
+       end,
+       ItemIds).
 
-get_item_name(_Host, _Node, Id) -> Id.
+get_item_name(_Host, _Node, Id) ->
+    Id.
 
 %% @doc <p>Return the name of the node if known: Default is to return
 %% node id.</p>
--spec(node_to_path/1 ::
-(
-  Node::binary())
-    -> [binary()]
-).
-node_to_path(Node) -> str:tokens((Node), <<"/">>).
-
--spec(path_to_node/1 ::
-(
-  Path :: [binary()])
-    -> binary()
-).
+node_to_path(Node) ->
+    str:tokens(Node, <<"/">>).
 
 path_to_node([]) -> <<>>;
-path_to_node(Path) ->
-    iolist_to_binary(str:join([<<"">> | Path], <<"/">>)).
+path_to_node(Path) -> iolist_to_binary(str:join([<<"">> | Path], <<"/">>)).
 
-%% @spec (Affiliation, Subscription) -> true | false
-%%       Affiliation = owner | member | publisher | outcast | none
-%%       Subscription = subscribed | none
-%% @doc Determines if the combination of Affiliation and Subscribed
-%% are allowed to get items from a node.
 can_fetch_item(owner, _) -> true;
 can_fetch_item(member, _) -> true;
 can_fetch_item(publisher, _) -> true;
 can_fetch_item(outcast, _) -> false;
-can_fetch_item(none, Subscriptions) ->
-    is_subscribed(Subscriptions).
+can_fetch_item(none, Subscriptions) -> is_subscribed(Subscriptions).
 %can_fetch_item(_Affiliation, _Subscription) -> false.
 
 is_subscribed(Subscriptions) ->
-    lists:any(fun ({subscribed, _SubId}) -> true;
-                 (_) -> false
-             end,
-             Subscriptions).
+    lists:any(fun
+           ({subscribed, _SubId}) -> true;
+           (_) -> false
+       end,
+       Subscriptions).
 
-%% Returns the first item where Pred() is true in List
-first_in_list(_Pred, []) -> false;
+first_in_list(_Pred, []) ->
+    false;
 first_in_list(Pred, [H | T]) ->
     case Pred(H) of
-      true -> {value, H};
-      _ -> first_in_list(Pred, T)
+       true -> {value, H};
+       _ -> first_in_list(Pred, T)
     end.
index 0678b9898e8916ae444a0a5e7c70acc2a8bc74b0..d0cfad0f1ea2dd9f8647f98c3c70bcce82a6e9c0 100644 (file)
@@ -4,20 +4,19 @@
 %%% 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/]
 %%% improvements.</p>
 
 -module(node_hometree_odbc).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--define(PUBSUB, mod_pubsub_odbc).
+-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/7, get_items/3,
+    get_items/6, get_items/2, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1,
+    get_entity_subscriptions_for_send_last/2, get_last_items/3]).
+
+-export([decode_jid/1, encode_jid/1,
+    decode_affiliation/1, decode_subscriptions/1,
+    encode_affiliation/1, encode_subscriptions/1,
+    encode_host/1]).
+
+init(Host, ServerHost, _Opts) ->
+    pubsub_subscription_odbc:init(),
+    Owner = mod_pubsub:service_jid(Host),
+    mod_pubsub:create_node(Host, ServerHost, <<"/home">>, Owner, <<"hometree">>),
+    mod_pubsub:create_node(Host, ServerHost, <<"/home/", ServerHost/binary>>, Owner, <<"hometree">>),
+    ok.
 
--behaviour(gen_pubsub_node).
+terminate(_Host, _ServerHost) ->
+    ok.
 
-%% 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_entity_subscriptions_for_send_last/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/7, get_items/6,
-        get_items/3, get_items/2, get_item/7, get_item/2,
-        set_item/1, get_item_name/3, get_last_items/3,
-        path_to_node/1, node_to_path/1]).
-
--export([decode_jid/1,
-        decode_affiliation/1, decode_subscriptions/1,
-        encode_jid/1, encode_affiliation/1,
-        encode_subscriptions/1]).
-
-%% ================
-%% API definition
-%% ================
-
-%% @spec (Host, ServerHost, Opts) -> any()
-%%      Host = mod_pubsub:host()
-%%      ServerHost = mod_pubsub: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>
-init(_Host, _ServerHost, _Opts) ->
-    pubsub_subscription_odbc:init(), ok.
-
-%% @spec (Host, ServerHost) -> any()
-%%      Host = mod_pubsub:host()
-%%      ServerHost = host()
-%% @doc <p>Called during pubsub modules termination. Any pubsub plugin must
-%% implement this function. It can return anything.</p>
-terminate(_Host, _ServerHost) -> ok.
-
-%% @spec () -> [Option]
-%%      Option = mod_pubsub:nodeOption()
-%% @doc Returns the default pubsub node options.
-%% <p>Example of function return value:</p>
-%%     ```
-%%      [{deliver_payloads, true},
-%%       {notify_config, false},
-%%       {notify_delete, false},
-%%       {notify_retract, true},
-%%       {persist_items, true},
-%%       {max_items, 10},
-%%       {subscribe, true},
-%%       {access_model, open},
-%%       {publish_model, publishers},
-%%       {max_payload_size, 100000},
-%%       {send_last_published_item, never},
-%%       {presence_based_delivery, false}]'''
--spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
 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}, {odbc, true},
-     {rsm, true}].
-
-%% @spec () -> []
-%% @doc Returns the node features
--spec(features/0 :: () -> Features::[Feature::binary(),...]).
+    [{odbc, true}, {rsm, true} | node_hometree:options()].
+
 features() ->
-    [<<"create-nodes">>, <<"auto-create">>,
-     <<"access-authorize">>, <<"delete-nodes">>,
-     <<"delete-items">>, <<"get-pending">>,
-     <<"instant-nodes">>, <<"manage-subscriptions">>,
-     <<"modify-affiliations">>, <<"multi-subscribe">>,
-     <<"outcast-affiliation">>, <<"persistent-items">>,
-     <<"publish">>, <<"purge-nodes">>, <<"retract-items">>,
-     <<"retrieve-affiliations">>, <<"retrieve-items">>,
-     <<"retrieve-subscriptions">>, <<"subscribe">>,
-     <<"subscription-notifications">>,
-     <<"subscription-options">>, <<"rsm">>].
-
-%% @spec (Host, ServerHost, Node, ParentNode, Owner, Access) -> bool()
-%%      Host = mod_pubsub:host()
-%%      ServerHost = mod_pubsub:host()
-%%      Node = mod_pubsub:pubsubNode()
-%%      ParentNode = mod_pubsub:pubsubNode()
-%%      Owner = mod_pubsub:jid()
-%%      Access = all | atom()
+    [<<"rsm">> | node_hometree:features()].
+
 %% @doc Checks if the current user has the permission to create the requested node
 %% <p>In {@link node_default}, the permission is decided by the place in the
 %% hierarchy where the user is creating the node. The access parameter is also
@@ -158,95 +89,32 @@ features() ->
 %% <tt>access_createnode</tt> ACL value in ejabberd config file.</p>
 %% <p>This function also check that node can be created a a children of its
 %% parent node</p>
-%% <p>PubSub plugins can redefine the PubSub node creation rights as they
-%% which. They can simply delegate this check to the {@link node_default}
-%% module by implementing this function like this:
-%% ```check_create_user_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
-%%        node_default:check_create_user_permission(Host, ServerHost, Node, ParentNode, Owner, Access).'''</p>
--spec(create_node_permission/6 ::
-(
-  Host        :: mod_pubsub:host(),
-  ServerHost  :: binary(),
-  Node        :: mod_pubsub:nodeId(),
-  _ParentNode :: _,
-  Owner       :: jid(),
-  Access      :: atom())
-    -> {result, boolean()}
-).
-create_node_permission(Host, ServerHost, Node, _ParentNode, Owner, Access) ->
-    LOwner = jlib:jid_tolower(Owner),
-    {User, Server, _Resource} = LOwner,
-    Allowed = case LOwner of
-               {<<"">>, Host, <<"">>} ->
-                   true; % pubsub service always allowed
-               _ ->
-                   case acl:match_rule(ServerHost, Access, LOwner) of
-                     allow ->
-                         case node_to_path(Node) of
-                           [<<"home">>, Server, User | _] -> true;
-                           _ -> false
-                         end;
-                     _ -> false
-                   end
-             end,
-    {result, Allowed}.
-
-%% @spec (NodeId, Owner) ->
-%%               {result, Result} | exit
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Owner = mod_pubsub:jid()
-%% @doc <p></p>
--spec(create_node/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, {default, broadcast}}
-).
-create_node(NodeIdx, Owner) ->
-    OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    State = #pubsub_state{stateid = {OwnerKey, NodeIdx}, affiliation = owner},
-    catch
-      ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, "
-                                  "affiliation, subscriptions) values(">>,
-                                state_to_raw(NodeIdx, State), <<");">>]),
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+    node_hometree:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+    {_U, _S, _R} = OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
+    State = #pubsub_state{stateid = {OwnerKey, Nidx}, affiliation = owner},
+    catch ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, affiliation, subscriptions) "
+               "values(">>, state_to_raw(Nidx, State), <<");">>]),
     {result, {default, broadcast}}.
 
-%% @spec (Removed) -> ok
-%%      Removed = [mod_pubsub:pubsubNode()]
-%% @doc <p>purge items of deleted nodes after effective deletion.</p>
--spec(delete_node/1 ::
-(
-  Removed :: [mod_pubsub:pubsubNode(),...])
-    -> {result, {default, broadcast, _}}
-).
-delete_node(Removed) ->
-    Reply = lists:map(fun (#pubsub_node{id = NodeId} =
-                              PubsubNode) ->
-                             Subscriptions = case catch
-                                                    ejabberd_odbc:sql_query_t([<<"select jid, subscriptions from pubsub_state "
-                                                                                 "where nodeid='">>,
-                                                                               NodeId,
-                                                                               <<"';">>])
-                                                 of
-                                               {selected,
-                                                [<<"jid">>,
-                                                 <<"subscriptions">>],
-                                                RItems} ->
-                                                   lists:map(fun ([SJID,
-                                                                   Subscriptions]) ->
-                                                                     {decode_jid(SJID),
-                                                                      decode_subscriptions(Subscriptions)}
-                                                             end,
-                                                             RItems);
-                                               _ -> []
-                                             end,
-                             {PubsubNode, Subscriptions}
-                     end,
-                     Removed),
+delete_node(Nodes) ->
+    Reply = lists:map(fun (#pubsub_node{id = Nidx} = Node) ->
+                   Subscriptions = case catch
+                       ejabberd_odbc:sql_query_t([<<"select jid, subscriptions "
+                                   "from pubsub_state where nodeid='">>, Nidx, <<"';">>])
+                   of
+                       {selected, [<<"jid">>, <<"subscriptions">>], RItems} ->
+                           [{decode_jid(SJID), decode_subscriptions(Subs)} || [SJID, Subs] <- RItems];
+                       _ ->
+                           []
+                   end,
+                   {Node, Subscriptions}
+           end,
+           Nodes),
     {result, {default, broadcast, Reply}}.
 
-%% @spec (NodeId, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup, Options) ->
-%%              {error, Reason} | {result, Result}
 %% @doc <p>Accepts or rejects subcription requests on a PubSub node.</p>
 %% <p>The mechanism works as follow:
 %% <ul>
@@ -278,192 +146,118 @@ delete_node(Removed) ->
 %%   to completly disable persistance.</li></ul>
 %% </p>
 %% <p>In the default plugin module, the record is unchanged.</p>
--spec(subscribe_node/8 ::
-(
-  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) ->
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
     SubKey = jlib:jid_tolower(Subscriber),
     GenKey = jlib:jid_remove_resource(SubKey),
-    Authorized =
-       jlib:jid_tolower(jlib:jid_remove_resource(Sender)) ==
-         GenKey,
-    {Affiliation, Subscriptions} =
-       select_affiliation_subscriptions(NodeId, GenKey,
-                                        SubKey),
-    Whitelisted = lists:member(Affiliation,
-                              [member, publisher, owner]),
-    PendingSubscription = lists:any(fun ({pending, _}) ->
-                                           true;
-                                       (_) -> false
-                                   end,
-                                   Subscriptions),
+    Authorized = jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == GenKey,
+    {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey),
+    Whitelisted = lists:member(Affiliation, [member, publisher, owner]),
+    PendingSubscription = lists:any(fun
+               ({pending, _}) -> true;
+               (_) -> false
+           end,
+           Subscriptions),
     if not Authorized ->
-          {error,
-           ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)};
-       Affiliation == outcast -> {error, ?ERR_FORBIDDEN};
-       PendingSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"pending-subscription">>)};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       %%ForbiddenAnonymous ->
-       %%      % Requesting entity is anonymous
-       %%      {error, ?ERR_FORBIDDEN};
-       true ->
-          {result, SubId} = pubsub_subscription_odbc:subscribe_node(Subscriber, NodeId, Options),
-                NewSub = case AccessModel of
-                           authorize -> pending;
-                           _ -> subscribed
-                         end,
-                update_subscription(NodeId, SubKey,
-                                    [{NewSub, SubId} | Subscriptions]),
-                case {NewSub, SendLast} of
-                  {subscribed, never} ->
-                      {result, {default, subscribed, SubId}};
-                  {subscribed, _} ->
-                      {result, {default, subscribed, SubId, send_last}};
-                  {_, _} -> {result, {default, pending, SubId}}
-                end
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"invalid-jid">>)};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       PendingSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"pending-subscription">>)};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       %%ForbiddenAnonymous ->
+       %%        % Requesting entity is anonymous
+       %%        {error, ?ERR_FORBIDDEN};
+       true ->
+           {result, SubId} = pubsub_subscription_odbc:subscribe_node(Subscriber, Nidx, Options),
+           NewSub = case AccessModel of
+               authorize -> pending;
+               _ -> subscribed
+           end,
+           update_subscription(Nidx, SubKey, [{NewSub, SubId} | Subscriptions]),
+           case {NewSub, SendLast} of
+               {subscribed, never} -> {result, {default, subscribed, SubId}};
+               {subscribed, _} -> {result, {default, subscribed, SubId, send_last}};
+               {_, _} -> {result, {default, pending, SubId}}
+           end
     end.
 
-%% @spec (NodeId, Sender, Subscriber, SubId) ->
-%%                     {error, Reason} | {result, []}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Sender = mod_pubsub:jid()
-%%      Subscriber = mod_pubsub:jid()
-%%      SubId = mod_pubsub:subid()
-%%      Reason = mod_pubsub:stanzaError()
 %% @doc <p>Unsubscribe the <tt>Subscriber</tt> from the <tt>Node</tt>.</p>
--spec(unsubscribe_node/4 ::
-(
-  NodeIdx    :: mod_pubsub:nodeIdx(),
-  Sender     :: jid(),
-  Subscriber :: jid(),
-  SubId      :: subId())
-    -> {result, default}
-     %
-     | {error, _}
-     | {error, _, binary()}
-).
-unsubscribe_node(NodeId, Sender, Subscriber, SubId) ->
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
     SubKey = jlib:jid_tolower(Subscriber),
     GenKey = jlib:jid_remove_resource(SubKey),
-    Authorized =
-       jlib:jid_tolower(jlib:jid_remove_resource(Sender)) ==
-         GenKey,
-    {Affiliation, Subscriptions} =
-       select_affiliation_subscriptions(NodeId, SubKey),
+    Authorized = jlib:jid_tolower(jlib:jid_remove_resource(Sender)) == GenKey,
+    {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, SubKey),
     SubIdExists = case SubId of
-                   <<>> -> false;
-                   List when is_binary(List) -> true;
-                   _ -> false
-                 end,
+       <<>> -> false;
+       Binary when is_binary(Binary) -> true;
+       _ -> false
+    end,
     if
-      %% Requesting entity is prohibited from unsubscribing entity
-      not Authorized -> {error, ?ERR_FORBIDDEN};
-      %% Entity did not specify SubId
-      %%SubId == "", ?? ->
-      %%       {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-      %% Invalid subscription identifier
-      %%InvalidSubId ->
-      %%       {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-      %% Requesting entity is not a subscriber
-      Subscriptions == [] ->
-         {error,
-          ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL),
-                        <<"not-subscribed">>)};
-      %% Subid supplied, so use that.
-      SubIdExists ->
-         Sub = first_in_list(fun (S) ->
-                                     case S of
-                                       {_Sub, SubId} -> true;
-                                       _ -> false
-                                     end
-                             end,
-                             Subscriptions),
-         case Sub of
-           {value, S} ->
-               delete_subscription(SubKey, NodeId, S, Affiliation,
-                                   Subscriptions),
-               {result, default};
-           false ->
-               {error,
-                ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL),
-                              <<"not-subscribed">>)}
-         end;
-      %% Asking to remove all subscriptions to the given node
-      SubId == all ->
-         [delete_subscription(SubKey, NodeId, S, Affiliation,
-                              Subscriptions)
-          || S <- Subscriptions],
-         {result, default};
-      %% No subid supplied, but there's only one matching
-      %% subscription, so use that.
-      length(Subscriptions) == 1 ->
-         delete_subscription(SubKey, NodeId, hd(Subscriptions),
-                             Affiliation, Subscriptions),
-         {result, default};
-      %% No subid and more than one possible subscription match.
-      true ->
-         {error,
-          ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}
+       %% Requesting entity is prohibited from unsubscribing entity
+       not Authorized ->
+           {error, ?ERR_FORBIDDEN};
+       %% Entity did not specify SubId
+       %%SubId == "", ?? ->
+       %%        {error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %% Invalid subscription identifier
+       %%InvalidSubId ->
+       %%        {error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       %% Requesting entity is not a subscriber
+       Subscriptions == [] ->
+           {error,
+               ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)};
+       %% Subid supplied, so use that.
+       SubIdExists ->
+           Sub = first_in_list(fun
+                       ({_, S}) when S == SubId -> true;
+                       (_) -> false
+                   end,
+                   Subscriptions),
+           case Sub of
+               {value, S} ->
+                   delete_subscription(SubKey, Nidx, S, Affiliation, Subscriptions),
+                   {result, default};
+               false ->
+                   {error,
+                       ?ERR_EXTENDED((?ERR_UNEXPECTED_REQUEST_CANCEL), <<"not-subscribed">>)}
+           end;
+       %% Asking to remove all subscriptions to the given node
+       SubId == all ->
+           [delete_subscription(SubKey, Nidx, S, Affiliation, Subscriptions)
+               || S <- Subscriptions],
+           {result, default};
+       %% No subid supplied, but there's only one matching subscription
+       length(Subscriptions) == 1 ->
+           delete_subscription(SubKey, Nidx, hd(Subscriptions), Affiliation, Subscriptions),
+           {result, default};
+       %% No subid and more than one possible subscription match.
+       true ->
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)}
     end.
 
-%-spec(delete_subscriptions/5 ::
-%(
-%  SubKey        :: ljid(),
-%  NodeIdx       :: mod_pubsub:nodeIdx(),
-%  _             :: {mod_pubsub:subscription(), mod_pubsub:subId()},
-%  SubState      :: mod_pubsub:pubsubState(),
-%  Subscriptions :: [{mod_pubsub:subscription(), mod_pubsub:subId()}])
-%    -> ok
-%).
-delete_subscription(SubKey, NodeIdx,
-                   {Subscription, SubId}, Affiliation, Subscriptions) ->
+delete_subscription(SubKey, Nidx, {Subscription, SubId}, Affiliation, Subscriptions) ->
     NewSubs = Subscriptions -- [{Subscription, SubId}],
-    pubsub_subscription_odbc:unsubscribe_node(SubKey, NodeIdx, SubId),
+    pubsub_subscription_odbc:unsubscribe_node(SubKey, Nidx, SubId),
     case {Affiliation, NewSubs} of
-      {none, []} -> del_state(NodeIdx, SubKey);
-      _ -> update_subscription(NodeIdx, SubKey, NewSubs)
+       {none, []} -> del_state(Nidx, SubKey);
+       _ -> update_subscription(Nidx, SubKey, NewSubs)
     end.
 
-%% @spec (NodeId, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
-%%              {true, PubsubItem} | {result, Reply}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Publisher = mod_pubsub:jid()
-%%      PublishModel = atom()
-%%      MaxItems = integer()
-%%      ItemId = string()
-%%      Payload = term()
 %% @doc <p>Publishes the item passed as parameter.</p>
 %% <p>The mechanism works as follow:
 %% <ul>
@@ -494,53 +288,34 @@ delete_subscription(SubKey, NodeIdx,
 %%   to completly disable persistance.</li></ul>
 %% </p>
 %% <p>In the default plugin module, the record is unchanged.</p>
-
--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, PublishModel, MaxItems, ItemId, Payload) ->
+publish_item(Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
     SubKey = jlib:jid_tolower(Publisher),
     GenKey = jlib:jid_remove_resource(SubKey),
-    {Affiliation, Subscriptions} =
-       select_affiliation_subscriptions(NodeIdx, GenKey, SubKey),
+    {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey),
     Subscribed = case PublishModel of
-                  subscribers -> is_subscribed(Subscriptions);
-                  _ -> undefined
-                end,
-    if not
-        ((PublishModel == open) or
-           (PublishModel == publishers) and
-             ((Affiliation == owner) or (Affiliation == publisher))
-           or (Subscribed == true)) ->
-          {error, ?ERR_FORBIDDEN};
-       true ->
-          if MaxItems > 0 ->
-                 PubId = {now(), SubKey},
-                 set_item(#pubsub_item{itemid = {ItemId, NodeIdx},
-                                       creation = {now(), GenKey},
-                                       modification = PubId,
-                                       payload = Payload}),
-                 Items = [ItemId | itemids(NodeIdx, GenKey) -- [ItemId]],
-                 {result, {_, OI}} = remove_extra_items(NodeIdx, MaxItems, Items),
-                 {result, {default, broadcast, OI}};
-             true -> {result, {default, broadcast, []}}
-          end
+       subscribers -> is_subscribed(Subscriptions);
+       _ -> undefined
+    end,
+    if not ((PublishModel == open) or
+                   (PublishModel == publishers) and
+                   ((Affiliation == owner) or (Affiliation == publisher))
+                   or (Subscribed == true)) ->
+           {error, ?ERR_FORBIDDEN};
+       true ->
+           if MaxItems > 0 ->
+                   PubId = {now(), SubKey},
+                   set_item(#pubsub_item{itemid = {ItemId, Nidx},
+                           creation = {now(), GenKey},
+                           modification = PubId,
+                           payload = Payload}),
+                   Items = [ItemId | itemids(Nidx, GenKey) -- [ItemId]],
+                   {result, {_, OI}} = remove_extra_items(Nidx, MaxItems, Items),
+                   {result, {default, broadcast, OI}};
+               true ->
+                   {result, {default, broadcast, []}}
+           end
     end.
 
-%% @spec (NodeId, MaxItems, ItemIds) -> {NewItemIds,OldItemIds}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      MaxItems = integer() | unlimited
-%%      ItemIds = [ItemId::string()]
-%%      NewItemIds = [ItemId::string()]
 %% @doc <p>This function is used to remove extra items, most notably when the
 %% maximum number of items has been reached.</p>
 %% <p>This function is used internally by the core PubSub module, as no
@@ -549,86 +324,56 @@ publish_item(NodeIdx, Publisher, PublishModel, MaxItems, ItemId, Payload) ->
 %% rules can be used.</p>
 %% <p>If another PubSub plugin wants to delegate the item removal (and if the
 %% plugin is using the default pubsub storage), it can implements this function like this:
-%% ```remove_extra_items(NodeId, MaxItems, ItemIds) ->
-%%        node_default:remove_extra_items(NodeId, MaxItems, ItemIds).'''</p>
-remove_extra_items(_NodeId, unlimited, ItemIds) ->
+%% ```remove_extra_items(Nidx, MaxItems, ItemIds) ->
+%%           node_default:remove_extra_items(Nidx, MaxItems, ItemIds).'''</p>
+remove_extra_items(_Nidx, unlimited, ItemIds) ->
     {result, {ItemIds, []}};
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
     NewItems = lists:sublist(ItemIds, MaxItems),
     OldItems = lists:nthtail(length(NewItems), ItemIds),
-    del_items(NodeId, OldItems),
+    del_items(Nidx, OldItems),
     {result, {NewItems, OldItems}}.
 
-%% @spec (NodeId, Publisher, PublishModel, ItemId) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, []}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Publisher = mod_pubsub:jid()
-%%      PublishModel = atom()
-%%      ItemId = string()
 %% @doc <p>Triggers item deletion.</p>
 %% <p>Default plugin: The user performing the deletion must be the node owner
-%% or a publisher.</p>
--spec(delete_item/4 ::
-(
-  NodeIdx      :: mod_pubsub:nodeIdx(),
-  Publisher    :: jid(),
-  PublishModel :: mod_pubsub:publishModel(),
-  ItemId       :: <<>> | mod_pubsub:itemId())
-    -> {result, {default, broadcast}}
-    %%%
-     | {error, _}
-).
-delete_item(NodeIdx, Publisher, PublishModel, ItemId) ->
+%% or a publisher, or PublishModel being open.</p>
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
     SubKey = jlib:jid_tolower(Publisher),
     GenKey = jlib:jid_remove_resource(SubKey),
-    {result, Affiliation} = get_affiliation(NodeIdx, GenKey),
+    {result, Affiliation} = get_affiliation(Nidx, GenKey),
     Allowed = Affiliation == publisher orelse
-               Affiliation == owner orelse
-                 PublishModel == open orelse
-                   case get_item(NodeIdx, ItemId) of
-                     {result, #pubsub_item{creation = {_, GenKey}}} -> true;
-                     _ -> false
-                   end,
-    if not Allowed -> {error, ?ERR_FORBIDDEN};
-       true ->
-          case del_item(NodeIdx, ItemId) of
-            {updated, 1} -> {result, {default, broadcast}};
-            _ -> {error, ?ERR_ITEM_NOT_FOUND}
-          end
+       Affiliation == owner orelse
+       PublishModel == open orelse
+       case get_item(Nidx, ItemId) of
+       {result, #pubsub_item{creation = {_, GenKey}}} -> true;
+       _ -> false
+    end,
+    if not Allowed ->
+           {error, ?ERR_FORBIDDEN};
+       true ->
+           case del_item(Nidx, ItemId) of
+               {updated, 1} -> {result, {default, broadcast}};
+               _ -> {error, ?ERR_ITEM_NOT_FOUND}
+           end
     end.
 
-%% @spec (NodeId, Owner) ->
-%%               {error, Reason::stanzaError()} |
-%%               {result, {default, broadcast}}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Owner = mod_pubsub:jid()
--spec(purge_node/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, {default, broadcast}}
-     | {error, _}
-).
-purge_node(NodeIdx, Owner) ->
+purge_node(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    GenState = get_state(NodeIdx, GenKey),
+    GenState = get_state(Nidx, GenKey),
     case GenState of
-      #pubsub_state{affiliation = owner} ->
-         {result, States} = get_states(NodeIdx),
-         lists:foreach(fun (#pubsub_state{items = []}) -> ok;
-                           (#pubsub_state{items = Items}) ->
-                               del_items(NodeIdx, Items)
-                       end,
-                       States),
-         {result, {default, broadcast}};
-      _ -> {error, ?ERR_FORBIDDEN}
+       #pubsub_state{affiliation = owner} ->
+           {result, States} = get_states(Nidx),
+           lists:foreach(fun
+                   (#pubsub_state{items = []}) -> ok;
+                   (#pubsub_state{items = Items}) -> del_items(Nidx, Items)
+               end,
+               States),
+           {result, {default, broadcast}};
+       _ ->
+           {error, ?ERR_FORBIDDEN}
     end.
 
-%% @spec (Host, JID) -> [{Node,Affiliation}]
-%%      Host = host()
-%%      JID = mod_pubsub:jid()
 %% @doc <p>Return the current affiliations for the given user</p>
 %% <p>The default module reads affiliations in the main Mnesia
 %% <tt>pubsub_state</tt> table. If a plugin stores its data in the same
@@ -636,103 +381,60 @@ purge_node(NodeIdx, Owner) ->
 %% the default PubSub module. Otherwise, it should return its own affiliation,
 %% that will be added to the affiliation stored in the main
 %% <tt>pubsub_state</tt> table.</p>
--spec(get_entity_affiliations/2 ::
-(
-  Host  :: mod_pubsub:hostPubsub(),
-  Owner :: jid())
-    -> {result, [{mod_pubsub:pubsubNode(), mod_pubsub:affiliation()}]}
-).
 get_entity_affiliations(Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    H = (?PUBSUB):escape(Host),
+    H = encode_host(Host),
     J = encode_jid(GenKey),
     Reply = case catch
-                  ejabberd_odbc:sql_query_t([<<"select node, type, i.nodeid, affiliation "
-                                               "from pubsub_state i, pubsub_node n where "
-                                               "i.nodeid = n.nodeid and jid='">>,
-                                             J, <<"' and host='">>, H,
-                                             <<"';">>])
-               of
-             {selected,
-              [<<"node">>, <<"type">>, <<"nodeid">>,
-               <<"affiliation">>],
-              RItems} ->
-                 lists:map(fun ([N, T, I, A]) ->
-                                   Node = nodetree_tree_odbc:raw_to_node(Host,
-                                                                         [N,
-                                                                          <<"">>,
-                                                                          T,
-                                                                          I]),
-                                   {Node, decode_affiliation(A)}
-                           end,
-                           RItems);
-             _ -> []
-           end,
+       ejabberd_odbc:sql_query_t([<<"select node, type, i.nodeid, affiliation "
+                   "from pubsub_state i, pubsub_node n where "
+                   "i.nodeid = n.nodeid and jid='">>, J, <<"' and host='">>, H, <<"';">>])
+    of
+       {selected, [<<"node">>, <<"type">>, <<"nodeid">>, <<"affiliation">>], RItems} ->
+           [{nodetree_tree_odbc:raw_to_node(Host, [N, <<"">>, T, I]), decode_affiliation(A)}
+               || [N, T, I, A] <- RItems];
+       _ ->
+           []
+    end,
     {result, Reply}.
 
--spec(get_node_affiliations/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [{ljid(), mod_pubsub:affiliation()}]}
-).
-get_node_affiliations(NodeIdx) ->
+get_node_affiliations(Nidx) ->
     Reply = case catch
-                  ejabberd_odbc:sql_query_t([<<"select jid, affiliation from pubsub_state "
-                                               "where nodeid='">>,
-                                             NodeIdx, <<"';">>])
-               of
-             {selected, [<<"jid">>, <<"affiliation">>], RItems} ->
-                 lists:map(fun ([J, A]) ->
-                                   {decode_jid(J), decode_affiliation(A)}
-                           end,
-                           RItems);
-             _ -> []
-           end,
+       ejabberd_odbc:sql_query_t([<<"select jid, affiliation from pubsub_state "
+                   "where nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected, [<<"jid">>, <<"affiliation">>], RItems} ->
+           [{decode_jid(J), decode_affiliation(A)} || [J, A] <- RItems];
+       _ ->
+           []
+    end,
     {result, Reply}.
 
--spec(get_affiliation/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: ljid())
-    -> {result, mod_pubsub:affiliation()}
-).
-
-get_affiliation(NodeIdx, Owner) ->
+get_affiliation(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
     J = encode_jid(GenKey),
     Reply = case catch
-                  ejabberd_odbc:sql_query_t([<<"select affiliation from pubsub_state "
-                                               "where nodeid='">>,
-                                             NodeIdx, <<"' and jid='">>, J,
-                                             <<"';">>])
-               of
-             {selected, [<<"affiliation">>], [[A]]} ->
-                 decode_affiliation(A);
-             _ -> none
-           end,
+       ejabberd_odbc:sql_query_t([<<"select affiliation from pubsub_state "
+                   "where nodeid='">>, Nidx, <<"' and jid='">>, J, <<"';">>])
+    of
+       {selected, [<<"affiliation">>], [[A]]} ->
+           decode_affiliation(A);
+       _ ->
+           none
+    end,
     {result, Reply}.
 
--spec(set_affiliation/3 ::
-(
-  NodeIdx     :: mod_pubsub:nodeIdx(),
-  Owner       :: ljid(),
-  Affiliation :: mod_pubsub:affiliation())
-    -> ok
-).
-set_affiliation(NodeIdx, Owner, Affiliation) ->
+set_affiliation(Nidx, Owner, Affiliation) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    {_, Subscriptions} = select_affiliation_subscriptions(NodeIdx, GenKey),
+    {_, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey),
     case {Affiliation, Subscriptions} of
-      {none, none} -> del_state(NodeIdx, GenKey);
-      _ -> update_affiliation(NodeIdx, GenKey, Affiliation)
+       {none, []} -> del_state(Nidx, GenKey);
+       _ -> update_affiliation(Nidx, GenKey, Affiliation)
     end.
 
-%% @spec (Host, Owner) -> [{Node,Subscription}]
-%%      Host = host()
-%%      Owner = mod_pubsub:jid()
 %% @doc <p>Return the current subscriptions for the given user</p>
 %% <p>The default module reads subscriptions in the main Mnesia
 %% <tt>pubsub_state</tt> table. If a plugin stores its data in the same
@@ -740,338 +442,225 @@ set_affiliation(NodeIdx, Owner, Affiliation) ->
 %% the default PubSub module. Otherwise, it should return its own affiliation,
 %% that will be added to the affiliation stored in the main
 %% <tt>pubsub_state</tt> table.</p>
-
--spec(get_entity_subscriptions/2 ::
-(
-  Host :: mod_pubsub:host(),
-  Owner :: jid())
-    -> {result,
-          [{mod_pubsub:pubsubNode(),
-            mod_pubsub:subscription(),
-            mod_pubsub:subId(),
-            ljid()}]
-       }
-).
 get_entity_subscriptions(Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    H = (?PUBSUB):escape(Host),
+    H = encode_host(Host),
     SJ = encode_jid(SubKey),
     GJ = encode_jid(GenKey),
     Query = case SubKey of
-             GenKey ->
-                 [<<"select node, type, i.nodeid, jid, subscriptio"
-                    "ns from pubsub_state i, pubsub_node "
-                    "n where i.nodeid = n.nodeid and jid "
-                    "like '">>,
-                  GJ, <<"%' and host='">>, H, <<"';">>];
-             _ ->
-                 [<<"select node, type, i.nodeid, jid, subscriptio"
-                    "ns from pubsub_state i, pubsub_node "
-                    "n where i.nodeid = n.nodeid and jid "
-                    "in ('">>,
-                  SJ, <<"', '">>, GJ, <<"') and host='">>, H, <<"';">>]
-           end,
+       GenKey ->
+           [<<"select node, type, i.nodeid, jid, subscriptions "
+                   "from pubsub_state i, pubsub_node n "
+                   "where i.nodeid = n.nodeid and jid like '">>, GJ, <<"%' and host='">>, H, <<"';">>];
+       _ ->
+           [<<"select node, type, i.nodeid, jid, subscriptions "
+                   "from pubsub_state i, pubsub_node n "
+                   "where i.nodeid = n.nodeid and jid in ('">>, SJ, <<"', '">>, GJ, <<"') and host='">>, H, <<"';">>]
+    end,
     Reply = case catch ejabberd_odbc:sql_query_t(Query) of
-             {selected,
-              [<<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>,
-               <<"subscriptions">>],
-              RItems} ->
-                 lists:foldl(fun ([N, T, I, J, S], Acc) ->
-                                     Node =
-                                         nodetree_tree_odbc:raw_to_node(Host,
-                                                                        [N,
-                                                                         <<"">>,
-                                                                         T,
-                                                                         I]),
-                                     Jid = decode_jid(J),
-                                     case decode_subscriptions(S) of
-                                       [] -> [{Node, none, Jid} | Acc];
-                                       Subs ->
-                                           lists:foldl(fun ({Sub, SubId}, Acc2) ->
-                                                               [{Node, Sub, SubId, Jid} | Acc2]
-                                                       end,
-                                                       Acc, Subs)
-                                     end
-                             end,
-                             [], RItems);
-             _ -> []
-           end,
+       {selected,
+                   [<<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>], RItems} ->
+           lists:foldl(fun ([N, T, I, J, S], Acc) ->
+                       Node = nodetree_tree_odbc:raw_to_node(Host, [N, <<"">>, T, I]),
+                       Jid = decode_jid(J),
+                       case decode_subscriptions(S) of
+                           [] ->
+                               [{Node, none, Jid} | Acc];
+                           Subs ->
+                               lists:foldl(fun ({Sub, SubId}, Acc2) ->
+                                           [{Node, Sub, SubId, Jid} | Acc2]
+                                   end,
+                                   Acc, Subs)
+                       end
+               end,
+               [], RItems);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
 %% do the same as get_entity_subscriptions but filter result only to
 %% nodes having send_last_published_item=on_sub_and_presence
 %% as this call avoid seeking node, it must return node and type as well
 -spec(get_entity_subscriptions_for_send_last/2 ::
-(
-  Host :: mod_pubsub:hostPubsub(),
-  Owner :: jid())
+    (
+       Host :: mod_pubsub:hostPubsub(),
+       Owner :: jid())
     -> {result,
-          [{mod_pubsub:pubsubNode(),
-            mod_pubsub:subscription(),
-            mod_pubsub:subId(),
-            ljid()}]
-       }
-).
-
+    [{mod_pubsub:pubsubNode(),
+    mod_pubsub:subscription(),
+    mod_pubsub:subId(),
+    ljid()}]
+    }
+    ).
 get_entity_subscriptions_for_send_last(Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    H = (?PUBSUB):escape(Host),
+    H = encode_host(Host),
     SJ = encode_jid(SubKey),
     GJ = encode_jid(GenKey),
     Query = case SubKey of
-             GenKey ->
-                 [<<"select node, type, i.nodeid, jid, subscriptio"
-                    "ns from pubsub_state i, pubsub_node "
-                    "n, pubsub_node_option o where i.nodeid "
-                    "= n.nodeid and n.nodeid = o.nodeid and "
-                    "name='send_last_published_item' and "
-                    "val='on_sub_and_presence' and jid like "
-                    "'">>,
-                  GJ, <<"%' and host='">>, H, <<"';">>];
-             _ ->
-                 [<<"select node, type, i.nodeid, jid, subscriptio"
-                    "ns from pubsub_state i, pubsub_node "
-                    "n, pubsub_node_option o where i.nodeid "
-                    "= n.nodeid and n.nodeid = o.nodeid and "
-                    "name='send_last_published_item' and "
-                    "val='on_sub_and_presence' and jid in "
-                    "('">>,
-                  SJ, <<"', '">>, GJ, <<"') and host='">>, H, <<"';">>]
-           end,
+       GenKey ->
+           [<<"select node, type, i.nodeid, jid, subscriptions "
+                   "from pubsub_state i, pubsub_node n, pubsub_node_option o "
+                   "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and name='send_last_published_item' "
+                   "and val='on_sub_and_presence' and jid like '">>, GJ, <<"%' and host='">>, H, <<"';">>];
+       _ ->
+           [<<"select node, type, i.nodeid, jid, subscriptions "
+                   "from pubsub_state i, pubsub_node n, pubsub_node_option o "
+                   "where i.nodeid = n.nodeid and n.nodeid = o.nodeid and name='send_last_published_item' "
+                   "and val='on_sub_and_presence' and jid in ('">>, SJ, <<"', '">>, GJ, <<"') and host='">>, H, <<"';">>]
+    end,
     Reply = case catch ejabberd_odbc:sql_query_t(Query) of
-             {selected,
-              [<<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>,
-               <<"subscriptions">>],
-              RItems} ->
-                 lists:foldl(fun ([N, T, I, J, S], Acc) ->
-                                     Node =
-                                         nodetree_tree_odbc:raw_to_node(Host,
-                                                                        [N,
-                                                                         <<"">>,
-                                                                         T,
-                                                                         I]),
-                                     Jid = decode_jid(J),
-                                     case decode_subscriptions(S) of
-                                       [] -> [{Node, none, Jid} | Acc];
-                                       Subs ->
-                                           lists:foldl(fun ({Sub, SubId}, Acc2) ->
-                                                               [{Node, Sub, SubId, Jid}| Acc2]
-                                                       end,
-                                                       Acc, Subs)
-                                     end
-                             end,
-                             [], RItems);
-             _ -> []
-           end,
+       {selected,
+                   [<<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>], RItems} ->
+           lists:foldl(fun ([N, T, I, J, S], Acc) ->
+                       Node = nodetree_tree_odbc:raw_to_node(Host, [N, <<"">>, T, I]),
+                       Jid = decode_jid(J),
+                       case decode_subscriptions(S) of
+                           [] ->
+                               [{Node, none, Jid} | Acc];
+                           Subs ->
+                               lists:foldl(fun ({Sub, SubId}, Acc2) ->
+                                           [{Node, Sub, SubId, Jid}| Acc2]
+                                   end,
+                                   Acc, Subs)
+                       end
+               end,
+               [], RItems);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
--spec(get_node_subscriptions/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [{ljid(), mod_pubsub:subscription(), mod_pubsub:subId()}]}
-).
-get_node_subscriptions(NodeIdx) ->
+get_node_subscriptions(Nidx) ->
     Reply = case catch
-                  ejabberd_odbc:sql_query_t([<<"select jid, subscriptions from pubsub_state "
-                                               "where nodeid='">>,
-                                             NodeIdx, <<"';">>])
-               of
-             {selected, [<<"jid">>, <<"subscriptions">>], RItems} ->
-                 lists:foldl(fun ([J, S], Acc) ->
-                                     Jid = decode_jid(J),
-                                     case decode_subscriptions(S) of
-                                       [] -> [{Jid, none} | Acc];
-                                       Subs ->
-                                           lists:foldl(fun ({Sub, SubId}, Acc2) ->
-                                                               [{Jid, Sub, SubId} | Acc2]
-                                                       end,
-                                                       Acc, Subs)
-                                     end
-                             end,
-                             [], RItems);
-             _ -> []
-           end,
+       ejabberd_odbc:sql_query_t([<<"select jid, subscriptions from pubsub_state "
+                   "where nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected, [<<"jid">>, <<"subscriptions">>], RItems} ->
+           lists:foldl(fun ([J, S], Acc) ->
+                       Jid = decode_jid(J),
+                       case decode_subscriptions(S) of
+                           [] ->
+                               [{Jid, none} | Acc];
+                           Subs ->
+                               lists:foldl(fun ({Sub, SubId}, Acc2) ->
+                                           [{Jid, Sub, SubId} | Acc2]
+                                   end,
+                                   Acc, Subs)
+                       end
+               end,
+               [], RItems);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
--spec(get_subscriptions/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: ljid())
-    -> {result, [{mod_pubsub:subscription(), mod_pubsub:subId()}]}
-).
-get_subscriptions(NodeIdx, Owner) ->
+get_subscriptions(Nidx, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     J = encode_jid(SubKey),
     Reply = case catch
-                  ejabberd_odbc:sql_query_t([<<"select subscriptions from pubsub_state "
-                                               "where nodeid='">>,
-                                             NodeIdx, <<"' and jid='">>, J,
-                                             <<"';">>])
-               of
-             {selected, [<<"subscriptions">>], [[S]]} ->
-                 decode_subscriptions(S);
-             _ -> []
-           end,
+       ejabberd_odbc:sql_query_t([<<"select subscriptions from pubsub_state where "
+                   "nodeid='">>, Nidx, <<"' and jid='">>, J, <<"';">>])
+    of
+       {selected, [<<"subscriptions">>], [[S]]} ->
+           decode_subscriptions(S);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
--spec(set_subscriptions/4 ::
-(
-  NodeIdx      :: mod_pubsub:nodeIdx(),
-  Owner        :: jid(),
-  Subscription :: mod_pubsub:subscription(),
-  SubId        :: mod_pubsub:subId())
-    -> _
-    %%%
-     | {error, xmlel()}
-).
-set_subscriptions(NodeIdx, Owner, Subscription, SubId) ->
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
     SubKey = jlib:jid_tolower(Owner),
-    SubState = get_state_without_itemids(NodeIdx, SubKey),
+    SubState = get_state_without_itemids(Nidx, SubKey),
     case {SubId, SubState#pubsub_state.subscriptions} of
-      {_, []} ->
-         case Subscription of
-           none ->
-               {error,
-                ?ERR_EXTENDED((?ERR_BAD_REQUEST),
-                              <<"not-subscribed">>)};
-           _ ->
-               new_subscription(NodeIdx, Owner, Subscription, SubState)
-         end;
-      {<<"">>, [{_, SID}]} ->
-         case Subscription of
-           none -> unsub_with_subid(NodeIdx, SID, SubState);
-           _ -> replace_subscription({Subscription, SID}, SubState)
-         end;
-      {<<"">>, [_ | _]} ->
-         {error,
-          ?ERR_EXTENDED((?ERR_BAD_REQUEST),
-                        <<"subid-required">>)};
-      _ ->
-         case Subscription of
-           none -> unsub_with_subid(NodeIdx, SubId, SubState);
-           _ ->
-               replace_subscription({Subscription, SubId}, SubState)
-         end
+       {_, []} ->
+           case Subscription of
+               none ->
+                   {error,
+                       ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"not-subscribed">>)};
+               _ ->
+                   new_subscription(Nidx, Owner, Subscription, SubState)
+           end;
+       {<<>>, [{_, SID}]} ->
+           case Subscription of
+               none -> unsub_with_subid(Nidx, SID, SubState);
+               _ -> replace_subscription({Subscription, SID}, SubState)
+           end;
+       {<<>>, [_ | _]} ->
+           {error,
+               ?ERR_EXTENDED((?ERR_BAD_REQUEST), <<"subid-required">>)};
+       _ ->
+           case Subscription of
+               none -> unsub_with_subid(Nidx, SubId, SubState);
+               _ -> replace_subscription({Subscription, SubId}, SubState)
+           end
     end.
 
--spec(replace_subscription/2 ::
-(
-  NewSub :: {mod_pubsub:subscription(), mod_pubsub:subId()},
-  SubState :: mod_pubsub:pubsubState())
-    -> {result, []}
-).
 replace_subscription(NewSub, SubState) ->
     NewSubs = replace_subscription(NewSub, SubState#pubsub_state.subscriptions, []),
     set_state(SubState#pubsub_state{subscriptions = NewSubs}).
 
 replace_subscription(_, [], Acc) -> Acc;
-replace_subscription({Sub, SubId}, [{_, SubID} | T], Acc) ->
-    replace_subscription({Sub, SubId}, T, [{Sub, SubID} | Acc]).
-
--spec(new_subscription/4 ::
-(
-  NodeIdx      :: mod_pubsub:nodeIdx(),
-  Owner        :: jid(),
-  Subscription :: mod_pubsub:subscription(),
-  SubState     :: mod_pubsub:pubsubState())
-    -> {mod_pubsub:subscription(), mod_pubsub:subId()}
-    %%%
-     | {error, xmlel()}
-).
-
-new_subscription(NodeIdx, Owner, Subscription, SubState) ->
-    case pubsub_subscription_odbc:subscribe_node(Owner, NodeIdx, []) of
-      {result, SubId} ->
-         Subscriptions = SubState#pubsub_state.subscriptions,
-         set_state(SubState#pubsub_state{subscriptions =
-                                             [{Subscription, SubId} | Subscriptions]}),
-         {Subscription, SubId};
-      _ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
-    end.
-
--spec(unsub_with_subid/3 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  SubId   :: mod_pubsub:subId(),
-  SubState :: mod_pubsub:pubsubState())
-    -> ok
-).
-unsub_with_subid(NodeIdx, SubId, SubState) ->
-    pubsub_subscription_odbc:unsubscribe_node(SubState#pubsub_state.stateid,
-                                             NodeIdx, SubId),
-    NewSubs = lists:filter(fun ({_, SID}) -> SubId =/= SID
-                          end,
-                          SubState#pubsub_state.subscriptions),
+replace_subscription({Sub, SubId}, [{_, SubId} | T], Acc) ->
+    replace_subscription({Sub, SubId}, T, [{Sub, SubId} | Acc]).
+
+new_subscription(Nidx, Owner, Subscription, SubState) ->
+    {result, SubId} = pubsub_subscription_odbc:subscribe_node(Owner, Nidx, []),
+    Subscriptions = [{Subscription, SubId} | SubState#pubsub_state.subscriptions],
+    set_state(SubState#pubsub_state{subscriptions = Subscriptions}),
+    {Subscription, SubId}.
+
+unsub_with_subid(Nidx, SubId, SubState) ->
+    pubsub_subscription_odbc:unsubscribe_node(SubState#pubsub_state.stateid, Nidx, SubId),
+    NewSubs = [{S, Sid}
+           || {S, Sid} <- SubState#pubsub_state.subscriptions,
+               SubId =/= Sid],
     case {NewSubs, SubState#pubsub_state.affiliation} of
-      {[], none} ->
-         del_state(NodeIdx,
-                   element(1, SubState#pubsub_state.stateid));
-      _ ->
-         set_state(SubState#pubsub_state{subscriptions = NewSubs})
+       {[], none} -> del_state(Nidx, element(1, SubState#pubsub_state.stateid));
+       _ -> set_state(SubState#pubsub_state{subscriptions = NewSubs})
     end.
 
-%% @spec (Host, Owner) -> {result, [Node]} | {error, Reason}
-%%       Host = host()
-%%       Owner = jid()
-%%       Node = pubsubNode()
 %% @doc <p>Returns a list of Owner's nodes on Host with pending
 %% subscriptions.</p>
--spec(get_pending_nodes/2 ::
-(
-  Host  :: mod_pubsub:hostPubsub(),
-  Owner :: jid())
-    -> {result, [mod_pubsub:nodeId()]}
-).
 get_pending_nodes(Host, Owner) ->
     GenKey = jlib:jid_remove_resource(jlib:jid_tolower(Owner)),
-    States = mnesia:match_object(#pubsub_state{stateid =
-                                                  {GenKey, '_'},
-                                              affiliation = owner, _ = '_'}),
-    NodeIDs = [ID
-              || #pubsub_state{stateid = {_, ID}} <- States],
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(Host, config),
-                                nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree_odbc
-              end,
-    Reply = mnesia:foldl(fun (#pubsub_state{stateid =
-                                               {_, NID}} =
-                                 S,
-                             Acc) ->
-                                case lists:member(NID, NodeIDs) of
-                                  true ->
-                                      case get_nodes_helper(NodeTree, S) of
-                                        {value, Node} -> [Node | Acc];
-                                        false -> Acc
-                                      end;
-                                  false -> Acc
-                                end
-                        end,
-                        [], pubsub_state),
+    States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'},
+               affiliation = owner, _ = '_'}),
+    Nidxxs = [Nidx || #pubsub_state{stateid = {_, Nidx}} <- States],
+    NodeTree = mod_pubsub:tree(Host),
+    Reply = mnesia:foldl(fun (#pubsub_state{stateid = {_, Nidx}} = S, Acc) ->
+                   case lists:member(Nidx, Nidxxs) of
+                       true ->
+                           case get_nodes_helper(NodeTree, S) of
+                               {value, Node} -> [Node | Acc];
+                               false -> Acc
+                           end;
+                       false ->
+                           Acc
+                   end
+           end,
+           [], pubsub_state),
     {result, Reply}.
 
-get_nodes_helper(NodeTree,
-                #pubsub_state{stateid = {_, N},
-                              subscriptions = Subs}) ->
-    HasPending = fun ({pending, _}) -> true;
-                    (pending) -> true;
-                    (_) -> false
-                end,
+get_nodes_helper(NodeTree, #pubsub_state{stateid = {_, N}, subscriptions = Subs}) ->
+    HasPending = fun
+       ({pending, _}) -> true;
+       (pending) -> true;
+       (_) -> false
+    end,
     case lists:any(HasPending, Subs) of
-      true ->
-         case NodeTree:get_node(N) of
-           #pubsub_node{nodeid = {_, Node}} -> {value, Node};
-           _ -> false
-         end;
-      false -> false
+       true ->
+           case NodeTree:get_node(N) of
+               #pubsub_node{nodeid = {_, Node}} -> {value, Node};
+               _ -> false
+           end;
+       false ->
+           false
     end.
 
-%% @spec (NodeId) -> [States] | []
-%%      NodeId = mod_pubsub:pubsubNodeId()
 %% @doc Returns the list of stored states for a given node.
 %% <p>For the default PubSub module, states are stored in Mnesia database.</p>
 %% <p>We can consider that the pubsub_state table have been created by the main
@@ -1080,127 +669,84 @@ get_nodes_helper(NodeTree,
 %% relational database).</p>
 %% <p>If a PubSub plugin wants to delegate the states storage to the default node,
 %% they can implement this function like this:
-%% ```get_states(NodeId) ->
-%%        node_default:get_states(NodeId).'''</p>
--spec(get_states/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [mod_pubsub:pubsubState()]}
-).
-get_states(NodeIdx) ->
+%% ```get_states(Nidx) ->
+%%           node_default:get_states(Nidx).'''</p>
+get_states(Nidx) ->
     case catch
-          ejabberd_odbc:sql_query_t([<<"select jid, affiliation, subscriptions "
-                                       "from pubsub_state where nodeid='">>,
-                                     NodeIdx, <<"';">>])
-       of
-      {selected,
-       [<<"jid">>, <<"affiliation">>, <<"subscriptions">>],
-       RItems} ->
-         {result,
-          lists:map(fun ([SJID, Affiliation, Subscriptions]) ->
-                            #pubsub_state{stateid = {decode_jid(SJID), NodeIdx},
-                                          items = itemids(NodeIdx, SJID),
-                                          affiliation = decode_affiliation(Affiliation),
-                                          subscriptions = decode_subscriptions(Subscriptions)}
-                    end,
-                    RItems)};
-      _ -> {result, []}
+       ejabberd_odbc:sql_query_t([<<"select jid, affiliation, subscriptions "
+                   "from pubsub_state where nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected,
+                   [<<"jid">>, <<"affiliation">>, <<"subscriptions">>], RItems} ->
+           {result,
+               lists:map(fun ([SJID, Aff, Subs]) ->
+                           #pubsub_state{stateid = {decode_jid(SJID), Nidx},
+                               items = itemids(Nidx, SJID),
+                               affiliation = decode_affiliation(Aff),
+                               subscriptions = decode_subscriptions(Subs)}
+                   end,
+                   RItems)};
+       _ ->
+           {result, []}
     end.
 
-%% @spec (NodeId, JID) -> [State] | []
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      JID = mod_pubsub:jid()
-%%      State = mod_pubsub:pubsubItems()
 %% @doc <p>Returns a state (one state list), given its reference.</p>
-
--spec(get_state/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  JID     :: ljid())
-    -> mod_pubsub:pubsubState()
-).
-get_state(NodeIdx, JID) ->
-    State = get_state_without_itemids(NodeIdx, JID),
+get_state(Nidx, JID) ->
+    State = get_state_without_itemids(Nidx, JID),
     {SJID, _} = State#pubsub_state.stateid,
-    State#pubsub_state{items = itemids(NodeIdx, SJID)}.
+    State#pubsub_state{items = itemids(Nidx, SJID)}.
 
 -spec(get_state_without_itemids/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  JID     :: jid())
-    -> mod_pubsub:pubsubState()
-).
-get_state_without_itemids(NodeIdx, JID) ->
+    (Nidx :: mod_pubsub:nodeIdx(),
+       Key :: ljid()) ->
+    mod_pubsub:pubsubState()
+    ).
+get_state_without_itemids(Nidx, JID) ->
     J = encode_jid(JID),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select jid, affiliation, subscriptions "
-                                       "from pubsub_state where jid='">>,
-                                     J, <<"' and nodeid='">>, NodeIdx,
-                                     <<"';">>])
-       of
-      {selected,
-       [<<"jid">>, <<"affiliation">>, <<"subscriptions">>],
-       [[SJID, Affiliation, Subscriptions]]} ->
-         #pubsub_state{stateid = {decode_jid(SJID), NodeIdx},
-                       affiliation = decode_affiliation(Affiliation),
-                       subscriptions = decode_subscriptions(Subscriptions)};
-      _ -> #pubsub_state{stateid = {JID, NodeIdx}}
+       ejabberd_odbc:sql_query_t([<<"select jid, affiliation, subscriptions "
+                   "from pubsub_state where jid='">>, J, <<"' and nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected,
+                   [<<"jid">>, <<"affiliation">>, <<"subscriptions">>], [[SJID, Aff, Subs]]} ->
+           #pubsub_state{stateid = {decode_jid(SJID), Nidx},
+               affiliation = decode_affiliation(Aff),
+               subscriptions = decode_subscriptions(Subs)};
+       _ ->
+           #pubsub_state{stateid = {JID, Nidx}}
     end.
 
-%% @spec (State) -> ok | {error, Reason::stanzaError()}
-%%      State = mod_pubsub:pubsubStates()
 %% @doc <p>Write a state into database.</p>
-
--spec(set_state/1 ::
-(
-  State :: mod_pubsub:pubsubState())
-    -> {result, []}
-).
 set_state(State) ->
-    {_, NodeIdx} = State#pubsub_state.stateid,
-    set_state(NodeIdx, State).
-
--spec(set_state/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  State   :: mod_pubsub:pubsubState())
-    -> {result, []}
-).
-set_state(NodeIdx, State) ->
+    {_, Nidx} = State#pubsub_state.stateid,
+    set_state(Nidx, State).
+
+set_state(Nidx, State) ->
     {JID, _} = State#pubsub_state.stateid,
     J = encode_jid(JID),
     S = encode_subscriptions(State#pubsub_state.subscriptions),
     A = encode_affiliation(State#pubsub_state.affiliation),
     case catch
-          ejabberd_odbc:sql_query_t([<<"update pubsub_state set subscriptions='">>,
-                                     S, <<"', affiliation='">>, A,
-                                     <<"' where nodeid='">>, NodeIdx,
-                                     <<"' and jid='">>, J, <<"';">>])
-       of
-      {updated, 1} -> ok;
-      _ ->
-         catch
-           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, "
-                                        "affiliation, subscriptions) values('">>,
-                                      NodeIdx, <<"', '">>, J, <<"', '">>, A,
-                                      <<"', '">>, S, <<"');">>])
+       ejabberd_odbc:sql_query_t([<<"update pubsub_state set subscriptions='">>, S, <<"', affiliation='">>, A,
+               <<"' where nodeid='">>, Nidx, <<"' and jid='">>, J, <<"';">>])
+    of
+       {updated, 1} ->
+           ok;
+       _ ->
+           catch
+           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, affiliation, subscriptions) "
+                       "values('">>,
+                   Nidx, <<"', '">>, J, <<"', '">>, A, <<"', '">>, S, <<"');">>])
     end,
-    {result, []}.
+    ok.
 
-%% @spec (NodeId, JID) -> ok | {error, Reason::stanzaError()}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      JID = mod_pubsub:jid()
 %% @doc <p>Delete a state from database.</p>
-del_state(NodeId, JID) ->
+del_state(Nidx, JID) ->
     J = encode_jid(JID),
-    catch
-      ejabberd_odbc:sql_query_t([<<"delete from pubsub_state where jid='">>,
-                                J, <<"' and nodeid='">>, NodeId, <<"';">>]),
+    catch ejabberd_odbc:sql_query_t([<<"delete from pubsub_state where jid='">>,
+           J, <<"' and nodeid='">>, Nidx, <<"';">>]),
     ok.
 
-%% @spec (NodeId, From) -> {[Items],RsmOut} | []
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      Items = mod_pubsub:pubsubItems()
 %% @doc Returns the list of stored items for a given node.
 %% <p>For the default PubSub module, items are stored in Mnesia database.</p>
 %% <p>We can consider that the pubsub_item table have been created by the main
@@ -1209,464 +755,439 @@ del_state(NodeId, JID) ->
 %% relational database), or they can even decide not to persist any items.</p>
 %% <p>If a PubSub plugin wants to delegate the item storage to the default node,
 %% they can implement this function like this:
-%% ```get_items(NodeId, From) ->
-%%        node_default:get_items(NodeId, From).'''</p>
-get_items(NodeId, _From) ->
+%% ```get_items(Nidx, From) ->
+%%           node_default:get_items(Nidx, From).'''</p>
+get_items(Nidx, _From) ->
     case catch
-          ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, "
-                                       "modification, payload from pubsub_item "
-                                       "where nodeid='">>,
-                                     NodeId,
-                                     <<"' order by modification desc;">>])
-       of
-      {selected,
-       [<<"itemid">>, <<"publisher">>, <<"creation">>,
-       <<"modification">>, <<"payload">>],
-       RItems} ->
-         {result,
-          lists:map(fun (RItem) -> raw_to_item(NodeId, RItem) end,
-                    RItems)};
-      _ -> {result, []}
+       ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, modification, payload "
+                   "from pubsub_item where nodeid='">>, Nidx,
+               <<"' order by modification desc;">>])
+    of
+       {selected,
+                   [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} ->
+           {result, [raw_to_item(Nidx, RItem) || RItem <- RItems]};
+       _ ->
+           {result, []}
     end.
 
-get_items(NodeId, From, none) ->
+get_items(Nidx, From, none) ->
     MaxItems = case catch
-                     ejabberd_odbc:sql_query_t([<<"select val from pubsub_node_option where "
-                                                  "nodeid='">>,
-                                                NodeId,
-                                                <<"' and name='max_items';">>])
-                  of
-                {selected, [<<"val">>], [[Value]]} ->
-                    Tokens = element(2,
-                               erl_scan:string(binary_to_list(<<Value/binary, ".">>))),
-                    element(2, erl_parse:parse_term(Tokens));
-                _ -> ?MAXITEMS
-              end,
-    get_items(NodeId, From, #rsm_in{max = MaxItems});
-get_items(NodeId, _From,
-         #rsm_in{max = M, direction = Direction, id = I,
-                 index = IncIndex}) ->
-    Max = (?PUBSUB):escape(jlib:i2l(M)),
+       ejabberd_odbc:sql_query_t([<<"select val from pubsub_node_option "
+                   "where nodeid='">>, Nidx, <<"' and name='max_items';">>])
+    of
+       {selected, [<<"val">>], [[Value]]} ->
+           Tokens = element(2, erl_scan:string(binary_to_list(<<Value/binary, ".">>))),
+           element(2, erl_parse:parse_term(Tokens));
+       _ ->
+           ?MAXITEMS
+    end,
+    get_items(Nidx, From, #rsm_in{max = MaxItems});
+get_items(Nidx, _From,
+           #rsm_in{max = M, direction = Direction, id = I, index = IncIndex}) ->
+    Max = ejabberd_odbc:escape(jlib:i2l(M)),
     {Way, Order} = case Direction of
-                    aft -> {<<"<">>, <<"desc">>};
-                    before when I == <<>> -> {<<"is not">>, <<"asc">>};
-                    before -> {<<">">>, <<"asc">>};
-                    _ when IncIndex =/= undefined ->
-                        {<<"<">>, <<"desc">>}; % using index
-                    _ -> {<<"is not">>, <<"desc">>}% Can be better
-                  end,
+       %     aft -> {<<"<">>, <<"desc">>};
+       %     before when I == <<>> -> {<<"is not">>, <<"asc">>};
+       %     before -> {<<">">>, <<"asc">>};
+       %     _ when IncIndex =/= undefined ->
+       %         {<<"<">>, <<"desc">>}; % using index
+       _ ->
+           {<<"is not">>, <<"desc">>}% Can be better
+    end,
     [AttrName, Id] = case I of
-                      undefined when IncIndex =/= undefined ->
-                          case catch
-                                 ejabberd_odbc:sql_query_t([<<"select modification from pubsub_item "
-                                                              "pi where exists ( select count(*) as "
-                                                              "count1 from pubsub_item where nodeid='">>,
-                                                            NodeId,
-                                                            <<"' and modification > pi.modification "
-                                                              "having count1 = ">>,
-                                                            (?PUBSUB):escape(jlib:i2l(IncIndex)),
-                                                            <<" );">>])
-                              of
-                            {selected, [_], [[O]]} ->
-                                [<<"modification">>, <<"'", O/binary, "'">>];
-                            _ -> [<<"modification">>, <<"null">>]
-                          end;
-                      undefined -> [<<"modification">>, <<"null">>];
-                      <<>> -> [<<"modification">>, <<"null">>];
-                      I ->
-                          [A, B] = str:tokens((?PUBSUB):escape(jlib:i2l(I)),
-                                              <<"@">>),
-                          [A, <<"'", B/binary, "'">>]
-                    end,
+       undefined when IncIndex =/= undefined ->
+           case catch
+               ejabberd_odbc:sql_query_t([<<"select modification from pubsub_item pi "
+                           "where exists ( select count(*) as count1 "
+                           "from pubsub_item where nodeid='">>, Nidx,
+                       <<"' and modification > pi.modification having count1 = ">>,
+                       ejabberd_odbc:escape(jlib:i2l(IncIndex)), <<" );">>])
+           of
+               {selected, [_], [[O]]} ->
+                   [<<"modification">>, <<"'", O/binary, "'">>];
+               _ ->
+                   [<<"modification">>, <<"null">>]
+           end;
+       undefined ->
+           [<<"modification">>, <<"null">>];
+       <<>> ->
+           [<<"modification">>, <<"null">>];
+       I ->
+           [A, B] = str:tokens(ejabberd_odbc:escape(jlib:i2l(I)), <<"@">>),
+           [A, <<"'", B/binary, "'">>]
+    end,
     Count = case catch
-                  ejabberd_odbc:sql_query_t([<<"select count(*) from pubsub_item where "
-                                               "nodeid='">>,
-                                             NodeId, <<"';">>])
-               of
-             {selected, [_], [[C]]} -> C;
-             _ -> <<"0">>
-           end,
+       ejabberd_odbc:sql_query_t([<<"select count(*) from pubsub_item where nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected, [_], [[C]]} -> C;
+       _ -> <<"0">>
+    end,
     case catch
-          ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, "
-                                       "modification, payload from pubsub_item "
-                                       "where nodeid='">>,
-                                     NodeId, <<"' and ">>, AttrName, <<" ">>,
-                                     Way, <<" ">>, Id, <<" order by ">>,
-                                     AttrName, <<" ">>, Order, <<" limit ">>,
-                                     jlib:i2l(Max), <<" ;">>])
-       of
-      {selected,
-       [<<"itemid">>, <<"publisher">>, <<"creation">>,
-       <<"modification">>, <<"payload">>],
-       RItems} ->
-         case RItems of
-           [[_, _, _, F, _]|_] ->
-               Index = case catch
-                              ejabberd_odbc:sql_query_t([<<"select count(*) from pubsub_item where "
-                                                           "nodeid='">>,
-                                                         NodeId, <<"' and ">>,
-                                                         AttrName, <<" > '">>,
-                                                         F, <<"';">>])
-                           of
-                         %{selected, [_], [{C}, {In}]} -> [string:strip(C, both, $"), string:strip(In, both, $")];
-                         {selected, [_], [[In]]} -> In;
-                         _ -> <<"0">>
-                       end,
-               [_, _, _, L, _] = lists:last(RItems),
-               RsmOut = #rsm_out{count = Count, index = Index,
-                                 first = <<"modification@", F/binary>>,
-                                 last = <<"modification@", (jlib:i2l(L))/binary>>},
-               {result, {[raw_to_item(NodeId, RItem) || RItem <- RItems], RsmOut}};
-           [] -> {result, {[], #rsm_out{count = Count}}};
-           0 -> {result, {[], #rsm_out{count = Count}}}
-         end;
-      _ -> {result, {[], none}}
+       ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, modification, payload "
+                   "from pubsub_item where nodeid='">>, Nidx,
+               <<"' and ">>, AttrName, <<" ">>, Way, <<" ">>, Id, <<" order by ">>,
+               AttrName, <<" ">>, Order, <<" limit ">>, jlib:i2l(Max), <<" ;">>])
+    of
+       {selected,
+                   [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} ->
+           case RItems of
+               [[_, _, _, F, _]|_] ->
+                   Index = case catch
+                       ejabberd_odbc:sql_query_t([<<"select count(*) from pubsub_item "
+                                   "where nodeid='">>, Nidx, <<"' and ">>,
+                               AttrName, <<" > '">>, F, <<"';">>])
+                   of
+                       %{selected, [_], [{C}, {In}]} -> [string:strip(C, both, $"), string:strip(In, both, $")];
+                       {selected, [_], [[In]]} -> In;
+                       _ -> <<"0">>
+                   end,
+                   [_, _, _, L, _] = lists:last(RItems),
+                   RsmOut = #rsm_out{count = Count, index = Index,
+                           first = <<"modification@", F/binary>>,
+                           last = <<"modification@", (jlib:i2l(L))/binary>>},
+                   {result, {[raw_to_item(Nidx, RItem) || RItem <- RItems], RsmOut}};
+               [] ->
+                   {result, {[], #rsm_out{count = Count}}}
+           end;
+       _ ->
+           {result, {[], none}}
     end.
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    get_items(NodeId, JID, AccessModel,
-             PresenceSubscription, RosterGroup, SubId, none).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, none).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, _SubId, RSM) ->
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId, RSM) ->
     SubKey = jlib:jid_tolower(JID),
     GenKey = jlib:jid_remove_resource(SubKey),
-    {Affiliation, Subscriptions} =
-       select_affiliation_subscriptions(NodeId, GenKey,
-                                        SubKey),
-    Whitelisted = can_fetch_item(Affiliation,
-                                Subscriptions),
+    {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey),
+    Whitelisted = can_fetch_item(Affiliation, Subscriptions),
     if %%SubId == "", ?? ->
-       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-       %%InvalidSubId ->
-       %% Entity is subscribed but specifies an invalid subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-       Affiliation == outcast -> {error, ?ERR_FORBIDDEN};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       (AccessModel == authorize) and not Whitelisted ->
-          {error, ?ERR_FORBIDDEN};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       true -> get_items(NodeId, JID, RSM)
+       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %%InvalidSubId ->
+       %% Entity is subscribed but specifies an invalid subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       (AccessModel == authorize) and not Whitelisted ->
+           {error, ?ERR_FORBIDDEN};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       true ->
+           get_items(Nidx, JID, RSM)
     end.
 
-get_last_items(NodeId, _From, Count) ->
+get_last_items(Nidx, _From, Count) ->
     case catch
-          ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, "
-                                       "modification, payload from pubsub_item "
-                                       "where nodeid='">>,
-                                     NodeId,
-                                     <<"' order by modification desc limit ">>,
-                                     jlib:i2l(Count), <<";">>])
-       of
-      {selected,
-       [<<"itemid">>, <<"publisher">>, <<"creation">>,
-       <<"modification">>, <<"payload">>],
-       RItems} ->
-         {result,
-          lists:map(fun (RItem) -> raw_to_item(NodeId, RItem) end,
-                    RItems)};
-      _ -> {result, []}
+       ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, modification, payload "
+                   "from pubsub_item where nodeid='">>, Nidx,
+               <<"' order by modification desc limit ">>, jlib:i2l(Count), <<";">>])
+    of
+       {selected,
+                   [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], RItems} ->
+           {result, [raw_to_item(Nidx, RItem) || RItem <- RItems]};
+       _ ->
+           {result, []}
     end.
 
-%% @spec (NodeId, ItemId) -> [Item] | []
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      ItemId = string()
-%%      Item = mod_pubsub:pubsubItems()
 %% @doc <p>Returns an item (one item list), given its reference.</p>
-get_item(NodeId, ItemId) ->
-    I = (?PUBSUB):escape(ItemId),
+get_item(Nidx, ItemId) ->
+    I = ejabberd_odbc:escape(ItemId),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, "
-                                       "modification, payload from pubsub_item "
-                                       "where nodeid='">>,
-                                     NodeId, <<"' and itemid='">>, I,
-                                     <<"';">>])
-       of
-      {selected,
-       [<<"itemid">>, <<"publisher">>, <<"creation">>,
-       <<"modification">>, <<"payload">>],
-       [RItem]} ->
-         {result, raw_to_item(NodeId, RItem)};
-      _ -> {error, ?ERR_ITEM_NOT_FOUND}
+       ejabberd_odbc:sql_query_t([<<"select itemid, publisher, creation, "
+                   "modification, payload from pubsub_item "
+                   "where nodeid='">>, Nidx, <<"' and itemid='">>, I, <<"';">>])
+    of
+       {selected,
+                   [<<"itemid">>, <<"publisher">>, <<"creation">>, <<"modification">>, <<"payload">>], [RItem]} ->
+           {result, raw_to_item(Nidx, RItem)};
+       _ ->
+           {error, ?ERR_ITEM_NOT_FOUND}
     end.
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, _SubId) ->
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
     SubKey = jlib:jid_tolower(JID),
     GenKey = jlib:jid_remove_resource(SubKey),
-    {Affiliation, Subscriptions} =
-       select_affiliation_subscriptions(NodeId, GenKey,
-                                        SubKey),
-    Whitelisted = can_fetch_item(Affiliation,
-                                Subscriptions),
+    {Affiliation, Subscriptions} = select_affiliation_subscriptions(Nidx, GenKey, SubKey),
+    Whitelisted = can_fetch_item(Affiliation, Subscriptions),
     if %%SubId == "", ?? ->
-       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
-       %%InvalidSubId ->
-       %% Entity is subscribed but specifies an invalid subscription ID
-       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
-       Affiliation == outcast -> {error, ?ERR_FORBIDDEN};
-       (AccessModel == presence) and
-        not PresenceSubscription ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"presence-subscription-required">>)};
-       (AccessModel == roster) and not RosterGroup ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED),
-                         <<"not-in-roster-group">>)};
-       (AccessModel == whitelist) and not Whitelisted ->
-          {error,
-           ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
-       (AccessModel == authorize) and not Whitelisted ->
-          {error, ?ERR_FORBIDDEN};
-       %%MustPay ->
-       %%      % Payment is required for a subscription
-       %%      {error, ?ERR_PAYMENT_REQUIRED};
-       true -> get_item(NodeId, ItemId)
+       %% Entity has multiple subscriptions to the node but does not specify a subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_BAD_REQUEST, "subid-required")};
+       %%InvalidSubId ->
+       %% Entity is subscribed but specifies an invalid subscription ID
+       %{error, ?ERR_EXTENDED(?ERR_NOT_ACCEPTABLE, "invalid-subid")};
+       Affiliation == outcast ->
+           {error, ?ERR_FORBIDDEN};
+       (AccessModel == presence) and not PresenceSubscription ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"presence-subscription-required">>)};
+       (AccessModel == roster) and not RosterGroup ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_AUTHORIZED), <<"not-in-roster-group">>)};
+       (AccessModel == whitelist) and not Whitelisted ->
+           {error,
+               ?ERR_EXTENDED((?ERR_NOT_ALLOWED), <<"closed-node">>)};
+       (AccessModel == authorize) and not Whitelisted ->
+           {error, ?ERR_FORBIDDEN};
+       %%MustPay ->
+       %%        % Payment is required for a subscription
+       %%        {error, ?ERR_PAYMENT_REQUIRED};
+       true ->
+           get_item(Nidx, ItemId)
     end.
 
-%% @spec (Item) -> ok | {error, Reason::stanzaError()}
-%%      Item = mod_pubsub:pubsubItems()
 %% @doc <p>Write an item into database.</p>
 set_item(Item) ->
-    {ItemId, NodeId} = Item#pubsub_item.itemid,
-    I = (?PUBSUB):escape(ItemId),
+    {ItemId, Nidx} = Item#pubsub_item.itemid,
+    I = ejabberd_odbc:escape(ItemId),
     {C, _} = Item#pubsub_item.creation,
     {M, JID} = Item#pubsub_item.modification,
     P = encode_jid(JID),
     Payload = Item#pubsub_item.payload,
-    XML = (?PUBSUB):escape(str:join([xml:element_to_binary(X) || X<-Payload], <<>>)),
+    XML = ejabberd_odbc:escape(str:join([xml:element_to_binary(X) || X<-Payload], <<>>)),
     S = fun ({T1, T2, T3}) ->
-               str:join([jlib:i2l(T1, 6), jlib:i2l(T2, 6), jlib:i2l(T3, 6)], <<":">>)
-       end,
+           str:join([jlib:i2l(T1, 6), jlib:i2l(T2, 6), jlib:i2l(T3, 6)], <<":">>)
+    end,
     case catch
-          ejabberd_odbc:sql_query_t([<<"update pubsub_item set publisher='">>,
-                                     P, <<"', modification='">>, S(M),
-                                     <<"', payload='">>, XML,
-                                     <<"' where nodeid='">>, NodeId,
-                                     <<"' and itemid='">>, I, <<"';">>])
-       of
-      {updated, 1} -> ok;
-      _ ->
-         catch
+       ejabberd_odbc:sql_query_t([<<"update pubsub_item set publisher='">>, P,
+               <<"', modification='">>, S(M),
+               <<"', payload='">>, XML,
+               <<"' where nodeid='">>, Nidx, <<"' and itemid='">>, I, <<"';">>])
+    of
+       {updated, 1} ->
+           ok;
+       _ ->
+           catch
            ejabberd_odbc:sql_query_t([<<"insert into pubsub_item (nodeid, itemid, "
-                                        "publisher, creation, modification, payload) "
-                                        "values('">>,
-                                      NodeId, <<"', '">>, I, <<"', '">>, P,
-                                      <<"', '">>, S(C), <<"', '">>, S(M),
-                                      <<"', '">>, XML, <<"');">>])
+                       "publisher, creation, modification, payload) "
+                       "values('">>, Nidx, <<"', '">>, I, <<"', '">>, P,
+                   <<"', '">>, S(C), <<"', '">>, S(M),
+                   <<"', '">>, XML, <<"');">>])
     end,
-    {result, []}.
+    ok.
 
-%% @spec (NodeId, ItemId) -> ok | {error, Reason::stanzaError()}
-%%      NodeId = mod_pubsub:pubsubNodeId()
-%%      ItemId = string()
 %% @doc <p>Delete an item from database.</p>
-del_item(NodeId, ItemId) ->
-    I = (?PUBSUB):escape(ItemId),
-    catch
-      ejabberd_odbc:sql_query_t([<<"delete from pubsub_item where itemid='">>,
-                                I, <<"' and nodeid='">>, NodeId, <<"';">>]).
-
-del_items(_, []) -> ok;
-del_items(NodeId, [ItemId]) -> del_item(NodeId, ItemId);
-del_items(NodeId, ItemIds) ->
-    I = str:join([[<<"'">>, (?PUBSUB):escape(X), <<"'">>]
-                 || X <- ItemIds],
-                <<",">>),
+del_item(Nidx, ItemId) ->
+    I = ejabberd_odbc:escape(ItemId),
+    catch ejabberd_odbc:sql_query_t([<<"delete from pubsub_item where itemid='">>,
+           I, <<"' and nodeid='">>, Nidx, <<"';">>]).
+
+del_items(_, []) ->
+    ok;
+del_items(Nidx, [ItemId]) ->
+    del_item(Nidx, ItemId);
+del_items(Nidx, ItemIds) ->
+    I = str:join([[<<"'">>, ejabberd_odbc:escape(X), <<"'">>] || X <- ItemIds], <<",">>),
     catch
-      ejabberd_odbc:sql_query_t([<<"delete from pubsub_item where itemid "
-                                  "in (">>,
-                                I, <<") and nodeid='">>, NodeId, <<"';">>]).
+    ejabberd_odbc:sql_query_t([<<"delete from pubsub_item where itemid in (">>,
+           I, <<") and nodeid='">>, Nidx, <<"';">>]).
 
-get_item_name(_Host, _Node, Id) -> Id.
+get_item_name(_Host, _Node, Id) ->
+    Id.
 
-node_to_path(Node) -> str:tokens((Node), <<"/">>).
+node_to_path(Node) ->
+    node_hometree:node_to_path(Node).
 
-path_to_node([]) -> <<>>;
 path_to_node(Path) ->
-    iolist_to_binary(str:join([<<"">> | Path], <<"/">>)).
+    node_hometree:path_to_node(Path).
 
-%% @spec (Affiliation, Subscription) -> true | false
-%%       Affiliation = owner | member | publisher | outcast | none
-%%       Subscription = subscribed | none
-%% @doc Determines if the combination of Affiliation and Subscribed
-%% are allowed to get items from a node.
 can_fetch_item(owner, _) -> true;
 can_fetch_item(member, _) -> true;
 can_fetch_item(publisher, _) -> true;
 can_fetch_item(outcast, _) -> false;
 can_fetch_item(none, Subscriptions) -> is_subscribed(Subscriptions).
+%can_fetch_item(_Affiliation, _Subscription) -> false.
 
 is_subscribed(Subscriptions) ->
-    lists:any(fun ({subscribed, _SubId}) -> true;
-                 (_) -> false
-             end,
-             Subscriptions).
+    lists:any(fun
+           ({subscribed, _SubId}) -> true;
+           (_) -> false
+       end,
+       Subscriptions).
 
-%% Returns the first item where Pred() is true in List
-first_in_list(_Pred, []) -> false;
+first_in_list(_Pred, []) ->
+    false;
 first_in_list(Pred, [H | T]) ->
     case Pred(H) of
-      true -> {value, H};
-      _ -> first_in_list(Pred, T)
+       true -> {value, H};
+       _ -> first_in_list(Pred, T)
     end.
 
-itemids(NodeId, {U, S, R}) ->
-    itemids(NodeId, encode_jid({U, S, R}));
-itemids(NodeId, SJID) ->
+itemids(Nidx, {U, S, R}) ->
+    itemids(Nidx, encode_jid({U, S, R}));
+itemids(Nidx, SJID) ->
     case catch
-          ejabberd_odbc:sql_query_t([<<"select itemid from pubsub_item where "
-                                       "nodeid='">>,
-                                     NodeId, <<"' and publisher like '">>,
-                                     SJID,
-                                     <<"%' order by modification desc;">>])
-       of
-      {selected, [<<"itemid">>], RItems} ->
-         lists:map(fun ([ItemId]) -> ItemId end, RItems);
-      _ -> []
+       ejabberd_odbc:sql_query_t([<<"select itemid from pubsub_item where "
+                   "nodeid='">>, Nidx, <<"' and publisher like '">>, SJID,
+               <<"%' order by modification desc;">>])
+    of
+       {selected, [<<"itemid">>], RItems} ->
+           [ItemId || [ItemId] <- RItems];
+       _ ->
+           []
     end.
 
-select_affiliation_subscriptions(NodeId, JID) ->
+select_affiliation_subscriptions(Nidx, JID) ->
     J = encode_jid(JID),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select affiliation,subscriptions from "
-                                       "pubsub_state where nodeid='">>,
-                                     NodeId, <<"' and jid='">>, J, <<"';">>])
-       of
-      {selected, [<<"affiliation">>, <<"subscriptions">>],
-       [[A, S]]} ->
-         {decode_affiliation(A), decode_subscriptions(S)};
-      _ -> {none, []}
+       ejabberd_odbc:sql_query_t([<<"select affiliation,subscriptions from "
+                   "pubsub_state where nodeid='">>,
+               Nidx, <<"' and jid='">>, J, <<"';">>])
+    of
+       {selected, [<<"affiliation">>, <<"subscriptions">>], [[A, S]]} ->
+           {decode_affiliation(A), decode_subscriptions(S)};
+       _ ->
+           {none, []}
     end.
 
-select_affiliation_subscriptions(NodeId, JID, JID) ->
-    select_affiliation_subscriptions(NodeId, JID);
-select_affiliation_subscriptions(NodeId, GenKey,
-                                SubKey) ->
-    {result, Affiliation} = get_affiliation(NodeId, GenKey),
-    {result, Subscriptions} = get_subscriptions(NodeId,
-                                               SubKey),
+select_affiliation_subscriptions(Nidx, JID, JID) ->
+    select_affiliation_subscriptions(Nidx, JID);
+select_affiliation_subscriptions(Nidx, GenKey, SubKey) ->
+    {result, Affiliation} = get_affiliation(Nidx, GenKey),
+    {result, Subscriptions} = get_subscriptions(Nidx, SubKey),
     {Affiliation, Subscriptions}.
 
-update_affiliation(NodeId, JID, Affiliation) ->
+update_affiliation(Nidx, JID, Affiliation) ->
     J = encode_jid(JID),
     A = encode_affiliation(Affiliation),
     case catch
-          ejabberd_odbc:sql_query_t([<<"update pubsub_state set affiliation='">>,
-                                     A, <<"' where nodeid='">>, NodeId,
-                                     <<"' and jid='">>, J, <<"';">>])
-       of
-      {updated, 1} -> ok;
-      _ ->
-         catch
-           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, "
-                                        "affiliation, subscriptions) values('">>,
-                                      NodeId, <<"', '">>, J, <<"', '">>, A,
-                                      <<"', '');">>])
+       ejabberd_odbc:sql_query_t([<<"update pubsub_state set affiliation='">>,
+               A, <<"' where nodeid='">>, Nidx,
+               <<"' and jid='">>, J, <<"';">>])
+    of
+       {updated, 1} ->
+           ok;
+       _ ->
+           catch
+           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, affiliation, subscriptions) "
+                       "values('">>, Nidx, <<"', '">>, J, <<"', '">>, A, <<"', '');">>])
     end.
 
-update_subscription(NodeId, JID, Subscription) ->
+update_subscription(Nidx, JID, Subscription) ->
     J = encode_jid(JID),
     S = encode_subscriptions(Subscription),
     case catch
-          ejabberd_odbc:sql_query_t([<<"update pubsub_state set subscriptions='">>,
-                                     S, <<"' where nodeid='">>, NodeId,
-                                     <<"' and jid='">>, J, <<"';">>])
-       of
-      {updated, 1} -> ok;
-      _ ->
-         catch
-           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, "
-                                        "affiliation, subscriptions) values('">>,
-                                      NodeId, <<"', '">>, J, <<"', 'n', '">>,
-                                      S, <<"');">>])
+       ejabberd_odbc:sql_query_t([<<"update pubsub_state set subscriptions='">>, S,
+               <<"' where nodeid='">>, Nidx, <<"' and jid='">>, J, <<"';">>])
+    of
+       {updated, 1} ->
+           ok;
+       _ ->
+           catch
+           ejabberd_odbc:sql_query_t([<<"insert into pubsub_state(nodeid, jid, affiliation, subscriptions) "
+                       "values('">>, Nidx, <<"', '">>, J, <<"', 'n', '">>, S, <<"');">>])
     end.
 
-decode_jid(JID) ->
-    jlib:jid_tolower(jlib:string_to_jid(JID)).
-
+-spec(decode_jid/1 ::
+    (   SJID :: binary())
+    -> ljid()
+    ).
+decode_jid(SJID) ->
+    jlib:jid_tolower(jlib:string_to_jid(SJID)).
+
+-spec(decode_affiliation/1 ::
+    (   Arg :: binary())
+    -> atom()
+    ).
 decode_affiliation(<<"o">>) -> owner;
 decode_affiliation(<<"p">>) -> publisher;
 decode_affiliation(<<"m">>) -> member;
 decode_affiliation(<<"c">>) -> outcast;
 decode_affiliation(_) -> none.
 
+-spec(decode_subscription/1 ::
+    (   Arg :: binary())
+    -> atom()
+    ).
 decode_subscription(<<"s">>) -> subscribed;
 decode_subscription(<<"p">>) -> pending;
 decode_subscription(<<"u">>) -> unconfigured;
 decode_subscription(_) -> none.
 
+-spec(decode_subscriptions/1 ::
+    (   Subscriptions :: binary())
+    -> [] | [{atom(), binary()},...]
+    ).
 decode_subscriptions(Subscriptions) ->
     lists:foldl(fun (Subscription, Acc) ->
-                       case str:tokens(Subscription, <<":">>) of
-                         [S, SubId] -> [{decode_subscription(S), SubId} | Acc];
-                         _ -> Acc
-                       end
-               end,
-               [], str:tokens(Subscriptions, <<",">>)).
+               case str:tokens(Subscription, <<":">>) of
+                   [S, SubId] -> [{decode_subscription(S), SubId} | Acc];
+                   _ -> Acc
+               end
+       end,
+       [], str:tokens(Subscriptions, <<",">>)).
 
+-spec(encode_jid/1 ::
+    (   JID :: ljid())
+    -> binary()
+    ).
 encode_jid(JID) ->
-    (?PUBSUB):escape(JID).
-
+    ejabberd_odbc:escape(jlib:jid_to_string(JID)).
+
+-spec(encode_host/1 ::
+    (   Host :: host())
+    -> binary()
+    ).
+encode_host({_U, _S, _R} = LJID) -> encode_jid(LJID);
+encode_host(Host) -> ejabberd_odbc:escape(Host).
+
+-spec(encode_affiliation/1 ::
+    (   Arg :: atom())
+    -> binary()
+    ).
 encode_affiliation(owner) -> <<"o">>;
 encode_affiliation(publisher) -> <<"p">>;
 encode_affiliation(member) -> <<"m">>;
 encode_affiliation(outcast) -> <<"c">>;
 encode_affiliation(_) -> <<"n">>.
 
+-spec(encode_subscription/1 ::
+    (   Arg :: atom())
+    -> binary()
+    ).
 encode_subscription(subscribed) -> <<"s">>;
 encode_subscription(pending) -> <<"p">>;
 encode_subscription(unconfigured) -> <<"u">>;
 encode_subscription(_) -> <<"n">>.
 
+-spec(encode_subscriptions/1 ::
+    (   Subscriptions :: [] | [{atom(), binary()},...])
+    -> binary()
+    ).
 encode_subscriptions(Subscriptions) ->
-    str:join(lists:map(fun ({S, SubId}) ->
-                              <<(encode_subscription(S))/binary, ":",
-                                SubId/binary>>
-                      end,
-                      Subscriptions),
-            <<",">>).
+    str:join([<<(encode_subscription(S))/binary, ":", SubId/binary>>
+           || {S, SubId} <- Subscriptions], <<",">>).
 
 %%% record getter/setter
 
-state_to_raw(NodeId, State) ->
+state_to_raw(Nidx, State) ->
     {JID, _} = State#pubsub_state.stateid,
     J = encode_jid(JID),
     A = encode_affiliation(State#pubsub_state.affiliation),
-    S =
-       encode_subscriptions(State#pubsub_state.subscriptions),
-    [<<"'">>, NodeId, <<"', '">>, J, <<"', '">>, A,
-     <<"', '">>, S, <<"'">>].
+    S = encode_subscriptions(State#pubsub_state.subscriptions),
+    [<<"'">>, Nidx, <<"', '">>, J, <<"', '">>, A, <<"', '">>, S, <<"'">>].
 
-raw_to_item(NodeId,
-           [ItemId, SJID, Creation, Modification, XML]) ->
+raw_to_item(Nidx, [ItemId, SJID, Creation, Modification, XML]) ->
     JID = decode_jid(SJID),
     ToTime = fun (Str) ->
-                    [T1, T2, T3] = str:tokens(Str, <<":">>),
-                    {jlib:l2i(T1), jlib:l2i(T2), jlib:l2i(T3)}
-            end,
+           [T1, T2, T3] = str:tokens(Str, <<":">>),
+           {jlib:l2i(T1), jlib:l2i(T2), jlib:l2i(T3)}
+    end,
     Payload = case xml_stream:parse_element(XML) of
-               {error, _Reason} -> [];
-               El -> [El]
-             end,
-    #pubsub_item{itemid = {ItemId, NodeId},
-                creation = {ToTime(Creation), JID},
-                modification = {ToTime(Modification), JID},
-                payload = Payload}.
+       {error, _Reason} -> [];
+       El -> [El]
+    end,
+    #pubsub_item{itemid = {ItemId, Nidx},
+       creation = {ToTime(Creation), JID},
+       modification = {ToTime(Modification), JID},
+       payload = Payload}.
index efcacf6e8a080164e549621304d789017e8d40dc..e48fd7967f8c6f21ae75cf740fa44c101e61da8a 100644 (file)
@@ -4,62 +4,54 @@
 %%% 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 Eric Cestari <eric@ohmforce.com>
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
-%%% @doc The module <strong>{@module}</strong> is the pep microblog PubSub plugin.
-%%% <p> To be used, mod_pubsub must be configured :
-%%% {mod_pubsub,   [ % requires mod_caps
-%%%      {access_createnode, pubsub_createnode},
-%%%      {plugins, ["default", "pep","mb"]},
-%%%      {pep_mapping, [{"urn:xmpp:microblog", "mb"}]}
-%%%     ]},
-%%% </p>
-%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-
 -module(node_mb).
-
+-behaviour(gen_pubsub_node).
 -author('eric@ohmforce.com').
 
--include("ejabberd.hrl").
--include("logger.hrl").
-
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
 
--behaviour(gen_pubsub_node).
+%%% @doc The module <strong>{@module}</strong> is the pep microblog PubSub plugin.
+%%% <p> To be used, mod_pubsub must be configured :
+%%% {mod_pubsub,   [ % requires mod_caps
+%%%             {access_createnode, pubsub_createnode},
+%%%             {plugins, ["default", "pep","mb"]},
+%%%          {pep_mapping, [{"urn:xmpp:microblog", "mb"}]}
+%%%            ]},
+%%% </p>
+%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
 
-%% 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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_pep:init(Host, ServerHost, Opts).
@@ -68,124 +60,127 @@ terminate(Host, ServerHost) ->
     node_pep:terminate(Host, ServerHost), ok.
 
 options() ->
-    [{deliver_payloads, true}, {notify_config, false},
-     {notify_delete, false}, {notify_retract, false},
-     {purge_offline, false}, {persist_items, true},
-     {max_items, ?MAXITEMS}, {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}].
+    [{deliver_payloads, true},
+       {notify_config, false},
+       {notify_delete, false},
+       {notify_retract, false},
+       {purge_offline, false},
+       {persist_items, true},
+       {max_items, ?MAXITEMS},
+       {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}].
 
 features() ->
-    [<<"create-nodes">>, %*
-     <<"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">>].
-
-create_node_permission(Host, ServerHost, Node,
-                      ParentNode, Owner, Access) ->
-    node_pep:create_node_permission(Host, ServerHost, Node,
-                                   ParentNode, Owner, Access).
-
-create_node(NodeId, Owner) ->
-    node_pep:create_node(NodeId, Owner).
-
-delete_node(Removed) -> node_pep:delete_node(Removed).
-
-subscribe_node(NodeId, Sender, Subscriber, AccessModel,
-              SendLast, PresenceSubscription, RosterGroup, Options) ->
-    node_pep:subscribe_node(NodeId, Sender, Subscriber,
-                           AccessModel, SendLast, PresenceSubscription,
-                           RosterGroup, Options).
-
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_pep:unsubscribe_node(NodeId, Sender, Subscriber,
-                             SubID).
-
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_pep:publish_item(NodeId, Publisher, Model,
-                         MaxItems, ItemId, Payload).
-
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_pep:remove_extra_items(NodeId, MaxItems, ItemIds).
-
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_pep:delete_item(NodeId, Publisher, PublishModel,
-                        ItemId).
-
-purge_node(NodeId, Owner) ->
-    node_pep:purge_node(NodeId, Owner).
+    [<<"create-nodes">>,
+       <<"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">>].
+
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+    node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+    node_pep:create_node(Nidx, Owner).
+
+delete_node(Removed) ->
+    node_pep:delete_node(Removed).
+
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_pep:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
+
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_pep:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
+
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_pep:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
+
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_pep:remove_extra_items(Nidx, MaxItems, ItemIds).
+
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_pep:delete_item(Nidx, Publisher, PublishModel, ItemId).
+
+purge_node(Nidx, Owner) ->
+    node_pep:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_pep:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_pep:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_pep:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_pep:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_pep:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_pep:set_affiliation(NodeId, Owner, Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_pep:set_affiliation(Nidx, Owner, Affiliation).
 
 get_entity_subscriptions(Host, Owner) ->
     node_pep:get_entity_subscriptions(Host, Owner).
 
-get_node_subscriptions(NodeId) ->
-    node_pep:get_node_subscriptions(NodeId).
+get_node_subscriptions(Nidx) ->
+    node_pep:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_pep:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_pep:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_pep:set_subscriptions(NodeId, Owner, Subscription,
-                              SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_pep:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_pep:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_pep:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_pep:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_pep:get_items(NodeId, JID, AccessModel,
-                      PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_pep:get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_pep:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_pep:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_pep:get_item(NodeId, ItemId, JID, AccessModel,
-                     PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_pep:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index f6575dafae826c70389dc34e7d0ef2c94b3ec99d..8eeb28f0e81d93d7c9164b2b44a9066027b51c43 100644 (file)
@@ -4,20 +4,19 @@
 %%% 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/]
 %%% @end
 %%% ====================================================================
 
-%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
-%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-
 -module(node_pep).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
--include("ejabberd.hrl").
--include("logger.hrl").
-
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
+-include("logger.hrl").
 
--behaviour(gen_pubsub_node).
+%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
+%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
 
-%% 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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts),
@@ -63,423 +56,193 @@ init(Host, ServerHost, Opts) ->
 terminate(Host, ServerHost) ->
     node_hometree:terminate(Host, ServerHost), ok.
 
--spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
 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(),...]).
+    [{deliver_payloads, true},
+       {notify_config, false},
+       {notify_delete, false},
+       {notify_retract, false},
+       {purge_offline, false},
+       {persist_items, false},
+       {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}].
+
 features() ->
-    [<<"create-nodes">>, %*
-     <<"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 ::
-(
-  Host          :: mod_pubsub:hostPEP(),
-  ServerHost    :: binary(),
-  NodeId        :: mod_pubsub:nodeId(),
-  _ParentNodeId :: mod_pubsub:nodeId(),
-  Owner         :: jid(),
-  Access        :: atom())
-    -> {result, boolean()}
-).
+    [<<"create-nodes">>,
+       <<"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">>].
 
 create_node_permission(Host, ServerHost, _Node, _ParentNode, Owner, Access) ->
     LOwner = jlib:jid_tolower(Owner),
     {User, Server, _Resource} = LOwner,
     Allowed = case LOwner of
-               {<<"">>, Host, <<"">>} ->
-                   true; % pubsub service always allowed
+       {<<"">>, Host, <<"">>} ->
+           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
-                     allow ->
-                         case Host of
-                           {User, Server, _} -> true;
-                           _ -> false
-                         end;
-                     E -> ?DEBUG("Create not allowed : ~p~n", [E]), false
-                   end
-             end,
+                   false
+           end
+    end,
     {result, Allowed}.
 
--spec(create_node/2 :: 
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, {default, broadcast}}
-).
-create_node(NodeIdx, Owner) ->
-    node_hometree:create_node(NodeIdx, Owner).
-
--spec(delete_node/1 ::
-(
-  Nodes :: [mod_pubsub:pubsubNode(),...])
-    -> {result,
-        {[],
-         [{mod_pubsub:pubsubNode(),
-           [{ljid(), [{mod_pubsub:subscription(), mod_pubsub:subId()}]},...]},...]
-         }
-        }
-).
-
-delete_node(Removed) ->
-    case node_hometree:delete_node(Removed) of
-      {result, {_, _, Result}} -> {result, {[], Result}};
-      Error -> Error
-    end.
+create_node(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, Owner).
+
+delete_node(Nodes) ->
+    {result, {_, _, Result}} = node_hometree:delete_node(Nodes),
+    {result, {[], Result}}.
+
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
--spec(subscribe_node/8 ::
-(
-  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 ::
-(
-  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, []}
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    case node_hometree:unsubscribe_node(Nidx, 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, xmlel()}
-).
-publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload) ->
-    node_hometree:publish_item(NodeIdx, Publisher, Model, MaxItems, ItemId, Payload).
-
--spec(remove_extra_items/3 ::
-(
-  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).
-
-
--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) ->
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
+
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
+
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
+
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
+
+get_entity_affiliations(Host, Owner) ->
     {_, D, _} = SubKey = jlib:jid_tolower(Owner),
     SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
-    States = mnesia:match_object(#pubsub_state{stateid =
-                                                  {GenKey, '_'},
-                                              _ = '_'}),
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(D, config), nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree
-              end,
-    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),
+    States = mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'}),
+    NodeTree = mod_pubsub:tree(Host),
+    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}.
 
 
--spec(get_node_affiliations/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [{ljid(), mod_pubsub:affiliation()}]}
-).
-get_node_affiliations(NodeIdx) ->
-    node_hometree:get_node_affiliations(NodeIdx).
-
--spec(get_affiliation/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, mod_pubsub:affiliation()}
-).
-get_affiliation(NodeIdx, Owner) ->
-    node_hometree:get_affiliation(NodeIdx, Owner).
-
--spec(set_affiliation/3 ::
-(
-  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 ::
-(
-  Host :: mod_pubsub:hostPEP(),
-  Owner :: jid())
-    -> {result,
-          [{mod_pubsub:pubsubNode(),
-            mod_pubsub:subscription(),
-            mod_pubsub:subId(),
-            ljid()}]
-       }
-).
-get_entity_subscriptions(_Host, Owner) ->
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
+
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
+
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, Owner, Affiliation).
+
+get_entity_subscriptions(Host, Owner) ->
     {U, D, _} = SubKey = jlib:jid_tolower(Owner),
     GenKey = jlib:jid_remove_resource(SubKey),
     States = case SubKey of
-              GenKey ->
-                  mnesia:match_object(#pubsub_state{stateid =
-                                                        {{U, D, '_'}, '_'},
-                                                    _ = '_'});
-              _ ->
-                  mnesia:match_object(#pubsub_state{stateid =
-                                                        {GenKey, '_'},
-                                                    _ = '_'})
-                    ++
-                    mnesia:match_object(#pubsub_state{stateid =
-                                                          {SubKey, '_'},
-                                                      _ = '_'})
-            end,
-    NodeTree = case catch
-                     ets:lookup(gen_mod:get_module_proc(D, config), nodetree)
-                  of
-                [{nodetree, N}] -> N;
-                _ -> nodetree_tree
-              end,
-    Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N},
-                                          subscriptions = Ss},
-                            Acc) ->
-                               case NodeTree:get_node(N) of
-                                 #pubsub_node{nodeid = {{_, D, _}, _}} = Node ->
-                                     lists:foldl(fun
-                                       ({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),
+       GenKey ->
+           mnesia:match_object(#pubsub_state{stateid = {{U, D, '_'}, '_'}, _ = '_'});
+       _ ->
+           mnesia:match_object(#pubsub_state{stateid = {GenKey, '_'}, _ = '_'})
+           ++
+           mnesia:match_object(#pubsub_state{stateid = {SubKey, '_'}, _ = '_'})
+    end,
+    NodeTree = mod_pubsub:tree(Host),
+    Reply = lists:foldl(fun (#pubsub_state{stateid = {J, N}, subscriptions = Ss}, Acc) ->
+                   case NodeTree:get_node(N) of
+                       #pubsub_node{nodeid = {{_, D, _}, _}} = Node ->
+                           lists:foldl(fun
+                                   ({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}.
 
--spec(get_node_subscriptions/1 ::
-(
-  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 ::
-(
-  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 ::
-(
-  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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
+
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
+
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
+
 get_pending_nodes(Host, Owner) ->
     node_hometree:get_pending_nodes(Host, Owner).
 
--spec(get_states/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [mod_pubsub:pubsubState()]}
-).
-get_states(NodeIdx) -> node_hometree:get_states(NodeIdx).
-
--spec(get_state/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  JID     :: ljid())
-    -> mod_pubsub:pubsubState()
-).
-get_state(NodeIdx, JID) ->
-    node_hometree:get_state(NodeIdx, JID).
-
--spec(set_state/1 ::
-(
-  State::mod_pubsub:pubsubState())
-    -> ok
-).
-set_state(State) -> node_hometree:set_state(State).
-
--spec(get_items/2 ::
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  _From   :: jid())
-    -> {result, [mod_pubsub:pubsubItem()]}
-).
-get_items(NodeIdx, From) ->
-    node_hometree:get_items(NodeIdx, From).
-
--spec(get_items/6 ::
-(
-  NodeIdx               :: mod_pubsub:nodeIdx(),
-  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 ::
-(
-  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 ::
-(
-  NodeIdx              :: mod_pubsub:nodeIdx(),
-  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 ::
-(
-  Item::mod_pubsub:pubsubItem())
-    -> ok
-).
-set_item(Item) -> node_hometree:set_item(Item).
+get_states(Nidx) ->
+    node_hometree:get_states(Nidx).
+
+get_state(Nidx, JID) ->
+    node_hometree:get_state(Nidx, JID).
+
+set_state(State) ->
+    node_hometree:set_state(State).
+
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
+
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
+
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
+
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, 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).
 
-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
@@ -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.
 complain_if_modcaps_disabled(ServerHost) ->
     case gen_mod:is_loaded(ServerHost, mod_caps) of
-      false ->
-         ?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]);
-      true -> ok
+       false ->
+           ?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]);
+       true -> ok
     end.
index 39b936aad20afe10cd588faa6d914ce5ccf045f2..d80e686fbc7384ae87f0846c32aeb81f93e8067f 100644 (file)
@@ -4,20 +4,19 @@
 %%% 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/]
 %%% @end
 %%% ====================================================================
 
-%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
-%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
-
 -module(node_pep_odbc).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
--include("ejabberd.hrl").
--include("logger.hrl").
-
 -include("pubsub.hrl").
-
 -include("jlib.hrl").
+-include("logger.hrl").
 
--define(PUBSUB, mod_pubsub_odbc).
-
--behaviour(gen_pubsub_node).
+%%% @doc The module <strong>{@module}</strong> is the pep PubSub plugin.
+%%% <p>PubSub plugin nodes are using the {@link gen_pubsub_node} behaviour.</p>
 
-%% 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_entity_subscriptions_for_send_last/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/7, get_items/6,
-        get_items/3, get_items/2, get_item/7, get_item/2,
-        set_item/1, get_item_name/3, get_last_items/3,
-        node_to_path/1, path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1,
+    get_entity_subscriptions_for_send_last/2, get_last_items/3]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree_odbc:init(Host, ServerHost, Opts),
@@ -67,362 +57,183 @@ init(Host, ServerHost, Opts) ->
 terminate(Host, ServerHost) ->
     node_hometree_odbc:terminate(Host, ServerHost), ok.
 
--spec(options/0 :: () -> NodeOptions::mod_pubsub:nodeOptions()).
 options() ->
-    [{odbc, true},
-     {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(),...]).
+    [{odbc, true}, {rsm, true} | node_pep:options()].
+
 features() ->
-    [<<"create-nodes">>, %*
-     <<"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 ::
-(
-  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) ->
-    LOwner = jlib:jid_tolower(Owner),
-    {User, Server, _Resource} = LOwner,
-    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 :: 
-(
-  NodeIdx :: mod_pubsub:nodeIdx(),
-  Owner   :: jid())
-    -> {result, []}
-).
-create_node(NodeIdx, Owner) ->
-    case node_hometree_odbc:create_node(NodeIdx, Owner) of
-      {result, _} -> {result, []};
-      Error -> Error
-    end.
+    [<<"rsm">> | node_pep:features()].
 
--spec(delete_node/1 ::
-(
-  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.
+create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
+    node_pep:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
+
+create_node(Nidx, Owner) ->
+    node_hometree_odbc:create_node(Nidx, Owner),
+    {result, {default, broadcast}}.
 
--spec(subscribe_node/8 ::
-(
-  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).
-
-
--spec(unsubscribe_node/4 ::
-(
-  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, []}
+delete_node(Nodes) ->
+    {result, {_, _, Result}} = node_hometree_odbc:delete_node(Nodes),
+    {result, {[], Result}}.
+
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree_odbc:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
+
+
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    case node_hometree_odbc:unsubscribe_node(Nidx, 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()}]}
-).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree_odbc:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
+
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree_odbc:remove_extra_items(Nidx, MaxItems, ItemIds).
+
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree_odbc:delete_item(Nidx, Publisher, PublishModel, ItemId).
+
+purge_node(Nidx, Owner) ->
+    node_hometree_odbc:purge_node(Nidx, Owner).
+
 get_entity_affiliations(_Host, Owner) ->
     OwnerKey = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
     node_hometree_odbc:get_entity_affiliations(OwnerKey, Owner).
 
--spec(get_node_affiliations/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> {result, [{ljid(), mod_pubsub:affiliation()}]}
-).
-get_node_affiliations(NodeIdx) ->
-    node_hometree_odbc:get_node_affiliations(NodeIdx).
-
--spec(get_affiliation/2 ::
-(
-  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) ->
-    node_hometree_odbc:set_affiliation(NodeId, Owner, Affiliation).
+get_node_affiliations(Nidx) ->
+    node_hometree_odbc:get_node_affiliations(Nidx).
+
+get_affiliation(Nidx, Owner) ->
+    node_hometree_odbc:get_affiliation(Nidx, Owner).
+
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree_odbc:set_affiliation(Nidx, Owner, Affiliation).
 
 get_entity_subscriptions(_Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     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),
     GJ = node_hometree_odbc:encode_jid(GenKey),
     Query = case SubKey of
-             GenKey ->
-                 [<<"select host, node, type, i.nodeid, jid, "
-                    "subscriptions from pubsub_state i, pubsub_nod"
-                    "e n where i.nodeid = n.nodeid and jid "
-                    "like '">>,
-                  GJ, <<"%' and host like '%@">>, Host, <<"';">>];
-             _ ->
-                 [<<"select host, node, type, i.nodeid, jid, "
-                    "subscriptions from pubsub_state i, pubsub_nod"
-                    "e n where i.nodeid = n.nodeid and jid "
-                    "in ('">>,
-                  SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
-                  <<"';">>]
-           end,
+       GenKey ->
+           [<<"select host, node, type, i.nodeid, jid, "
+                   "subscriptions from pubsub_state i, pubsub_node n "
+                   "where i.nodeid = n.nodeid and jid "
+                   "like '">>, GJ, <<"%' and host like '%@">>, Host, <<"';">>];
+       _ ->
+           [<<"select host, node, type, i.nodeid, jid, "
+                   "subscriptions from pubsub_state i, pubsub_node n "
+                   "where i.nodeid = n.nodeid and jid "
+                   "in ('">>, SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host, <<"';">>]
+    end,
     Reply = case catch ejabberd_odbc:sql_query_t(Query) of
-             {selected,
-              [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>,
-               <<"jid">>, <<"subscriptions">>],
-              RItems} ->
-                 lists:map(fun ([H, N, T, I, J, S]) ->
-                                   O = node_hometree_odbc:decode_jid(H),
-                                   Node = nodetree_tree_odbc:raw_to_node(O,
-                                                                         [N,
-                                                                          <<"">>,
-                                                                          T,
-                                                                          I]),
-                                   {Node,
-                                    node_hometree_odbc:decode_subscriptions(S),
-                                    node_hometree_odbc:decode_jid(J)}
-                           end,
-                           RItems);
-             _ -> []
-           end,
+       {selected,
+                   [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
+                   RItems} ->
+           lists:map(fun ([H, N, T, I, J, S]) ->
+                       O = node_hometree_odbc:decode_jid(H),
+                       Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
+                       {Node,
+                           node_hometree_odbc:decode_subscriptions(S),
+                           node_hometree_odbc:decode_jid(J)}
+               end,
+               RItems);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
 get_entity_subscriptions_for_send_last(_Host, Owner) ->
     SubKey = jlib:jid_tolower(Owner),
     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),
     GJ = node_hometree_odbc:encode_jid(GenKey),
     Query = case SubKey of
-             GenKey ->
-                 [<<"select host, node, type, i.nodeid, jid, "
-                    "subscriptions from pubsub_state i, pubsub_nod"
-                    "e n, pubsub_node_option o where i.nodeid "
-                    "= n.nodeid and n.nodeid = o.nodeid and "
-                    "name='send_last_published_item' and "
-                    "val='on_sub_and_presence' and jid like "
-                    "'">>,
-                  GJ, <<"%' and host like '%@">>, Host, <<"';">>];
-             _ ->
-                 [<<"select host, node, type, i.nodeid, jid, "
-                    "subscriptions from pubsub_state i, pubsub_nod"
-                    "e n, pubsub_node_option o where i.nodeid "
-                    "= n.nodeid and n.nodeid = o.nodeid and "
-                    "name='send_last_published_item' and "
-                    "val='on_sub_and_presence' and jid in "
-                    "('">>,
-                  SJ, <<"', '">>, GJ, <<"') and host like '%@">>, Host,
-                  <<"';">>]
-           end,
+       GenKey ->
+           [<<"select host, node, type, i.nodeid, jid, "
+                   "subscriptions from pubsub_state i, pubsub_node n, "
+                   "pubsub_node_option o where i.nodeid = n.nodeid "
+                   "and n.nodeid = o.nodeid and name='send_last_published_item' and "
+                   "val='on_sub_and_presence' and jid like '">>,
+               GJ, <<"%' and host like '%@">>, Host, <<"';">>];
+       _ ->
+           [<<"select host, node, type, i.nodeid, jid, "
+                   "subscriptions from pubsub_state i, pubsub_node n, "
+                   "pubsub_node_option o where i.nodeid = n.nodeid "
+                   "and n.nodeid = o.nodeid and name='send_last_published_item' and "
+                   "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
-             {selected,
-              [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>,
-               <<"jid">>, <<"subscriptions">>],
-              RItems} ->
-                 lists:map(fun ([H, N, T, I, J, S]) ->
-                                   O = node_hometree_odbc:decode_jid(H),
-                                   Node = nodetree_tree_odbc:raw_to_node(O,
-                                                                         [N,
-                                                                          <<"">>,
-                                                                          T,
-                                                                          I]),
-                                   {Node,
-                                    node_hometree_odbc:decode_subscriptions(S),
-                                    node_hometree_odbc:decode_jid(J)}
-                           end,
-                           RItems);
-             _ -> []
-           end,
+       {selected,
+                   [<<"host">>, <<"node">>, <<"type">>, <<"nodeid">>, <<"jid">>, <<"subscriptions">>],
+                   RItems} ->
+           lists:map(fun ([H, N, T, I, J, S]) ->
+                       O = node_hometree_odbc:decode_jid(H),
+                       Node = nodetree_tree_odbc:raw_to_node(O, [N, <<"">>, T, I]),
+                       {Node,
+                           node_hometree_odbc:decode_subscriptions(S),
+                           node_hometree_odbc:decode_jid(J)}
+               end,
+               RItems);
+       _ ->
+           []
+    end,
     {result, Reply}.
 
-get_node_subscriptions(NodeId) ->
-    node_hometree_odbc:get_node_subscriptions(NodeId).
+get_node_subscriptions(Nidx) ->
+    node_hometree_odbc:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree_odbc:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree_odbc:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree_odbc:set_subscriptions(NodeId, Owner,
-                                        Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree_odbc:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 get_pending_nodes(Host, Owner) ->
     node_hometree_odbc:get_pending_nodes(Host, Owner).
 
-get_states(NodeId) ->
-    node_hometree_odbc:get_states(NodeId).
-
-get_state(NodeId, JID) ->
-    node_hometree_odbc:get_state(NodeId, JID).
-
-set_state(State) -> node_hometree_odbc:set_state(State).
+get_states(Nidx) ->
+    node_hometree_odbc:get_states(Nidx).
 
-get_items(NodeId, From) ->
-    node_hometree_odbc:get_items(NodeId, From).
+get_state(Nidx, JID) ->
+    node_hometree_odbc:get_state(Nidx, JID).
 
-get_items(NodeId, From, RSM) ->
-    node_hometree_odbc:get_items(NodeId, From, RSM).
+set_state(State) ->
+    node_hometree_odbc:set_state(State).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    get_items(NodeId, JID, AccessModel,
-             PresenceSubscription, RosterGroup, SubId, none).
+get_items(Nidx, From, RSM) ->
+    node_hometree_odbc:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId, RSM) ->
-    node_hometree_odbc:get_items(NodeId, JID, AccessModel,
-                                PresenceSubscription, RosterGroup, SubId, RSM).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree_odbc:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_last_items(NodeId, JID, Count) ->
-    node_hometree_odbc:get_last_items(NodeId, JID, Count).
+get_last_items(Nidx, JID, Count) ->
+    node_hometree_odbc:get_last_items(Nidx, JID, Count).
 
-get_item(NodeId, ItemId) ->
-    node_hometree_odbc:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree_odbc:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree_odbc:get_item(NodeId, ItemId, JID,
-                               AccessModel, PresenceSubscription, RosterGroup,
-                               SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree_odbc:get_item(Nidx, ItemId, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId).
 
-set_item(Item) -> node_hometree_odbc:set_item(Item).
+set_item(Item) ->
+    node_hometree_odbc:set_item(Item).
 
 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
@@ -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
 %% If not, show a warning message in the ejabberd log file.
 complain_if_modcaps_disabled(ServerHost) ->
-    Modules = ejabberd_config:get_option({modules,
-                                               ServerHost},
-                       fun(Ms) when is_list(Ms) -> Ms end),
-    ModCaps = [mod_caps_enabled
-              || {mod_caps, _Opts} <- Modules],
-    case ModCaps of
-      [] ->
-         ?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
+    case gen_mod:is_loaded(ServerHost, mod_caps) of
+       false ->
+           ?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]);
+       true -> ok
     end.
index 12459e595e1cff46e600d234be87d855dbaf6c4a..eab06d56209e987e8afedeb4aaa5a59b2152265c 100644 (file)
@@ -4,58 +4,45 @@
 %%% 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>
+%%% @author Christophe Romain <christophe.romain@process-one.net>
 %%%   [http://www.process-one.net/]
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_private).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -64,121 +51,126 @@ 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, whitelist}, {roster_groups_allowed, []},
-     {publish_model, publishers},
-     {notification_type, headline},
-     {max_payload_size, ?MAX_PAYLOAD_SIZE},
-     {send_last_published_item, never},
-     {deliver_notifications, false},
-     {presence_based_delivery, false}].
+    [{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, whitelist},
+       {roster_groups_allowed, []},
+       {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() ->
-    [<<"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).
+    [<<"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(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree:unsubscribe_node(NodeId, Sender,
-                                  Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree:publish_item(NodeId, Publisher, Model,
-                              MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree:remove_extra_items(NodeId, MaxItems,
-                                    ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree:delete_item(NodeId, Publisher,
-                             PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index cbb7baffb7ebac43382b595a50e1eab5ddb7d2ec..efefd67fe451c48a18ccce2b4c7c34ba54d9c7d4 100644 (file)
@@ -4,58 +4,45 @@
 %%% 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>
+%%% @author Christophe Romain <christophe.romain@process-one.net>
 %%%   [http://www.process-one.net/]
 %%% @version {@vsn}, {@date} {@time}
 %%% @end
 %%% ====================================================================
 
 -module(node_public).
-
+-behaviour(gen_pubsub_node).
 -author('christophe.romain@process-one.net').
 
 -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, node_to_path/1,
-        path_to_node/1]).
+    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/7, get_items/3, get_item/7,
+    get_item/2, set_item/1, get_item_name/3, node_to_path/1,
+    path_to_node/1]).
 
 init(Host, ServerHost, Opts) ->
     node_hometree:init(Host, ServerHost, Opts).
@@ -64,123 +51,126 @@ 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, never},
-     {deliver_notifications, true},
-     {presence_based_delivery, false}].
+    [{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, never},
+       {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).
+    [<<"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(Nidx, Owner) ->
+    node_hometree:create_node(Nidx, 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).
+subscribe_node(Nidx, Sender, Subscriber, AccessModel,
+           SendLast, PresenceSubscription, RosterGroup, Options) ->
+    node_hometree:subscribe_node(Nidx, Sender, Subscriber, AccessModel, SendLast,
+       PresenceSubscription, RosterGroup, Options).
 
-unsubscribe_node(NodeId, Sender, Subscriber, SubID) ->
-    node_hometree:unsubscribe_node(NodeId, Sender,
-                                  Subscriber, SubID).
+unsubscribe_node(Nidx, Sender, Subscriber, SubId) ->
+    node_hometree:unsubscribe_node(Nidx, Sender, Subscriber, SubId).
 
-publish_item(NodeId, Publisher, Model, MaxItems, ItemId,
-            Payload) ->
-    node_hometree:publish_item(NodeId, Publisher, Model,
-                              MaxItems, ItemId, Payload).
+publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload) ->
+    node_hometree:publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload).
 
-remove_extra_items(NodeId, MaxItems, ItemIds) ->
-    node_hometree:remove_extra_items(NodeId, MaxItems,
-                                    ItemIds).
+remove_extra_items(Nidx, MaxItems, ItemIds) ->
+    node_hometree:remove_extra_items(Nidx, MaxItems, ItemIds).
 
-delete_item(NodeId, Publisher, PublishModel, ItemId) ->
-    node_hometree:delete_item(NodeId, Publisher,
-                             PublishModel, ItemId).
+delete_item(Nidx, Publisher, PublishModel, ItemId) ->
+    node_hometree:delete_item(Nidx, Publisher, PublishModel, ItemId).
 
-purge_node(NodeId, Owner) ->
-    node_hometree:purge_node(NodeId, Owner).
+purge_node(Nidx, Owner) ->
+    node_hometree:purge_node(Nidx, Owner).
 
 get_entity_affiliations(Host, Owner) ->
     node_hometree:get_entity_affiliations(Host, Owner).
 
-get_node_affiliations(NodeId) ->
-    node_hometree:get_node_affiliations(NodeId).
+get_node_affiliations(Nidx) ->
+    node_hometree:get_node_affiliations(Nidx).
 
-get_affiliation(NodeId, Owner) ->
-    node_hometree:get_affiliation(NodeId, Owner).
+get_affiliation(Nidx, Owner) ->
+    node_hometree:get_affiliation(Nidx, Owner).
 
-set_affiliation(NodeId, Owner, Affiliation) ->
-    node_hometree:set_affiliation(NodeId, Owner,
-                                 Affiliation).
+set_affiliation(Nidx, Owner, Affiliation) ->
+    node_hometree:set_affiliation(Nidx, 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_node_subscriptions(Nidx) ->
+    node_hometree:get_node_subscriptions(Nidx).
 
-get_subscriptions(NodeId, Owner) ->
-    node_hometree:get_subscriptions(NodeId, Owner).
+get_subscriptions(Nidx, Owner) ->
+    node_hometree:get_subscriptions(Nidx, Owner).
 
-set_subscriptions(NodeId, Owner, Subscription, SubId) ->
-    node_hometree:set_subscriptions(NodeId, Owner,
-                                   Subscription, SubId).
+set_subscriptions(Nidx, Owner, Subscription, SubId) ->
+    node_hometree:set_subscriptions(Nidx, Owner, Subscription, SubId).
 
 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) ->
-    node_hometree:get_state(NodeId, JID).
+get_state(Nidx, 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) ->
-    node_hometree:get_items(NodeId, From).
+get_items(Nidx, From, RSM) ->
+    node_hometree:get_items(Nidx, From, RSM).
 
-get_items(NodeId, JID, AccessModel,
-         PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_items(NodeId, JID, AccessModel,
-                           PresenceSubscription, RosterGroup, SubId).
+get_items(Nidx, JID, AccessModel, PresenceSubscription, RosterGroup, SubId, RSM) ->
+    node_hometree:get_items(Nidx, JID, AccessModel,
+       PresenceSubscription, RosterGroup, SubId, RSM).
 
-get_item(NodeId, ItemId) ->
-    node_hometree:get_item(NodeId, ItemId).
+get_item(Nidx, ItemId) ->
+    node_hometree:get_item(Nidx, ItemId).
 
-get_item(NodeId, ItemId, JID, AccessModel,
-        PresenceSubscription, RosterGroup, SubId) ->
-    node_hometree:get_item(NodeId, ItemId, JID, AccessModel,
-                          PresenceSubscription, RosterGroup, SubId).
+get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, SubId) ->
+    node_hometree:get_item(Nidx, ItemId, JID, AccessModel,
+       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) ->
     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).
index e8ad8b14195c79e92b6a37ca735f7fbc5a1ba016..b2d4fade06a4600aa8d08087f5cd562d50bd71b3 100644 (file)
@@ -4,11 +4,13 @@
 %%% 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.
+%%% 
 %%%
 %%% @author Brian Cully <bjc@kublai.com>
 %%% @version {@vsn}, {@date} {@time}
 %%% ====================================================================
 
 -module(nodetree_dag).
-
+-behaviour(gen_pubsub_nodetree).
 -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("ejabberd.hrl").
--include("logger.hrl").
-
 -include("pubsub.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_CHILDREN, []).
 
--compile(export_all).
-
-%%====================================================================
-%% API
-%%====================================================================
 init(Host, ServerHost, Opts) ->
     nodetree_tree:init(Host, ServerHost, Opts).
 
 terminate(Host, ServerHost) ->
     nodetree_tree:terminate(Host, ServerHost).
 
--spec(create_node/6 ::
-(
-  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) ->
+set_node(#pubsub_node{nodeid = {Key, _}, owners = Owners, options = Options} = Node) ->
     Parents = find_opt(collection, ?DEFAULT_PARENTS, Options),
     case validate_parentage(Key, Owners, Parents) of
-      true ->
-         mnesia:write(Node#pubsub_node{parents = Parents});
-      Other -> Other
+       true -> mnesia:write(Node#pubsub_node{parents = Parents});
+       Other -> Other
     end.
 
--spec(delete_node/2 ::
-(
-  Key    :: mod_pubsub:hostPubsub(),
-  NodeID :: mod_pubsub:nodeId())
-    -> [mod_pubsub:pubsubNode(),...]
-    %%%
-     | {error, xmlel()}
-).
-delete_node(Key, NodeID) ->
-    case find_node(Key, NodeID) of
-      false -> {error, ?ERR_ITEM_NOT_FOUND};
-      Node ->
-         lists:foreach(fun (#pubsub_node{options = Opts} =
-                                Child) ->
-                               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]
+create_node(Key, Node, Type, Owner, Options, Parents) ->
+    OwnerJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
+    case find_node(Key, Node) of
+       false ->
+           Nidx = pubsub_index:new(node),
+           N = #pubsub_node{nodeid = oid(Key, Node), id = Nidx,
+                   type = Type, parents = Parents, owners = [OwnerJID],
+                   options = Options},
+           case set_node(N) of
+               ok -> {ok, Nidx};
+               Other -> Other
+           end;
+       _ ->
+           {error, ?ERR_CONFLICT}
     end.
 
-options() -> nodetree_tree:options().
+delete_node(Key, Node) ->
+    case find_node(Key, Node) of
+       false ->
+           {error, ?ERR_ITEM_NOT_FOUND};
+       Record ->
+           lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
+                       NewOpts = remove_config_parent(Node, 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, Node)),
+           pubsub_index:free(node, Record#pubsub_node.id),
+           mnesia:delete_object(pubsub_node, Record, write),
+           [Record]
+    end.
 
-get_node(Host, NodeID, _From) -> get_node(Host, NodeID).
+options() ->
+    nodetree_tree:options().
 
--spec(get_node/2 ::
-(
-  Host :: mod_pubsub:hostPubsub(),
-  NodeID :: mod_pubsub:nodeId())
-    -> mod_pubsub:pubsubNode()
-    %%%
-     | {error, xmlel}
-).
-get_node(Host, NodeID) ->
-    case find_node(Host, NodeID) of
-      false -> {error, ?ERR_ITEM_NOT_FOUND};
-      Node -> Node
+get_node(Host, Node, _From) ->
+    get_node(Host, Node).
+
+get_node(Host, Node) ->
+    case find_node(Host, Node) of
+       false -> {error, ?ERR_ITEM_NOT_FOUND};
+       Record -> Record
     end.
 
--spec(get_node/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> mod_pubsub:pubsubNode()
-     | {error, xmlel()}
-).
-get_node(NodeId) -> nodetree_tree:get_node(NodeId).
+get_node(Node) ->
+    nodetree_tree:get_node(Node).
 
 get_nodes(Key, From) ->
     nodetree_tree:get_nodes(Key, From).
 
--spec(get_nodes/1 ::
-(
-  Host::mod_pubsub:host())
-    -> [mod_pubsub:pubsubNode()]
-).
-get_nodes(Key) -> nodetree_tree:get_nodes(Key).
+get_nodes(Key) ->
+    nodetree_tree:get_nodes(Key).
 
--spec(get_parentnodes/3 ::
-(
-  Host   :: mod_pubsub:hostPubsub(),
-  NodeID :: mod_pubsub:nodeId(),
-  _From  :: _)
-    -> [mod_pubsub:pubsubNode()]
-    %%%
-     | {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
+get_parentnodes(Host, Node, _From) ->
+    case find_node(Host, Node) 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),
-                       Parent <- Parents, Host == NHost, Parent == NNode]),
-         qlc:e(Q)
+                           Parent <- Parents, Host == NHost, Parent == NNode]),
+           qlc:e(Q)
     end.
 
-get_parentnodes_tree(Host, NodeID, _From) ->
-    Pred = fun (NID, #pubsub_node{nodeid = {_, NNodeID}}) ->
-                  NID == NNodeID
-          end,
+get_parentnodes_tree(Host, Node, _From) ->
+    Pred = fun (NID, #pubsub_node{nodeid = {_, NNode}}) ->
+           NID == NNode
+    end,
     Tr = fun (#pubsub_node{parents = Parents}) -> Parents
-        end,
-    traversal_helper(Pred, Tr, Host, [NodeID]).
+    end,
+    traversal_helper(Pred, Tr, Host, [Node]).
 
-get_subnodes(Host, NodeID, _From) ->
-    get_subnodes(Host, NodeID).
+get_subnodes(Host, Node, _From) ->
+    get_subnodes(Host, Node).
 
--spec(get_subnodes/2 ::
-(
-  Host   :: mod_pubsub:hostPubsub(),
-  NodeId :: mod_pubsub:nodeId())
-    -> [mod_pubsub:pubsubNode()]
-).
 get_subnodes(Host, <<>>) ->
     get_subnodes_helper(Host, <<>>);
-get_subnodes(Host, NodeID) ->
-    case find_node(Host, NodeID) of
-      false -> {error, ?ERR_ITEM_NOT_FOUND};
-      _ -> get_subnodes_helper(Host, NodeID)
+get_subnodes(Host, Node) ->
+    case find_node(Host, Node) of
+       false -> {error, ?ERR_ITEM_NOT_FOUND};
+       _ -> get_subnodes_helper(Host, Node)
     end.
 
--spec(get_subnodes_helper/2 ::
-(
-  Host :: mod_pubsub:hostPubsub(),
-  NodeID :: mod_pubsub:nodeId())
-    -> [mod_pubsub:pubsubNode()]
-).
-get_subnodes_helper(Host, NodeID) ->
-    Q = qlc:q([Node
-              || #pubsub_node{nodeid = {NHost, _},
-                              parents = Parents} =
-                     Node
-                     <- mnesia:table(pubsub_node),
-                 Host == NHost, lists:member(NodeID, Parents)]),
+get_subnodes_helper(Host, Node) ->
+    Q = qlc:q([N
+               || #pubsub_node{nodeid = {NHost, _},
+                       parents = Parents} =
+                   N
+                   <- mnesia:table(pubsub_node),
+                   Host == NHost, lists:member(Node, Parents)]),
     qlc:e(Q).
 
-get_subnodes_tree(Host, NodeID, From) ->
+get_subnodes_tree(Host, Node, From) ->
     Pred = fun (NID, #pubsub_node{parents = Parents}) ->
-                  lists:member(NID, Parents)
-          end,
+           lists:member(NID, Parents)
+    end,
     Tr = fun (#pubsub_node{nodeid = {_, N}}) -> [N] end,
-    traversal_helper(Pred, Tr, 1, Host, [NodeID],
-                    [{0, [get_node(Host, NodeID, From)]}]).
+    traversal_helper(Pred, Tr, 1, Host, [Node],
+       [{0, [get_node(Host, Node, From)]}]).
 
 %%====================================================================
 %% Internal functions
@@ -236,17 +160,17 @@ get_subnodes_tree(Host, NodeID, From) ->
 oid(Key, Name) -> {Key, Name}.
 
 %% Key    = jlib:jid() | host()
-%% NodeID = string()
+%% Node = string()
 -spec(find_node/2 ::
-(
-  Key :: mod_pubsub:hostPubsub(),
-  NodeID :: mod_pubsub:nodeId())
+    (
+       Key :: mod_pubsub:hostPubsub(),
+       Node :: mod_pubsub:nodeId())
     -> mod_pubsub:pubsubNode() | false
-).
-find_node(Key, NodeID) ->
-    case mnesia:read(pubsub_node, oid(Key, NodeID), read) of
-      [] -> false;
-      [Node] -> Node
+    ).
+find_node(Key, Node) ->
+    case mnesia:read(pubsub_node, oid(Key, Node), read) of
+       [] -> false;
+       [Node] -> Node
     end.
 
 %% Key     = jlib:jid() | host()
@@ -254,77 +178,67 @@ find_node(Key, NodeID) ->
 %% Options = [{Key = atom(), Value = term()}]
 find_opt(Key, Default, Options) ->
     case lists:keysearch(Key, 1, Options) of
-      {value, {Key, Val}} -> Val;
-      _ -> Default
+       {value, {Key, Val}} -> Val;
+       _ -> Default
     end.
 
 -spec(traversal_helper/4 ::
-(
-  Pred   :: fun(),
-  Tr     :: fun(),
-  Host   :: mod_pubsub:hostPubsub(),
-  NodeId :: [mod_pubsub:pubsubNode(),...])
-    -> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
-).
-
-traversal_helper(Pred, Tr, Host, NodeIDs) ->
-    traversal_helper(Pred, Tr, 0, Host, NodeIDs, []).
+    (
+       Pred    :: fun(),
+                   Tr      :: fun(),
+                               Host    :: mod_pubsub:hostPubsub(),
+                               Nodes :: [mod_pubsub:nodeId(),...])
+                               -> [{Depth::non_neg_integer(), Nodes::[mod_pubsub:pubsubNode(),...]}]
+                               ).
+traversal_helper(Pred, Tr, Host, Nodes) ->
+    traversal_helper(Pred, Tr, 0, Host, Nodes, []).
 
 traversal_helper(_Pred, _Tr, _Depth, _Host, [], Acc) ->
     Acc;
-traversal_helper(Pred, Tr, Depth, Host, NodeIDs, Acc) ->
-    Q = qlc:q([Node
-              || #pubsub_node{nodeid = {NHost, _}} = Node
-                     <- mnesia:table(pubsub_node),
-                 NodeID <- NodeIDs, Host == NHost, Pred(NodeID, Node)]),
+traversal_helper(Pred, Tr, Depth, Host, Nodes, Acc) ->
+    Q = qlc:q([N
+               || #pubsub_node{nodeid = {NHost, _}} = N
+                   <- mnesia:table(pubsub_node),
+                   Node <- Nodes, Host == NHost, Pred(Node, N)]),
     Nodes = qlc:e(Q),
     IDs = lists:flatmap(Tr, Nodes),
-    traversal_helper(Pred, Tr, Depth + 1, Host, IDs,
-                    [{Depth, Nodes} | Acc]).
+    traversal_helper(Pred, Tr, Depth + 1, Host, IDs, [{Depth, Nodes} | Acc]).
 
-remove_config_parent(NodeID, Options) ->
-    remove_config_parent(NodeID, Options, []).
+remove_config_parent(Node, Options) ->
+    remove_config_parent(Node, Options, []).
 
-remove_config_parent(_NodeID, [], Acc) ->
+remove_config_parent(_Node, [], Acc) ->
     lists:reverse(Acc);
-remove_config_parent(NodeID, [{collection, Parents} | T], Acc) ->
-    remove_config_parent(NodeID, T,
-        [{collection, lists:delete(NodeID, Parents)} | Acc]);
-remove_config_parent(NodeID, [H | T], Acc) ->
-    remove_config_parent(NodeID, T, [H | Acc]).
+remove_config_parent(Node, [{collection, Parents} | T], Acc) ->
+    remove_config_parent(Node, T, [{collection, lists:delete(Node, Parents)} | Acc]);
+remove_config_parent(Node, [H | T], Acc) ->
+    remove_config_parent(Node, T, [H | Acc]).
 
 -spec(validate_parentage/3 ::
-(
-  Key            :: mod_pubsub:hostPubsub(),
-  Owners         :: [ljid(),...],
-  Parent_NodeIds :: [mod_pubsub:nodeId()])
+    (
+       Key            :: mod_pubsub:hostPubsub(),
+       Owners         :: [ljid(),...],
+       Parent_Nodes :: [mod_pubsub:nodeId()])
     -> true
     %%%
-     | {error, xmlel()}
-).
-validate_parentage(_Key, _Owners, []) -> true;
+    | {error, xmlel()}
+    ).
+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, [ParentID | T]) ->
     case find_node(Key, ParentID) of
-      false -> {error, ?ERR_ITEM_NOT_FOUND};
-      #pubsub_node{owners = POwners, options = POptions} ->
-         NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
-         MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
-         case {MutualOwners, NodeType} of
-           {[], _} -> {error, ?ERR_FORBIDDEN};
-           {_, collection} -> validate_parentage(Key, Owners, T);
-           {_, _} -> {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, <<>>)
+       false ->
+           {error, ?ERR_ITEM_NOT_FOUND};
+       #pubsub_node{owners = POwners, options = POptions} ->
+           NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
+           MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
+           case {MutualOwners, NodeType} of
+               {[], _} -> {error, ?ERR_FORBIDDEN};
+               {_, collection} -> validate_parentage(Key, Owners, T);
+               {_, _} -> {error, ?ERR_NOT_ALLOWED}
+           end
     end.
index 7a9b46bcc967cb9ceb49ae140c400ece9803e0a7..e34eabf307ac6f8b61c3a8ea530c440c9f09c0e0 100644 (file)
 %%% improvements.</p>
 
 -module(nodetree_tree).
-
+-behaviour(gen_pubsub_nodetree).
 -author('christophe.romain@process-one.net').
 
 -include_lib("stdlib/include/qlc.hrl").
 
 -include("pubsub.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]).
-
-%% ================
-%% API definition
-%% ================
+    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]).
 
-%% @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) ->
     mnesia:create_table(pubsub_node,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, pubsub_node)}]),
+       [{disc_copies, [node()]},
+           {attributes, record_info(fields, pubsub_node)}]),
     mnesia:add_table_index(pubsub_node, id),
     NodesFields = record_info(fields, pubsub_node),
     case mnesia:table_info(pubsub_node, attributes) of
-      NodesFields -> ok;
-      _ -> ok
+       NodesFields -> ok;
+       _ -> ok
     end,
     %% mnesia:transform_table(pubsub_state, ignore, StatesFields)
     ok.
-%% @spec (Host, ServerHost) -> ok
-%%        Host       = string()
-%%        ServerHost = string()
 
-%% @spec () -> Options
-%%        Options = [mod_pubsub:nodeOption()]
-%% @doc Returns the default pubsub node tree options.
-terminate(_Host, _ServerHost) -> ok.
+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) ->
     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}
-%%     Host   = mod_pubsub:host()
-%%     NodeId = mod_pubsub:nodeId()
-%%     Node   = mod_pubsub:pubsubNode()
-%%      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
+get_node(Host, Node) ->
+    case catch mnesia:read({pubsub_node, {Host, Node}}) of
+       [Record] when is_record(Record, pubsub_node) -> Record;
+       _ -> {error, ?ERR_ITEM_NOT_FOUND}
     end.
 
--spec(get_node/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> mod_pubsub:pubsubNode()
-     | {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
+get_node(Nidx) ->
+    case catch mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
+       [Record] when is_record(Record, pubsub_node) -> Record;
+       _ -> {error, ?ERR_ITEM_NOT_FOUND}
     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) ->
     mnesia:match_object(#pubsub_node{nodeid = {Host, '_'}, _ = '_'}).
 
-%% @spec (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) -> [].
+get_parentnodes(_Host, _Node, _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
 %% containing just this node.</p>
--spec(get_parentnodes_tree/3 ::
-(
-  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 -> []
+get_parentnodes_tree(Host, Node, _From) ->
+    case catch mnesia:read({pubsub_node, {Host, Node}}) of
+       [Record] when is_record(Record, pubsub_node) -> [{0, [Record]}];
+       _ -> []
     end.
 
-%% @spec (Host, NodeId, From) -> Nodes
-%%     Host   = mod_pubsub:host()
-%%     NodeId = mod_pubsub:nodeId()
-%%     From   = mod_pubsub:jid()
-%%     Nodes  = [mod_pubsub:pubsubNode()]
-get_subnodes(Host, NodeId, _From) ->
-    get_subnodes(Host, NodeId).
+get_subnodes(Host, Node, _From) ->
+    get_subnodes(Host, Node).
 
--spec(get_subnodes/2 ::
-(
-  Host   :: mod_pubsub:host(),
-  NodeId :: mod_pubsub:nodeId())
-    -> [mod_pubsub:pubsubNode()]
-).
 get_subnodes(Host, <<>>) ->
     Q = qlc:q([N
-              || #pubsub_node{nodeid = {NHost, _},
-                              parents = Parents} =
-                     N
-                     <- mnesia:table(pubsub_node),
-                 Host == NHost, Parents == []]),
+               || #pubsub_node{nodeid = {NHost, _},
+                       parents = Parents} =
+                   N
+                   <- mnesia:table(pubsub_node),
+                   Host == NHost, Parents == []]),
     qlc:e(Q);
 get_subnodes(Host, Node) ->
     Q = qlc:q([N
-              || #pubsub_node{nodeid = {NHost, _},
-                              parents = Parents} =
-                     N
-                     <- mnesia:table(pubsub_node),
-                 Host == NHost, lists:member(Node, Parents)]),
+               || #pubsub_node{nodeid = {NHost, _},
+                       parents = Parents} =
+                   N
+                   <- mnesia:table(pubsub_node),
+                   Host == NHost, lists:member(Node, Parents)]),
     qlc:e(Q).
 
 get_subnodes_tree(Host, Node, _From) ->
     get_subnodes_tree(Host, Node).
 
-%% @spec (Host, NodeId) -> Nodes
-%%     Host   = mod_pubsub:host()
-%%     NodeId = mod_pubsub:nodeId()
-%%     Nodes  = [] | [mod_pubsub:pubsubNode()]
--spec(get_subnodes_tree/2 ::
-(
-  Host :: mod_pubsub:host(),
-  NodeId :: mod_pubsub:nodeId())
-    -> [mod_pubsub:pubsubNode()]
-).
-get_subnodes_tree(Host, NodeId) ->
-    case get_node(Host, NodeId) of
-      {error, _} -> [];
-      Rec ->
-         BasePlugin = jlib:binary_to_atom(<<"node_",
-                                              (Rec#pubsub_node.type)/binary>>),
-         BasePath = BasePlugin:node_to_path(NodeId),
-         mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R,
-                           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)
+get_subnodes_tree(Host, Node) ->
+    case get_node(Host, Node) of
+       {error, _} ->
+           [];
+       Rec ->
+           BasePlugin = jlib:binary_to_atom(<<"node_",
+                       (Rec#pubsub_node.type)/binary>>),
+           BasePath = BasePlugin:node_to_path(Node),
+           mnesia:foldl(fun (#pubsub_node{nodeid = {H, N}} = R, 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.
 
-%% @spec (Host, NodeId, 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) ->
+create_node(Host, Node, Type, Owner, Options, Parents) ->
     BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    case catch mnesia:read({pubsub_node, {Host, NodeId}}) of
-      [] ->
-         ParentExists = case Host of
-                          {_U, _S, _R} ->
-                              %% This is special case for PEP handling
-                              %% PEP does not uses hierarchy
-                              true;
-                          _ ->
-                              case Parents of
-                                [] -> true;
-                                [Parent | _] ->
-                                    case catch mnesia:read({pubsub_node,
-                                                            {Host, Parent}})
-                                        of
-                                      [#pubsub_node{owners =
-                                                        [{[], Host, []}]}] ->
-                                          true;
-                                      [#pubsub_node{owners = Owners}] ->
-                                          lists:member(BJID, Owners);
-                                      _ -> false
-                                    end;
-                                _ -> false
-                              end
-                        end,
-         case ParentExists of
-           true ->
-               NodeIdx = pubsub_index:new(node),
-               mnesia:write(#pubsub_node{nodeid = {Host, NodeId},
-                                         id = NodeIdx, parents = Parents,
-                                         type = Type, owners = [BJID],
-                                         options = Options}),
-               {ok, NodeIdx};
-           false -> {error, ?ERR_FORBIDDEN}
-         end;
-      _ -> {error, ?ERR_CONFLICT}
+    case catch mnesia:read({pubsub_node, {Host, Node}}) of
+       [] ->
+           ParentExists = case Host of
+               {_U, _S, _R} ->
+                   %% This is special case for PEP handling
+                   %% PEP does not uses hierarchy
+                   true;
+               _ ->
+                   case Parents of
+                       [] ->
+                           true;
+                       [Parent | _] ->
+                           case catch mnesia:read({pubsub_node, {Host, Parent}}) of
+                               [#pubsub_node{owners = [{[], Host, []}]}] ->
+                                   true;
+                               [#pubsub_node{owners = Owners}] ->
+                                   lists:member(BJID, Owners);
+                               _ ->
+                                   false
+                           end;
+                       _ ->
+                           false
+                   end
+           end,
+           case ParentExists of
+               true ->
+                   Nidx = pubsub_index:new(node),
+                   mnesia:write(#pubsub_node{nodeid = {Host, Node},
+                           id = Nidx, parents = Parents,
+                           type = Type, owners = [BJID],
+                           options = Options}),
+                   {ok, Nidx};
+               false ->
+                   {error, ?ERR_FORBIDDEN}
+           end;
+       _ ->
+           {error, ?ERR_CONFLICT}
     end.
 
-%% @spec (Host, NodeId) -> Removed
-%%     Host    = mod_pubsub:host()
-%%     NodeId  = mod_pubsub:nodeId()
-%%     Removed = [mod_pubsub:pubsubNode()]
--spec(delete_node/2 ::
-(
-  Host :: mod_pubsub:host(),
-  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),
+delete_node(Host, Node) ->
+    Removed = get_subnodes_tree(Host, Node),
+    lists:foreach(fun (#pubsub_node{nodeid = {_, SubNode}, id = SubNidx}) ->
+               pubsub_index:free(node, SubNidx),
+               mnesia:delete({pubsub_node, {Host, SubNode}})
+       end,
+       Removed),
     Removed.
index eb966109b47956d94e0323914ba8df675910ab2c..38fb51c2afdc857ea04d1b7ea3e994038ceaf1e2 100644 (file)
 %%% improvements.</p>
 
 -module(nodetree_tree_odbc).
-
+-behaviour(gen_pubsub_nodetree).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.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,
-        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]).
+    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]).
 
 -export([raw_to_node/2]).
 
-%% ================
-%% API definition
-%% ================
+init(_Host, _ServerHost, _Opts) ->
+    ok.
 
-%% @spec (Host, ServerHost, Opts) -> any()
-%%     Host = mod_pubsub:host()
-%%     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.
 
-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) ->
-    H = (?PUBSUB):escape(Host),
-    N = (?PUBSUB):escape(Node),
+    H = node_hometree_odbc:encode_host(Host),
+    N = ejabberd_odbc:escape(Node),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
-                                       "pubsub_node where host='">>,
-                                     H, <<"' and node='">>, N, <<"';">>])
-       of
-      {selected,
-       [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
-       [RItem]} ->
-         raw_to_node(Host, RItem);
-      {'EXIT', _Reason} ->
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      _ -> {error, ?ERR_ITEM_NOT_FOUND}
+       ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+                   "pubsub_node where host='">>,
+               H, <<"' and node='">>, N, <<"';">>])
+    of
+       {selected,
+                   [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], [RItem]} ->
+           raw_to_node(Host, RItem);
+       {'EXIT', _Reason} ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR};
+       _ ->
+           {error, ?ERR_ITEM_NOT_FOUND}
     end.
 
--spec(get_node/1 ::
-(
-  NodeIdx::mod_pubsub:nodeIdx())
-    -> mod_pubsub:pubsubNode()
-     | {error, _}
-).
-get_node(NodeIdx) ->
+get_node(Nidx) ->
     case catch
-          ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
-                                       "pubsub_node where nodeid='">>,
-                                     NodeIdx, <<"';">>])
-       of
-      {selected,
-       [<<"host">>, <<"node">>, <<"parent">>, <<"type">>],
-       [[Host, Node, Parent, Type]]} ->
-         raw_to_node(Host, [Node, Parent, Type, NodeIdx]);
-      {'EXIT', _Reason} ->
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      _ -> {error, ?ERR_ITEM_NOT_FOUND}
+       ejabberd_odbc:sql_query_t([<<"select host, node, parent, type from "
+                   "pubsub_node where nodeid='">>,
+               Nidx, <<"';">>])
+    of
+       {selected,
+                   [<<"host">>, <<"node">>, <<"parent">>, <<"type">>], [[Host, Node, Parent, Type]]} ->
+           raw_to_node(Host, [Node, Parent, Type, Nidx]);
+       {'EXIT', _Reason} ->
+           {error, ?ERR_INTERNAL_SERVER_ERROR};
+       _ ->
+           {error, ?ERR_ITEM_NOT_FOUND}
     end.
 
-%% @spec (Host, From) -> [pubsubNode()] | {error, Reason}
-%%      Host = mod_pubsub:host() | mod_pubsub:jid()
-get_nodes(Host, _From) -> get_nodes(Host).
+get_nodes(Host, _From) ->
+    get_nodes(Host).
 
--spec(get_nodes/1 ::
-(
-  Host::mod_pubsub:host())
-    -> [mod_pubsub:pubsubNode()]
-).
 get_nodes(Host) ->
-    H = (?PUBSUB):escape(Host),
+    H = node_hometree_odbc:encode_host(Host),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
-                                       "pubsub_node where host='">>,
-                                     H, <<"';">>])
-       of
-      {selected,
-       [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
-       RItems} ->
-         lists:map(fun (Item) -> raw_to_node(Host, Item) end,
-                   RItems);
-      _ -> []
+       ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+                   "pubsub_node where host='">>, H, <<"';">>])
+    of
+       {selected,
+                   [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
+           [raw_to_node(Host, Item) || Item <- RItems];
+       _ ->
+           []
     end.
 
-%% @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 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()
+get_parentnodes(_Host, _Node, _From) ->
+    [].
+
 %% @doc <p>Default node tree does not handle parents, return a list
 %% 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) ->
     case get_node(Host, Node, From) of
-      N when is_record(N, pubsub_node) -> [{0, [N]}];
-      _Error -> []
+       {error, _} -> [];
+       Record -> [{0, [Record]}]
     end.
 
 get_subnodes(Host, Node, _From) ->
     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) ->
-    H = (?PUBSUB):escape(Host),
-    N = (?PUBSUB):escape(Node),
+    H = node_hometree_odbc:encode_host(Host),
+    N = ejabberd_odbc:escape(Node),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
-                                       "pubsub_node where host='">>,
-                                     H, <<"' and parent='">>, N, <<"';">>])
-       of
-      {selected,
-       [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
-       RItems} ->
-         lists:map(fun (Item) -> raw_to_node(Host, Item) end,
-                   RItems);
-      _ -> []
+       ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+                   "pubsub_node where host='">>,
+               H, <<"' and parent='">>, N, <<"';">>])
+    of
+       {selected,
+                   [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
+           [raw_to_node(Host, Item) || Item <- RItems];
+       _ ->
+           []
     end.
 
 get_subnodes_tree(Host, Node, _From) ->
     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) ->
-    H = (?PUBSUB):escape(Host),
-    N = (?PUBSUB):escape(Node),
+    H = node_hometree_odbc:encode_host(Host),
+    N = ejabberd_odbc:escape(Node),
     case catch
-          ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
-                                       "pubsub_node where host='">>,
-                                     H, <<"' and node like '">>, N, <<"%';">>])
-       of
-      {selected,
-       [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>],
-       RItems} ->
-         lists:map(fun (Item) -> raw_to_node(Host, Item) end,
-                   RItems);
-      _ -> []
+       ejabberd_odbc:sql_query_t([<<"select node, parent, type, nodeid from "
+                   "pubsub_node where host='">>,
+               H, <<"' and node like '">>, N, <<"%';">>])
+    of
+       {selected,
+                   [<<"node">>, <<"parent">>, <<"type">>, <<"nodeid">>], RItems} ->
+           [raw_to_node(Host, Item) || Item <- RItems];
+       _ ->
+           []
     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) ->
     BJID = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
-    case nodeid(Host, Node) of
-      {error, ?ERR_ITEM_NOT_FOUND} ->
-         ParentExists = case Host of
-                          {_U, _S, _R} ->
-                              %% This is special case for PEP handling
-                              %% PEP does not uses hierarchy
-                              true;
-                          _ ->
-                              case Parents of
-                                [] -> true;
-                                [Parent | _] ->
-                                    case nodeid(Host, Parent) of
-                                      {result, PNodeId} ->
-                                          case nodeowners(PNodeId) of
-                                            [{<<>>, Host, <<>>}] -> true;
-                                            Owners ->
-                                                lists:member(BJID, Owners)
-                                          end;
-                                      _ -> false
-                                    end;
-                                _ -> false
-                              end
-                        end,
-         case ParentExists of
-           true ->
-               case set_node(#pubsub_node{nodeid = {Host, Node},
-                                          parents = Parents, type = Type,
-                                          options = Options})
+    case nodeidx(Host, Node) of
+       {error, ?ERR_ITEM_NOT_FOUND} ->
+           ParentExists = case Host of
+               {_U, _S, _R} ->
+                   %% This is special case for PEP handling
+                   %% PEP does not uses hierarchy
+                   true;
+               _ ->
+                   case Parents of
+                       [] ->
+                           true;
+                       [Parent | _] ->
+                           case nodeidx(Host, Parent) of
+                               {result, PNode} ->
+                                   case nodeowners(PNode) of
+                                       [{<<>>, Host, <<>>}] -> true;
+                                       Owners -> lists:member(BJID, Owners)
+                                   end;
+                               _ ->
+                                   false
+                           end;
+                       _ ->
+                           false
+                   end
+           end,
+           case ParentExists of
+               true ->
+                   case set_node(#pubsub_node{nodeid = {Host, Node},
+                               parents = Parents, type = Type,
+                               options = Options})
                    of
-                 {result, NodeId} -> {ok, NodeId};
-                 Other -> Other
-               end;
-           false -> {error, ?ERR_FORBIDDEN}
-         end;
-      {result, _} -> {error, ?ERR_CONFLICT};
-      Error -> Error
+                       {result, Nidx} -> {ok, Nidx};
+                       Other -> Other
+                   end;
+               false ->
+                   {error, ?ERR_FORBIDDEN}
+           end;
+       {result, _} ->
+           {error, ?ERR_CONFLICT};
+       Error ->
+           Error
     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) ->
-    H = (?PUBSUB):escape(Host),
-    N = (?PUBSUB):escape(Node),
+    H = node_hometree_odbc:encode_host(Host),
+    N = ejabberd_odbc:escape(Node),
     Removed = get_subnodes_tree(Host, Node),
-    catch
-      ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
-                                H, <<"' and node like '">>, N, <<"%';">>]),
+    catch ejabberd_odbc:sql_query_t([<<"delete from pubsub_node where host='">>,
+           H, <<"' and node like '">>, N, <<"%';">>]),
     Removed.
 
 %% helpers
--spec(raw_to_node/2 ::
-(
-  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]) ->
+raw_to_node(Host, [Node, Parent, Type, Nidx]) ->
     Options = case catch
-                    ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
-                                                 "where nodeid='">>,
-                                               NodeIdx, <<"';">>])
-                 of
-               {selected, [<<"name">>, <<"val">>], ROptions} ->
-                   DbOpts = lists:map(fun ([Key, Value]) ->
-                                              RKey =
-                                                  jlib:binary_to_atom(Key),
-                                              Tokens = element(2,
-                                                               erl_scan:string(
-                                                                   binary_to_list(<<Value/binary, ".">>))),
-                                              RValue = element(2,
-                                                               erl_parse:parse_term(Tokens)),
-                                              {RKey, RValue}
-                                      end,
-                                      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,
+       ejabberd_odbc:sql_query_t([<<"select name,val from pubsub_node_option "
+                   "where nodeid='">>, Nidx, <<"';">>])
+    of
+       {selected, [<<"name">>, <<"val">>], ROptions} ->
+           DbOpts = lists:map(fun ([Key, Value]) ->
+                           RKey = jlib:binary_to_atom(Key),
+                           Tokens = element(2, erl_scan:string(binary_to_list(<<Value/binary, ".">>))),
+                           RValue = element(2, erl_parse:parse_term(Tokens)),
+                           {RKey, RValue}
+                   end,
+                   ROptions),
+           Module = jlib:binary_to_atom(<<"node_", Type/binary, "_odbc">>),
+           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),
+       <<>> -> [];
+       _ -> [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">>], [[NodeIdx]]} ->
-         {result, NodeIdx};
-      {'EXIT', _Reason} ->
-         {error, ?ERR_INTERNAL_SERVER_ERROR};
-      _ -> {error, ?ERR_ITEM_NOT_FOUND}
+       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.
 
--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,
-               [], Res).
+nodeowners(Nidx) ->
+    {result, Res} = node_hometree_odbc:get_node_affiliations(Nidx),
+    [LJID || {LJID, Aff} <- Res, Aff =:= owner].
index abe88560fca739232f2f2889339ecf69f81acba0..d56be860fc164dcd6db42dc43634fee3b83e654e 100644 (file)
 %%% ====================================================================
 
 %%% @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>This plugin development is still a work in progress. Due to optimizations in
 %%% mod_pubsub, this plugin can not work anymore without altering functioning.
 %%% Please, send us comments, feedback and improvements.</p>
 
 -module(nodetree_virtual).
-
+-behaviour(gen_pubsub_nodetree).
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.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]).
-
-%% ================
-%% API definition
-%% ================
-
-%% @spec (Host, ServerHost, Opts) -> any()
-%%     Host = mod_pubsub:host()
-%%     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}].
-
-set_node(_NodeRecord) -> ok.
-
-get_node(Host, Node, _From) -> get_node(Host, Node).
-
-get_node(Host, Node) -> get_node({Host, Node}).
-
-get_node({Host, _} = NodeId) ->
-    Record = #pubsub_node{nodeid = NodeId, id = NodeId},
-    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()]
-%%     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) -> [].
-
--spec(get_parentnodes_tree/3 ::
-(
-  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 -> []
+    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]).
+
+init(_Host, _ServerHost, _Opts) ->
+    ok.
+
+terminate(_Host, _ServerHost) ->
+    ok.
+
+options() ->
+    [{virtual_tree, true}].
+
+set_node(_Node) ->
+    ok.
+
+get_node(Host, Node, _From) ->
+    get_node(Host, Node).
+
+get_node(Host, Node) ->
+    get_node(nodeidx(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_nodes(Host, _From) ->
+    get_nodes(Host).
+
+get_nodes(_Host) ->
+    [].
+
+get_parentnodes(_Host, _Node, _From) ->
+    [].
+
+get_parentnodes_tree(Host, Node, From) ->
+    case get_node(Host, Node, From) of
+       Node when is_record(Node, pubsub_node) -> [{0, [Node]}];
+       _Error -> []
     end.
 
 get_subnodes(Host, Node, _From) ->
     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).
-%% @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) -> [].
-
-create_node(Host, Node, _Type, _Owner, _Options,
-           _Parents) ->
+
+get_subnodes_tree(_Host, _Node) ->
+    [].
+
+create_node(Host, Node, _Type, _Owner, _Options, _Parents) ->
     {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).
index f22ee196830e60c0c2ea2bca3d14f71b7229ced1..56a500ebbfcf79fb164976ee0fe5cb48b5350258 100644 (file)
 -include("pubsub.hrl").
 
 -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.
 %% They can be enabled again in ejabberd 3.0 because it uses R12B or higher.
 %% -spec read_subscription(SubID :: string()) -> {ok, #pubsub_subscription{}} |  notfound.
 read_subscription(SubID) ->
     case
-      ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
-                                  "iption_opt where subid = '">>,
-                                ejabberd_odbc:escape(SubID), <<"'">>])
-       of
-      {selected, [<<"opt_name">>, <<"opt_value">>], []} ->
-         notfound;
-      {selected, [<<"opt_name">>, <<"opt_value">>],
-       Options} ->
-         {ok,
-          #pubsub_subscription{subid = SubID,
-                               options =
-                                   lists:map(fun subscription_opt_from_odbc/1,
-                                             Options)}}
+       ejabberd_odbc:sql_query_t([<<"select opt_name, opt_value from pubsub_subscr"
+                   "iption_opt where subid = '">>,
+               ejabberd_odbc:escape(SubID), <<"'">>])
+    of
+       {selected, [<<"opt_name">>, <<"opt_value">>], []} ->
+           notfound;
+       {selected, [<<"opt_name">>, <<"opt_value">>], Options} ->
+           {ok,
+               #pubsub_subscription{subid = SubID,
+                   options = lists:map(fun subscription_opt_from_odbc/1, Options)}}
     end.
 
 %% -spec delete_subscription(SubID :: string()) -> ok.
 delete_subscription(SubID) ->
-%% -spec update_subscription(#pubsub_subscription{}) -> ok .
-%% -spec add_subscription(#pubsub_subscription{}) -> ok.
-%% -------------- Internal utilities -----------------------
+    %% -spec update_subscription(#pubsub_subscription{}) -> ok .
+    %% -spec add_subscription(#pubsub_subscription{}) -> ok.
+    %% -------------- Internal utilities -----------------------
     ejabberd_odbc:sql_query_t([<<"delete from pubsub_subscription_opt "
-                                "where subid = '">>,
-                              ejabberd_odbc:escape(SubID), <<"'">>]),
+               "where subid = '">>,
+           ejabberd_odbc:escape(SubID), <<"'">>]),
     ok.
 
-update_subscription(#pubsub_subscription{subid =
-                                            SubId} =
-                       Sub) ->
+update_subscription(#pubsub_subscription{subid = SubId} = Sub) ->
     delete_subscription(SubId), add_subscription(Sub).
 
-add_subscription(#pubsub_subscription{subid = SubId,
-                                     options = Opts}) ->
+add_subscription(#pubsub_subscription{subid = SubId, options = Opts}) ->
     EscapedSubId = ejabberd_odbc:escape(SubId),
     lists:foreach(fun (Opt) ->
-                         {OdbcOptName, OdbcOptValue} =
-                             subscription_opt_to_odbc(Opt),
-                         ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
-                                                      "opt_name, opt_value)values ('">>,
-                                                    EscapedSubId, <<"','">>,
-                                                    OdbcOptName, <<"','">>,
-                                                    OdbcOptValue, <<"')">>])
-                 end,
-                 Opts),
+               {OdbcOptName, OdbcOptValue} = subscription_opt_to_odbc(Opt),
+               ejabberd_odbc:sql_query_t([<<"insert into pubsub_subscription_opt(subid, "
+                           "opt_name, opt_value)values ('">>,
+                       EscapedSubId, <<"','">>,
+                       OdbcOptName, <<"','">>,
+                       OdbcOptValue, <<"')">>])
+       end,
+       Opts),
     ok.
 
 subscription_opt_from_odbc({<<"DELIVER">>, Value}) ->
     {deliver, odbc_to_boolean(Value)};
 subscription_opt_from_odbc({<<"DIGEST">>, Value}) ->
     {digest, odbc_to_boolean(Value)};
-subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>,
-                           Value}) ->
+subscription_opt_from_odbc({<<"DIGEST_FREQUENCY">>, Value}) ->
     {digest_frequency, odbc_to_integer(Value)};
 subscription_opt_from_odbc({<<"EXPIRE">>, Value}) ->
     {expire, odbc_to_timestamp(Value)};
-subscription_opt_from_odbc({<<"INCLUDE_BODY">>,
-                           Value}) ->
+subscription_opt_from_odbc({<<"INCLUDE_BODY">>, Value}) ->
     {include_body, odbc_to_boolean(Value)};
 %%TODO: might be > than 1 show_values value??.
 %%      need to use compact all in only 1 opt.
-subscription_opt_from_odbc({<<"SHOW_VALUES">>,
-                           Value}) ->
+subscription_opt_from_odbc({<<"SHOW_VALUES">>, Value}) ->
     {show_values, Value};
-subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>,
-                           Value}) ->
+subscription_opt_from_odbc({<<"SUBSCRIPTION_TYPE">>, Value}) ->
     {subscription_type,
-     case Value of
-       <<"items">> -> items;
-       <<"nodes">> -> nodes
-     end};
-subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>,
-                           Value}) ->
+       case Value of
+           <<"items">> -> items;
+           <<"nodes">> -> nodes
+       end};
+subscription_opt_from_odbc({<<"SUBSCRIPTION_DEPTH">>, Value}) ->
     {subscription_depth,
-     case Value of
-       <<"all">> -> all;
-       N -> odbc_to_integer(N)
-     end}.
+       case Value of
+           <<"all">> -> all;
+           N -> odbc_to_integer(N)
+       end}.
 
 subscription_opt_to_odbc({deliver, Bool}) ->
     {<<"DELIVER">>, boolean_to_odbc(Bool)};
@@ -124,19 +112,18 @@ subscription_opt_to_odbc({show_values, Values}) ->
     {<<"SHOW_VALUES">>, Values};
 subscription_opt_to_odbc({subscription_type, Type}) ->
     {<<"SUBSCRIPTION_TYPE">>,
-     case Type of
-       items -> <<"items">>;
-       nodes -> <<"nodes">>
-     end};
+       case Type of
+           items -> <<"items">>;
+           nodes -> <<"nodes">>
+       end};
 subscription_opt_to_odbc({subscription_depth, Depth}) ->
     {<<"SUBSCRIPTION_DEPTH">>,
-     case Depth of
-       all -> <<"all">>;
-       N -> integer_to_odbc(N)
-     end}.
+       case Depth of
+           all -> <<"all">>;
+           N -> integer_to_odbc(N)
+       end}.
 
-integer_to_odbc(N) ->
-    iolist_to_binary(integer_to_list(N)).
+integer_to_odbc(N) -> iolist_to_binary(integer_to_list(N)).
 
 boolean_to_odbc(true) -> <<"1">>;
 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_timestamp(T) ->
-    jlib:datetime_string_to_timestamp(T).
+odbc_to_timestamp(T) -> jlib:datetime_string_to_timestamp(T).
index 94efa0e963c85909564a61336ebeb3c5a2e885ee..c9ec94a6252ddb659cba68d0ddbd0cd88fc2baaa 100644 (file)
@@ -29,7 +29,6 @@
 %% new/1 and free/2 MUST be called inside a transaction bloc
 
 -module(pubsub_index).
-
 -author('christophe.romain@process-one.net').
 
 -include("pubsub.hrl").
 
 init(_Host, _ServerHost, _Opts) ->
     mnesia:create_table(pubsub_index,
-                       [{disc_copies, [node()]},
-                        {attributes, record_info(fields, pubsub_index)}]).
+       [{disc_copies, [node()]},
+           {attributes, record_info(fields, pubsub_index)}]).
 
 new(Index) ->
     case mnesia:read({pubsub_index, Index}) of
-      [I] ->
-         case I#pubsub_index.free of
-           [] ->
-               Id = I#pubsub_index.last + 1,
-               mnesia:write(I#pubsub_index{last = Id}),
-               Id;
-           [Id | Free] ->
-               mnesia:write(I#pubsub_index{free = Free}), Id
-         end;
-      _ ->
-         mnesia:write(#pubsub_index{index = Index, last = 1,
-                                    free = []}),
-         1
+       [I] ->
+           case I#pubsub_index.free of
+               [] ->
+                   Id = I#pubsub_index.last + 1,
+                   mnesia:write(I#pubsub_index{last = Id}),
+                   Id;
+               [Id | Free] ->
+                   mnesia:write(I#pubsub_index{free = Free}), Id
+           end;
+       _ ->
+           mnesia:write(#pubsub_index{index = Index, last = 1, free = []}),
+           1
     end.
 
 free(Index, Id) ->
     case mnesia:read({pubsub_index, Index}) of
-      [I] ->
-         Free = I#pubsub_index.free,
-         mnesia:write(I#pubsub_index{free = [Id | Free]});
-      _ -> ok
+       [I] ->
+           Free = I#pubsub_index.free,
+           mnesia:write(I#pubsub_index{free = [Id | Free]});
+       _ ->
+           ok
     end.
diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl
new file mode 100644 (file)
index 0000000..e48efcd
--- /dev/null
@@ -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().
index 1e3f18e7bc6fc272e777560603f0d4c689aaf417..df1560005c7e42bfdb384a9656a1d959a9fc009c 100644 (file)
 
 %% API
 -export([init/0, subscribe_node/3, unsubscribe_node/3,
-        get_subscription/3, set_subscription/4,
-        get_options_xform/2, parse_options_xform/1]).
+    get_subscription/3, set_subscription/4,
+    get_options_xform/2, parse_options_xform/1]).
 
 % Internal function also exported for use in transactional bloc from pubsub plugins
 -export([add_subscription/3, delete_subscription/3,
-        read_subscription/3, write_subscription/4]).
+    read_subscription/3, write_subscription/4]).
 
 -include("pubsub.hrl").
 
 -include("jlib.hrl").
 
 -define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-
 -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_INCLUDE_BODY, <<"pubsub#include_body">>).
-
 -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-
--define(PUBSUB_SUBSCRIPTION_TYPE,
-       <<"pubsub#subscription_type">>).
-
--define(PUBSUB_SUBSCRIPTION_DEPTH,
-       <<"pubsub#subscription_depth">>).
-
--define(DELIVER_LABEL,
-       <<"Whether an entity wants to receive or "
-         "disable notifications">>).
-
--define(DIGEST_LABEL,
-       <<"Whether an entity wants to receive digests "
-         "(aggregations) of notifications or all "
-         "notifications individually">>).
-
--define(DIGEST_FREQUENCY_LABEL,
-       <<"The minimum number of milliseconds between "
-         "sending any two notification digests">>).
-
--define(EXPIRE_LABEL,
-       <<"The DateTime at which a leased subscription "
-         "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(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
+-define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
+-define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
+-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
+       "(aggregations) of notifications or all notifications individually">>).
+-define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
+       "sending any two notification digests">>).
+-define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription 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
 %%====================================================================
 init() -> ok = create_table().
 
-subscribe_node(JID, NodeID, Options) ->
-    case catch mnesia:sync_dirty(fun add_subscription/3,
-                                [JID, NodeID, Options])
-       of
-      {'EXIT', {aborted, Error}} -> Error;
-      {error, Error} -> {error, Error};
-      Result -> {result, Result}
+subscribe_node(JID, NodeId, Options) ->
+    case catch mnesia:sync_dirty(fun add_subscription/3, [JID, NodeId, Options])
+    of
+       {'EXIT', {aborted, Error}} -> Error;
+       {error, Error} -> {error, Error};
+       Result -> {result, Result}
     end.
 
-unsubscribe_node(JID, NodeID, SubID) ->
-    case catch mnesia:sync_dirty(fun delete_subscription/3,
-                                [JID, NodeID, SubID])
-       of
-      {'EXIT', {aborted, Error}} -> Error;
-      {error, Error} -> {error, Error};
-      Result -> {result, Result}
+unsubscribe_node(JID, NodeId, SubID) ->
+    case catch mnesia:sync_dirty(fun delete_subscription/3, [JID, NodeId, SubID])
+    of
+       {'EXIT', {aborted, Error}} -> Error;
+       {error, Error} -> {error, Error};
+       Result -> {result, Result}
     end.
 
-get_subscription(JID, NodeID, SubID) ->
-    case catch mnesia:sync_dirty(fun read_subscription/3,
-                                [JID, NodeID, SubID])
-       of
-      {'EXIT', {aborted, Error}} -> Error;
-      {error, Error} -> {error, Error};
-      Result -> {result, Result}
+get_subscription(JID, NodeId, SubID) ->
+    case catch mnesia:sync_dirty(fun read_subscription/3, [JID, NodeId, SubID])
+    of
+       {'EXIT', {aborted, Error}} -> Error;
+       {error, Error} -> {error, Error};
+       Result -> {result, Result}
     end.
 
-set_subscription(JID, NodeID, SubID, Options) ->
-    case catch mnesia:sync_dirty(fun write_subscription/4,
-                                [JID, NodeID, SubID, Options])
-       of
-      {'EXIT', {aborted, Error}} -> Error;
-      {error, Error} -> {error, Error};
-      Result -> {result, Result}
+set_subscription(JID, NodeId, SubID, Options) ->
+    case catch mnesia:sync_dirty(fun write_subscription/4, [JID, NodeId, SubID, Options])
+    of
+       {'EXIT', {aborted, Error}} -> Error;
+       {error, Error} -> {error, Error};
+       Result -> {result, Result}
     end.
 
 
 get_options_xform(Lang, Options) ->
-    Keys = [deliver, show_values, subscription_type,
-           subscription_depth],
-    XFields = [get_option_xfield(Lang, Key, Options)
-              || Key <- Keys],
+    Keys = [deliver, show_values, subscription_type, subscription_depth],
+    XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
     {result,
-     #xmlel{name = <<"x">>,
+       #xmlel{name = <<"x">>,
            attrs = [{<<"xmlns">>, ?NS_XDATA}],
            children =
-               [#xmlel{name = <<"field">>,
-                       attrs =
-                           [{<<"var">>, <<"FORM_TYPE">>},
-                            {<<"type">>, <<"hidden">>}],
-                       children =
-                           [#xmlel{name = <<"value">>, attrs = [],
-                                   children =
-                                       [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
-                 ++ XFields}}.
+           [#xmlel{name = <<"field">>,
+                   attrs =
+                   [{<<"var">>, <<"FORM_TYPE">>},
+                       {<<"type">>, <<"hidden">>}],
+                   children =
+                   [#xmlel{name = <<"value">>, attrs = [],
+                           children =
+                           [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
+           ++ XFields}}.
 
 parse_options_xform(XFields) ->
     case xml:remove_cdata(XFields) of
-      [#xmlel{name = <<"x">>} = XEl] ->
-         case jlib:parse_xdata_submit(XEl) of
-           XData when is_list(XData) ->
-               Opts = set_xoption(XData, []),
-               {result, Opts};
-           Other -> Other
-         end;
-      _ -> {result, []}
+       [#xmlel{name = <<"x">>} = XEl] ->
+           case jlib:parse_xdata_submit(XEl) of
+               XData when is_list(XData) ->
+                   Opts = set_xoption(XData, []),
+                   {result, Opts};
+               Other -> Other
+           end;
+       _ -> {result, []}
     end.
 
 %%====================================================================
@@ -196,69 +138,67 @@ parse_options_xform(XFields) ->
 %%====================================================================
 create_table() ->
     case mnesia:create_table(pubsub_subscription,
-                            [{disc_copies, [node()]},
-                             {attributes,
-                              record_info(fields, pubsub_subscription)},
-                             {type, set}])
-       of
-      {atomic, ok} -> ok;
-      {aborted, {already_exists, _}} -> ok;
-      Other -> Other
+           [{disc_copies, [node()]},
+               {attributes,
+                   record_info(fields, pubsub_subscription)},
+               {type, set}])
+    of
+       {atomic, ok} -> ok;
+       {aborted, {already_exists, _}} -> ok;
+       Other -> Other
     end.
 
 -spec(add_subscription/3 ::
-(
-  _JID    :: ljid(),
-  _NodeID :: mod_pubsub:nodeIdx(),
-  Options :: [] | mod_pubsub:subOptions())
+    (
+       _JID    :: ljid(),
+       _NodeId :: mod_pubsub:nodeIdx(),
+       Options :: [] | mod_pubsub:subOptions())
     -> SubId :: mod_pubsub:subId()
-).
+    ).
 
-add_subscription(_JID, _NodeID, []) -> make_subid();
-add_subscription(_JID, _NodeID, Options) ->
+add_subscription(_JID, _NodeId, []) -> make_subid();
+add_subscription(_JID, _NodeId, Options) ->
     SubID = make_subid(),
-    mnesia:write(#pubsub_subscription{subid = SubID,
-                                     options = Options}),
+    mnesia:write(#pubsub_subscription{subid = SubID, options = Options}),
     SubID.
 
 -spec(delete_subscription/3 ::
-(
-  _JID    :: _,
-  _NodeID :: _,
-  SubId   :: mod_pubsub:subId())
+    (
+       _JID    :: _,
+       _NodeId :: _,
+       SubId   :: mod_pubsub:subId())
     -> ok
-).
+    ).
 
-delete_subscription(_JID, _NodeID, SubID) ->
+delete_subscription(_JID, _NodeId, SubID) ->
     mnesia:delete({pubsub_subscription, SubID}).
 
 -spec(read_subscription/3 ::
-(
-  _JID    :: ljid(),
-  _NodeID :: _,
-  SubID   :: mod_pubsub:subId())
+    (
+       _JID    :: ljid(),
+       _NodeId :: _,
+       SubID   :: mod_pubsub:subId())
     -> mod_pubsub:pubsubSubscription()
-     | {error, notfound}
-).
+    | {error, notfound}
+    ).
 
-read_subscription(_JID, _NodeID, SubID) ->
+read_subscription(_JID, _NodeId, SubID) ->
     case mnesia:read({pubsub_subscription, SubID}) of
-      [Sub] -> Sub;
-      _ -> {error, notfound}
+       [Sub] -> Sub;
+       _ -> {error, notfound}
     end.
 
 -spec(write_subscription/4 ::
-(
-  _JID    :: ljid(),
-  _NodeID :: _,
-  SubID   :: mod_pubsub:subId(),
-  Options :: mod_pubsub:subOptions())
+    (
+       _JID    :: ljid(),
+       _NodeId :: _,
+       SubID   :: mod_pubsub:subId(),
+       Options :: mod_pubsub:subOptions())
     -> ok
-).
+    ).
 
-write_subscription(_JID, _NodeID, SubID, Options) ->
-    mnesia:write(#pubsub_subscription{subid = SubID,
-                                     options = Options}).
+write_subscription(_JID, _NodeId, SubID, Options) ->
+    mnesia:write(#pubsub_subscription{subid = SubID, options = Options}).
 
 -spec(make_subid/0 :: () -> SubId::mod_pubsub:subId()).
 make_subid() ->
@@ -274,43 +214,42 @@ make_subid() ->
 set_xoption([], Opts) -> Opts;
 set_xoption([{Var, Value} | T], Opts) ->
     NewOpts = case var_xfield(Var) of
-               {error, _} -> Opts;
-               Key ->
-                   Val = val_xfield(Key, Value),
-                   lists:keystore(Key, 1, Opts, {Key, Val})
-             end,
+       {error, _} -> Opts;
+       Key ->
+           Val = val_xfield(Key, Value),
+           lists:keystore(Key, 1, Opts, {Key, Val})
+    end,
     set_xoption(T, NewOpts).
 
 %% Return the options list's key for an XForm var.
 %% Convert Values for option list's Key.
 var_xfield(?PUBSUB_DELIVER) -> deliver;
 var_xfield(?PUBSUB_DIGEST) -> digest;
-var_xfield(?PUBSUB_DIGEST_FREQUENCY) ->
-    digest_frequency;
+var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
 var_xfield(?PUBSUB_EXPIRE) -> expire;
 var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
 var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
-var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) ->
-    subscription_type;
-var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
-    subscription_depth;
+var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
+var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
 var_xfield(_) -> {error, badarg}.
 
 val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
-%val_xfield(digest, [Val]) -> xopt_to_bool(Val);
-%val_xfield(digest_frequency, [Val]) ->
-%    jlib:binary_to_integer(Val);
-%val_xfield(expire, [Val]) ->
-%    jlib:datetime_string_to_timestamp(Val);
-%val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
+val_xfield(digest, [Val]) -> xopt_to_bool(Val);
+val_xfield(digest_frequency, [Val]) ->
+    case catch jlib:binary_to_integer(Val) of
+       N when is_integer(N) -> N;
+       _ -> {error, ?ERR_NOT_ACCEPTABLE}
+    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(subscription_type, [<<"items">>]) -> items;
 val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
 val_xfield(subscription_depth, [<<"all">>]) -> all;
 val_xfield(subscription_depth, [Depth]) ->
     case catch jlib:binary_to_integer(Depth) of
-      N when is_integer(N) -> N;
-      _ -> {error, ?ERR_NOT_ACCEPTABLE}
+       N when is_integer(N) -> N;
+       _ -> {error, ?ERR_NOT_ACCEPTABLE}
     end.
 
 %% Convert XForm booleans to Erlang booleans.
@@ -321,31 +260,30 @@ xopt_to_bool(<<"true">>) -> true;
 xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
 
 -spec(get_option_xfield/3 ::
-(
-  Lang :: binary(),
-  Key  :: atom(),
-  Options :: mod_pubsub:subOptions())
+    (
+       Lang :: binary(),
+       Key  :: atom(),
+       Options :: mod_pubsub:subOptions())
     -> xmlel()
-).
+    ).
 
 %% Return a field for an XForm for Key, with data filled in, if
 %% applicable, from Options.
 get_option_xfield(Lang, Key, Options) ->
     Var = xfield_var(Key),
     Label = xfield_label(Key),
-    {Type, OptEls} = type_and_options(xfield_type(Key),
-                                     Lang),
+    {Type, OptEls} = type_and_options(xfield_type(Key), Lang),
     Vals = case lists:keysearch(Key, 1, Options) of
-            {value, {_, Val}} ->
-                [tr_xfield_values(Vals)
-                 || Vals <- xfield_val(Key, Val)];
-            false -> []
-          end,
+       {value, {_, Val}} ->
+           [tr_xfield_values(Vals)
+               || Vals <- xfield_val(Key, Val)];
+       false -> []
+    end,
     #xmlel{name = <<"field">>,
-          attrs =
-              [{<<"var">>, Var}, {<<"type">>, Type},
-               {<<"label">>, translate:translate(Lang, Label)}],
-          children = OptEls ++ Vals}.
+       attrs =
+       [{<<"var">>, Var}, {<<"type">>, Type},
+           {<<"label">>, translate:translate(Lang, Label)}],
+       children = OptEls ++ Vals}.
 
 type_and_options({Type, Options}, Lang) ->
     {Type, [tr_xfield_options(O, Lang) || O <- Options]};
@@ -353,42 +291,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
 
 tr_xfield_options({Value, Label}, Lang) ->
     #xmlel{name = <<"option">>,
-          attrs =
-              [{<<"label">>, translate:translate(Lang, Label)}],
-          children =
-              [#xmlel{name = <<"value">>, attrs = [],
-                      children = [{xmlcdata, Value}]}]}.
+       attrs =
+       [{<<"label">>, translate:translate(Lang, Label)}],
+       children =
+       [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, Value}]}]}.
 
 tr_xfield_values(Value) ->
-%% Return the XForm variable name for a subscription option key.
-%% Return the XForm variable type 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.
     #xmlel{name = <<"value">>, attrs = [],
-          children = [{xmlcdata, Value}]}.
-
--spec(xfield_var/1 ::
-(
-  Var :: 'deliver'
-%       | 'digest'
-%       | 'digest_frequency'
-%       | 'expire'
-%       | 'include_body'
-       | 'show_values'
-       | 'subscription_type'
-       | 'subscription_depth')
-    -> binary()
-).
+       children = [{xmlcdata, Value}]}.
 
 xfield_var(deliver) -> ?PUBSUB_DELIVER;
 %xfield_var(digest) -> ?PUBSUB_DIGEST;
-%xfield_var(digest_frequency) ->
-%    ?PUBSUB_DIGEST_FREQUENCY;
+%xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
 %xfield_var(expire) -> ?PUBSUB_EXPIRE;
 %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
 xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
-xfield_var(subscription_type) ->
-    ?PUBSUB_SUBSCRIPTION_TYPE;
-xfield_var(subscription_depth) ->
-    ?PUBSUB_SUBSCRIPTION_DEPTH.
+xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
+xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
 
 xfield_type(deliver) -> <<"boolean">>;
 %xfield_type(digest) -> <<"boolean">>;
@@ -397,52 +319,31 @@ xfield_type(deliver) -> <<"boolean">>;
 %xfield_type(include_body) -> <<"boolean">>;
 xfield_type(show_values) ->
     {<<"list-multi">>,
-     [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
-      {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
-      {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
-      {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
-      {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
+       [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
+           {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
+           {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
+           {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
+           {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
 xfield_type(subscription_type) ->
     {<<"list-single">>,
-     [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
-      {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
+       [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
+           {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
 xfield_type(subscription_depth) ->
     {<<"list-single">>,
-     [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
-      {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
+       [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
+           {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
 
 %% Return the XForm variable label for a subscription option key.
 xfield_label(deliver) -> ?DELIVER_LABEL;
 %xfield_label(digest) -> ?DIGEST_LABEL;
-%xfield_label(digest_frequency) ->
-%    ?DIGEST_FREQUENCY_LABEL;
+%xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
 %xfield_label(expire) -> ?EXPIRE_LABEL;
 %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
 xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
 %% Return the XForm value for a subscription option key.
 %% Convert erlang booleans to XForms.
-xfield_label(subscription_type) ->
-    ?SUBSCRIPTION_TYPE_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_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
+xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
 
 xfield_val(deliver, 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))];
 %xfield_val(expire, 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(subscription_type, items) -> [<<"items">>];
 xfield_val(subscription_type, nodes) -> [<<"nodes">>];
index b54733aaadf0556ef0da33be7ca926623e9e9c64..6c99b155dd5344b6a871cb5c98c5acb68ebfe5b1 100644 (file)
 %%% ====================================================================
 
 -module(pubsub_subscription_odbc).
-
 -author("pablo.polvorin@process-one.net").
 
 %% API
 -export([init/0, subscribe_node/3, unsubscribe_node/3,
-        get_subscription/3, set_subscription/4,
-        get_options_xform/2, parse_options_xform/1]).
+    get_subscription/3, set_subscription/4,
+    get_options_xform/2, parse_options_xform/1]).
 
 -include("pubsub.hrl").
 
 -include("jlib.hrl").
 
 -define(PUBSUB_DELIVER, <<"pubsub#deliver">>).
-
 -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_INCLUDE_BODY, <<"pubsub#include_body">>).
-
 -define(PUBSUB_SHOW_VALUES, <<"pubsub#show-values">>).
-
--define(PUBSUB_SUBSCRIPTION_TYPE,
-       <<"pubsub#subscription_type">>).
-
--define(PUBSUB_SUBSCRIPTION_DEPTH,
-       <<"pubsub#subscription_depth">>).
-
--define(DELIVER_LABEL,
-       <<"Whether an entity wants to receive or "
-         "disable notifications">>).
-
--define(DIGEST_LABEL,
-       <<"Whether an entity wants to receive digests "
-         "(aggregations) of notifications or all "
-         "notifications individually">>).
-
--define(DIGEST_FREQUENCY_LABEL,
-       <<"The minimum number of milliseconds between "
-         "sending any two notification digests">>).
-
--define(EXPIRE_LABEL,
-       <<"The DateTime at which a leased subscription "
-         "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(PUBSUB_SUBSCRIPTION_TYPE, <<"pubsub#subscription_type">>).
+-define(PUBSUB_SUBSCRIPTION_DEPTH, <<"pubsub#subscription_depth">>).
+-define(DELIVER_LABEL, <<"Whether an entity wants to receive or disable notifications">>).
+-define(DIGEST_LABEL, <<"Whether an entity wants to receive digests "
+       "(aggregations) of notifications or all notifications individually">>).
+-define(DIGEST_FREQUENCY_LABEL, <<"The minimum number of milliseconds between "
+       "sending any two notification digests">>).
+-define(EXPIRE_LABEL, <<"The DateTime at which a leased subscription 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).
 %%====================================================================
 init() -> ok = create_table().
 
 -spec(subscribe_node/3 ::
-(
-  _JID    :: _,
-  _NodeID :: _,
-  Options :: mod_pubsub:subOptions())
+    (
+       _JID    :: _,
+       _NodeId :: _,
+       Options :: [] | mod_pubsub:subOptions())
     -> {result, mod_pubsub:subId()}
-).
-subscribe_node(_JID, _NodeID, Options) ->
+    ).
+subscribe_node(_JID, _NodeId, Options) ->
     SubID = make_subid(),
-    (?DB_MOD):add_subscription(#pubsub_subscription{subid =
-                                                       SubID,
-                                                   options = Options}),
+    (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID, options = Options}),
     {result, SubID}.
 
 -spec(unsubscribe_node/3 ::
-(
-  _JID :: _,
-  _NodeID :: _,
-  SubID :: mod_pubsub:subId())
+    (
+       _JID :: _,
+       _NodeId :: _,
+       SubID :: mod_pubsub:subId())
     -> {result, mod_pubsub:subscription()}
-     | {error, notfound}
-).
-unsubscribe_node(_JID, _NodeID, SubID) ->
+    | {error, notfound}
+    ).
+unsubscribe_node(_JID, _NodeId, SubID) ->
     case (?DB_MOD):read_subscription(SubID) of
-      {ok, Sub} ->
-         (?DB_MOD):delete_subscription(SubID), {result, Sub};
-      notfound -> {error, notfound}
+       {ok, Sub} -> (?DB_MOD):delete_subscription(SubID), {result, Sub};
+       notfound -> {error, notfound}
     end.
 
 -spec(get_subscription/3 ::
-(
-  _JID    :: _,
-  _NodeID :: _,
-  SubId   :: mod_pubsub:subId())
+    (
+       _JID    :: _,
+       _NodeId :: _,
+       SubId   :: mod_pubsub:subId())
     -> {result, mod_pubsub:subscription()}
-     | {error, notfound}
-).
-get_subscription(_JID, _NodeID, SubID) ->
+    | {error, notfound}
+    ).
+get_subscription(_JID, _NodeId, SubID) ->
     case (?DB_MOD):read_subscription(SubID) of
-      {ok, Sub} -> {result, Sub};
-      notfound -> {error, notfound}
+       {ok, Sub} -> {result, Sub};
+       notfound -> {error, notfound}
     end.
 
 -spec(set_subscription/4 ::
-(
-  _JID    :: _,
-  _NodeID :: _,
-  SubId   :: mod_pubsub:subId(),
-  Options :: mod_pubsub:subOptions())
+    (
+       _JID    :: _,
+       _NodeId :: _,
+       SubId   :: mod_pubsub:subId(),
+       Options :: mod_pubsub:subOptions())
     -> {result, ok}
-).
-set_subscription(_JID, _NodeID, SubID, Options) ->
+    ).
+set_subscription(_JID, _NodeId, SubID, Options) ->
     case (?DB_MOD):read_subscription(SubID) of
-      {ok, _} ->
-         (?DB_MOD):update_subscription(#pubsub_subscription{subid
-                                                                = SubID,
-                                                            options =
-                                                                Options}),
-         {result, ok};
-      notfound ->
-         (?DB_MOD):add_subscription(#pubsub_subscription{subid =
-                                                             SubID,
-                                                         options = Options}),
-         {result, ok}
+       {ok, _} ->
+           (?DB_MOD):update_subscription(#pubsub_subscription{subid = SubID,
+                   options = Options}),
+           {result, ok};
+       notfound ->
+           (?DB_MOD):add_subscription(#pubsub_subscription{subid = SubID,
+                   options = Options}),
+           {result, ok}
     end.
 
 get_options_xform(Lang, Options) ->
     Keys = [deliver, show_values, subscription_type, subscription_depth],
-    XFields = [get_option_xfield(Lang, Key, Options)
-              || Key <- Keys],
+    XFields = [get_option_xfield(Lang, Key, Options) || Key <- Keys],
     {result,
-     #xmlel{name = <<"x">>,
+       #xmlel{name = <<"x">>,
            attrs = [{<<"xmlns">>, ?NS_XDATA}],
            children =
-               [#xmlel{name = <<"field">>,
-                       attrs =
-                           [{<<"var">>, <<"FORM_TYPE">>},
-                            {<<"type">>, <<"hidden">>}],
-                       children =
-                           [#xmlel{name = <<"value">>, attrs = [],
-                                   children =
-                                       [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
-                 ++ XFields}}.
+           [#xmlel{name = <<"field">>,
+                   attrs =
+                   [{<<"var">>, <<"FORM_TYPE">>},
+                       {<<"type">>, <<"hidden">>}],
+                   children =
+                   [#xmlel{name = <<"value">>, attrs = [],
+                           children =
+                           [{xmlcdata, ?NS_PUBSUB_SUB_OPTIONS}]}]}]
+           ++ XFields}}.
 
 parse_options_xform(XFields) ->
     case xml:remove_cdata(XFields) of
-      [#xmlel{name = <<"x">>} = XEl] ->
-         case jlib:parse_xdata_submit(XEl) of
-           XData when is_list(XData) ->
-               Opts = set_xoption(XData, []),
-               {result, Opts};
-           Other -> Other
-         end;
-      _ -> {result, []}
+       [#xmlel{name = <<"x">>} = XEl] ->
+           case jlib:parse_xdata_submit(XEl) of
+               XData when is_list(XData) ->
+                   Opts = set_xoption(XData, []),
+                   {result, Opts};
+               Other -> Other
+           end;
+       _ -> {result, []}
     end.
 
 %%====================================================================
@@ -237,43 +177,42 @@ make_subid() ->
 set_xoption([], Opts) -> Opts;
 set_xoption([{Var, Value} | T], Opts) ->
     NewOpts = case var_xfield(Var) of
-               {error, _} -> Opts;
-               Key ->
-                   Val = val_xfield(Key, Value),
-                   lists:keystore(Key, 1, Opts, {Key, Val})
-             end,
+       {error, _} -> Opts;
+       Key ->
+           Val = val_xfield(Key, Value),
+           lists:keystore(Key, 1, Opts, {Key, Val})
+    end,
     set_xoption(T, NewOpts).
 
 %% Return the options list's key for an XForm var.
 %% Convert Values for option list's Key.
 var_xfield(?PUBSUB_DELIVER) -> deliver;
-%var_xfield(?PUBSUB_DIGEST) -> digest;
-%var_xfield(?PUBSUB_DIGEST_FREQUENCY) ->
-%    digest_frequency;
-%var_xfield(?PUBSUB_EXPIRE) -> expire;
-%var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
+var_xfield(?PUBSUB_DIGEST) -> digest;
+var_xfield(?PUBSUB_DIGEST_FREQUENCY) -> digest_frequency;
+var_xfield(?PUBSUB_EXPIRE) -> expire;
+var_xfield(?PUBSUB_INCLUDE_BODY) -> include_body;
 var_xfield(?PUBSUB_SHOW_VALUES) -> show_values;
-var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) ->
-    subscription_type;
-var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) ->
-    subscription_depth;
+var_xfield(?PUBSUB_SUBSCRIPTION_TYPE) -> subscription_type;
+var_xfield(?PUBSUB_SUBSCRIPTION_DEPTH) -> subscription_depth;
 var_xfield(_) -> {error, badarg}.
 
 val_xfield(deliver, [Val]) -> xopt_to_bool(Val);
-%val_xfield(digest, [Val]) -> xopt_to_bool(Val);
-%val_xfield(digest_frequency, [Val]) ->
-%    jlib:binary_to_integer(Val);
-%val_xfield(expire, [Val]) ->
-%    jlib:datetime_string_to_timestamp(Val);
-%val_xfield(include_body, [Val]) -> xopt_to_bool(Val);
+val_xfield(digest, [Val]) -> xopt_to_bool(Val);
+val_xfield(digest_frequency, [Val]) ->
+    case catch jlib:binary_to_integer(Val) of
+       N when is_integer(N) -> N;
+       _ -> {error, ?ERR_NOT_ACCEPTABLE}
+    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(subscription_type, [<<"items">>]) -> items;
 val_xfield(subscription_type, [<<"nodes">>]) -> nodes;
 val_xfield(subscription_depth, [<<"all">>]) -> all;
 val_xfield(subscription_depth, [Depth]) ->
     case catch jlib:binary_to_integer(Depth) of
-      N when is_integer(N) -> N;
-      _ -> {error, ?ERR_NOT_ACCEPTABLE}
+       N when is_integer(N) -> N;
+       _ -> {error, ?ERR_NOT_ACCEPTABLE}
     end.
 
 %% Convert XForm booleans to Erlang booleans.
@@ -288,19 +227,18 @@ xopt_to_bool(_) -> {error, ?ERR_NOT_ACCEPTABLE}.
 get_option_xfield(Lang, Key, Options) ->
     Var = xfield_var(Key),
     Label = xfield_label(Key),
-    {Type, OptEls} = type_and_options(xfield_type(Key),
-                                     Lang),
+    {Type, OptEls} = type_and_options(xfield_type(Key), Lang),
     Vals = case lists:keysearch(Key, 1, Options) of
-            {value, {_, Val}} ->
-                [tr_xfield_values(Vals)
-                 || Vals <- xfield_val(Key, Val)];
-            false -> []
-          end,
+       {value, {_, Val}} ->
+           [tr_xfield_values(Vals)
+               || Vals <- xfield_val(Key, Val)];
+       false -> []
+    end,
     #xmlel{name = <<"field">>,
-          attrs =
-              [{<<"var">>, Var}, {<<"type">>, Type},
-               {<<"label">>, translate:translate(Lang, Label)}],
-          children = OptEls ++ Vals}.
+       attrs =
+       [{<<"var">>, Var}, {<<"type">>, Type},
+           {<<"label">>, translate:translate(Lang, Label)}],
+       children = OptEls ++ Vals}.
 
 type_and_options({Type, Options}, Lang) ->
     {Type, [tr_xfield_options(O, Lang) || O <- Options]};
@@ -308,29 +246,26 @@ type_and_options(Type, _Lang) -> {Type, []}.
 
 tr_xfield_options({Value, Label}, Lang) ->
     #xmlel{name = <<"option">>,
-          attrs =
-              [{<<"label">>, translate:translate(Lang, Label)}],
-          children =
-              [#xmlel{name = <<"value">>, attrs = [],
-                      children = [{xmlcdata, Value}]}]}.
+       attrs =
+       [{<<"label">>, translate:translate(Lang, Label)}],
+       children =
+       [#xmlel{name = <<"value">>, attrs = [],
+               children = [{xmlcdata, Value}]}]}.
 
 tr_xfield_values(Value) ->
-%% Return the XForm variable name for a subscription option key.
-%% Return the XForm variable type 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.
     #xmlel{name = <<"value">>, attrs = [],
-          children = [{xmlcdata, Value}]}.
+       children = [{xmlcdata, Value}]}.
 
 xfield_var(deliver) -> ?PUBSUB_DELIVER;
 %xfield_var(digest) -> ?PUBSUB_DIGEST;
-%xfield_var(digest_frequency) ->
-%    ?PUBSUB_DIGEST_FREQUENCY;
+%xfield_var(digest_frequency) -> ?PUBSUB_DIGEST_FREQUENCY;
 %xfield_var(expire) -> ?PUBSUB_EXPIRE;
 %xfield_var(include_body) -> ?PUBSUB_INCLUDE_BODY;
 xfield_var(show_values) -> ?PUBSUB_SHOW_VALUES;
-xfield_var(subscription_type) ->
-    ?PUBSUB_SUBSCRIPTION_TYPE;
-xfield_var(subscription_depth) ->
-    ?PUBSUB_SUBSCRIPTION_DEPTH.
+xfield_var(subscription_type) -> ?PUBSUB_SUBSCRIPTION_TYPE;
+xfield_var(subscription_depth) -> ?PUBSUB_SUBSCRIPTION_DEPTH.
 
 xfield_type(deliver) -> <<"boolean">>;
 %xfield_type(digest) -> <<"boolean">>;
@@ -339,34 +274,31 @@ xfield_type(deliver) -> <<"boolean">>;
 %xfield_type(include_body) -> <<"boolean">>;
 xfield_type(show_values) ->
     {<<"list-multi">>,
-     [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
-      {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
-      {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
-      {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
-      {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
+       [{<<"away">>, ?SHOW_VALUE_AWAY_LABEL},
+           {<<"chat">>, ?SHOW_VALUE_CHAT_LABEL},
+           {<<"dnd">>, ?SHOW_VALUE_DND_LABEL},
+           {<<"online">>, ?SHOW_VALUE_ONLINE_LABEL},
+           {<<"xa">>, ?SHOW_VALUE_XA_LABEL}]};
 xfield_type(subscription_type) ->
     {<<"list-single">>,
-     [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
-      {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
+       [{<<"items">>, ?SUBSCRIPTION_TYPE_VALUE_ITEMS_LABEL},
+           {<<"nodes">>, ?SUBSCRIPTION_TYPE_VALUE_NODES_LABEL}]};
 xfield_type(subscription_depth) ->
     {<<"list-single">>,
-     [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
-      {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
+       [{<<"1">>, ?SUBSCRIPTION_DEPTH_VALUE_ONE_LABEL},
+           {<<"all">>, ?SUBSCRIPTION_DEPTH_VALUE_ALL_LABEL}]}.
 
 %% Return the XForm variable label for a subscription option key.
 xfield_label(deliver) -> ?DELIVER_LABEL;
 %xfield_label(digest) -> ?DIGEST_LABEL;
-%xfield_label(digest_frequency) ->
-%    ?DIGEST_FREQUENCY_LABEL;
+%xfield_label(digest_frequency) -> ?DIGEST_FREQUENCY_LABEL;
 %xfield_label(expire) -> ?EXPIRE_LABEL;
 %xfield_label(include_body) -> ?INCLUDE_BODY_LABEL;
 xfield_label(show_values) -> ?SHOW_VALUES_LABEL;
 %% Return the XForm value for a subscription option key.
 %% Convert erlang booleans to XForms.
-xfield_label(subscription_type) ->
-    ?SUBSCRIPTION_TYPE_LABEL;
-xfield_label(subscription_depth) ->
-    ?SUBSCRIPTION_DEPTH_LABEL.
+xfield_label(subscription_type) -> ?SUBSCRIPTION_TYPE_LABEL;
+xfield_label(subscription_depth) -> ?SUBSCRIPTION_DEPTH_LABEL.
 
 xfield_val(deliver, Val) -> [bool_to_xopt(Val)];
 %xfield_val(digest, Val) -> [bool_to_xopt(Val)];
index 4606ae0fd79eb48752212aaa3a550e0e3c802793..a827a9174f0a078ba2eae61c1ad8464353bc5ae6 100644 (file)
@@ -813,7 +813,7 @@ pubsub(Config) ->
                                       node = Node,
                                       jid = my_jid(Config)}}]}),
     ?recv2(
-       #message{sub_els = [#pubsub_event{}, #delay{}, #legacy_delay{}]},
+       #message{sub_els = [#pubsub_event{}, #delay{}]},
        #iq{type = result, id = I1}),
     %% Get subscriptions
     true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
@@ -860,8 +860,7 @@ pubsub(Config) ->
        #message{sub_els = [#pubsub_event{
                               items = [#pubsub_event_items{
                                           node = Node,
-                                          retract = [ItemID]}]},
-                           #shim{headers = [{<<"Collection">>, Node}]}]}),
+                                          retract = [ItemID]}]}]}),
     %% Unsubscribe from node "presence"
     #iq{type = result, sub_els = []} =
         send_recv(Config,
index cb321661e68a9cfbab9c399038690c93d4124606..a92608b60614685f67e7648e3223758b70c06fe2 100644 (file)
@@ -94,7 +94,8 @@
                     {mod_offline,     [{db_type, odbc}]},
                     {mod_privacy,     [{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},
                                        {last_item_cache, false},
                                        {plugins, ["flat", "hometree", "pep"]}]},
                     {mod_offline,     [{db_type, odbc}]},
                     {mod_privacy,     [{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},
                                        {last_item_cache, false},
                                        {plugins, ["flat", "hometree", "pep"]}]},
                     {mod_offline,     [{db_type, odbc}]},
                     {mod_privacy,     [{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},
                                        {last_item_cache, false},
                                        {plugins, ["flat", "hometree", "pep"]}]},
index c19e2244177a2f2fc8ced1513eac9d1199831cdb..0a836b8051e3ffe2560e0438f6804e0232a0d7aa 100644 (file)
@@ -26,7 +26,8 @@ host_config:
         db_type: odbc
       mod_private: 
         db_type: odbc
-      mod_pubsub_odbc: 
+      mod_pubsub: 
+        db_type: odbc
         access_createnode: pubsub_createnode
         ignore_pep_from_offline: true
         last_item_cache: false
@@ -76,7 +77,8 @@ Welcome to this XMPP server."
         db_type: odbc
       mod_private: 
         db_type: odbc
-      mod_pubsub_odbc: 
+      mod_pubsub: 
+        db_type: odbc
         access_createnode: pubsub_createnode
         ignore_pep_from_offline: true
         last_item_cache: false
@@ -132,7 +134,8 @@ Welcome to this XMPP server."
         db_type: odbc
       mod_private: 
         db_type: odbc
-      mod_pubsub_odbc: 
+      mod_pubsub: 
+        db_type: odbc
         access_createnode: pubsub_createnode
         ignore_pep_from_offline: true
         last_item_cache: false