2007-12-01 06:16:30 +01:00
%%% ====================================================================
2007-12-24 14:57:53 +01:00
%%% ``The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
%%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be
%%% retrieved via the world wide web at http://www.erlang.org/.
%%%
%%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations
%%% under the License.
%%%
2008-09-16 16:39:57 +02:00
%%% The Initial Developer of the Original Code is ProcessOne.
2011-02-14 13:47:22 +01:00
%%% Portions created by ProcessOne are Copyright 2006-2011, ProcessOne
2007-12-24 14:57:53 +01:00
%%% All Rights Reserved.''
2011-02-14 13:47:22 +01:00
%%% This software is copyright 2006-2011, ProcessOne.
2007-12-01 06:16:30 +01:00
%%%
2011-02-14 13:47:22 +01:00
%%% @copyright 2006-2011 ProcessOne
2007-12-01 06:16:30 +01:00
%%% @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.
2009-01-03 01:29:36 +01:00
%%% This module uses version 1.12 of the specification as a base.
2007-12-01 06:16:30 +01:00
%%% Most of the specification is implemented.
2008-07-11 14:48:27 +02:00
%%% Functions concerning configuration should be rewritten.
2009-08-25 19:14:30 +02:00
%%%
%%% Support for subscription-options and multi-subscribe features was
2009-08-25 21:54:44 +02:00
%%% added by Brian Cully (bjc AT kublai.com). Subscriptions and options are
2009-08-25 19:14:30 +02:00
%%% 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.
2007-12-01 06:16:30 +01:00
2003-07-08 22:11:27 +02:00
- module ( mod_pubsub ) .
2008-07-11 14:48:27 +02:00
- author ( 'christophe.romain@process-one.net' ) .
2011-02-08 19:08:38 +01:00
- version ( '1.13-1' ) .
2003-07-08 22:11:27 +02:00
2006-02-02 06:00:27 +01:00
- behaviour ( gen_server ) .
2003-07-08 22:11:27 +02:00
- behaviour ( gen_mod ) .
- include ( " ejabberd.hrl " ) .
2009-08-25 19:14:30 +02:00
- include ( " adhoc.hrl " ) .
2007-12-01 06:16:30 +01:00
- include ( " pubsub.hrl " ) .
2010-11-10 15:14:16 +01:00
- include ( " mod_roster.hrl " ) .
2007-12-01 06:16:30 +01:00
2009-05-29 01:21:50 +02:00
- define ( STDTREE , " tree " ) .
- define ( STDNODE , " flat " ) .
2007-12-10 15:09:31 +01:00
- define ( PEPNODE , " pep " ) .
2007-12-01 06:16:30 +01:00
%% exports for hooks
2007-12-22 01:04:05 +01:00
- export ( [ presence_probe / 3 ,
2010-12-02 14:05:19 +01:00
caps_update / 3 ,
2009-05-08 02:02:08 +02:00
in_subscription / 6 ,
2009-03-10 12:13:46 +01:00
out_subscription / 4 ,
2010-03-05 16:09:06 +01:00
on_user_offline / 3 ,
2007-12-18 15:45:26 +01:00
remove_user / 2 ,
2007-12-01 06:16:30 +01:00
disco_local_identity / 5 ,
2007-12-11 17:19:17 +01:00
disco_local_features / 5 ,
disco_local_items / 5 ,
2007-12-01 06:16:30 +01:00
disco_sm_identity / 5 ,
disco_sm_features / 5 ,
disco_sm_items / 5
] ) .
%% exported iq handlers
2010-08-04 18:30:22 +02:00
- export ( [ iq_sm / 3
2007-12-01 06:16:30 +01:00
] ) .
%% exports for console debug manual use
- export ( [ create_node / 5 ,
delete_node / 3 ,
2009-08-25 19:14:30 +02:00
subscribe_node / 5 ,
2007-12-01 06:16:30 +01:00
unsubscribe_node / 5 ,
publish_item / 6 ,
delete_item / 4 ,
2009-05-01 01:17:38 +02:00
send_items / 6 ,
2009-05-29 01:21:50 +02:00
get_items / 2 ,
get_item / 3 ,
get_cached_item / 2 ,
2009-03-20 01:08:38 +01:00
get_configure / 5 ,
2007-12-01 06:16:30 +01:00
set_configure / 5 ,
2008-10-08 14:02:30 +02:00
tree_action / 3 ,
2007-12-01 06:16:30 +01:00
node_action / 4
] ) .
%% general helpers for plugins
- export ( [ node_to_string / 1 ,
string_to_node / 1 ,
subscription_to_string / 1 ,
affiliation_to_string / 1 ,
string_to_subscription / 1 ,
string_to_affiliation / 1 ,
extended_error / 2 ,
2009-08-27 10:48:21 +02:00
extended_error / 3 ,
rename_default_nodeplugin / 0
2007-12-01 06:16:30 +01:00
] ) .
%% 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
] ) .
2003-07-08 22:11:27 +02:00
2009-04-23 01:01:51 +02:00
%% calls for parallel sending of last items
- export ( [ send_loop / 1
] ) .
2005-05-23 02:30:29 +02:00
- define ( PROCNAME , ejabberd_mod_pubsub ) .
2010-01-12 16:14:57 +01:00
- define ( LOOPNAME , ejabberd_mod_pubsub_loop ) .
2007-12-01 06:16:30 +01:00
- define ( PLUGIN_PREFIX , " node_ " ) .
- define ( TREE_PREFIX , " nodetree_ " ) .
2010-10-19 17:08:59 +02:00
- record ( state ,
{
server_host : : string ( ) ,
host : : string ( ) ,
access : : atom ( ) ,
pep_mapping = [ ] : : [ { Namespace : : string ( ) , NodeId : : string ( ) } ] ,
ignore_pep_from_offline = true : : boolean ( ) ,
last_item_cache = false : : boolean ( ) ,
max_items_node = ? MAXITEMS : : integer ( ) ,
nodetree = 'nodetree_tree' : : atom ( ) ,
plugins = [ ? STDNODE ] : : [ Plugin : : nodeType ( ) ]
} ) .
2009-08-25 19:14:30 +02:00
2006-02-02 06:00:27 +01:00
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
2010-10-19 17:08:59 +02:00
- spec ( start_link / 2 : :
(
Host : : string ( ) ,
Opts : : [ { Option : : atom ( ) , Value : : term ( ) } ] )
- > { 'ok' , pid ( ) } | 'ignore' | { 'error' , _ }
) .
2006-02-02 06:00:27 +01:00
start_link ( Host , Opts ) - >
Proc = gen_mod : get_module_proc ( Host , ? PROCNAME ) ,
gen_server : start_link ( { local , Proc } , ? MODULE , [ Host , Opts ] , [ ] ) .
2005-06-20 05:18:13 +02:00
2010-10-19 17:08:59 +02:00
- spec ( start / 2 : :
(
Host : : string ( ) ,
Opts : : [ { Option : : atom ( ) , Value : : term ( ) } ] )
- > { 'error' , _ } | { 'ok' , 'undefined' | pid ( ) } | { 'ok' , 'undefined' | pid ( ) , _ }
) .
2006-02-02 06:00:27 +01:00
start ( Host , Opts ) - >
Proc = gen_mod : get_module_proc ( Host , ? PROCNAME ) ,
2007-12-01 06:16:30 +01:00
ChildSpec = { Proc ,
{ ? MODULE , start_link , [ Host , Opts ] } ,
transient , 1000 , worker , [ ? MODULE ] } ,
2006-02-02 06:00:27 +01:00
supervisor : start_child ( ejabberd_sup , ChildSpec ) .
2003-07-08 22:11:27 +02:00
2010-10-19 17:08:59 +02:00
- spec ( stop / 1 : :
(
Host : : string ( ) )
- > 'ok' | { 'error' , 'not_found' | 'running' | 'simple_one_for_one' }
) .
2006-02-02 06:00:27 +01:00
stop ( Host ) - >
Proc = gen_mod : get_module_proc ( Host , ? PROCNAME ) ,
gen_server : call ( Proc , stop ) ,
2007-02-22 08:39:05 +01:00
supervisor : delete_child ( ejabberd_sup , Proc ) .
2005-07-20 05:09:34 +02:00
2006-02-02 06:00:27 +01:00
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
2010-10-19 17:08:59 +02:00
- spec ( init / 1 : :
(
Args : : [ ServerHost : : string ( ) | [ { Option : : atom ( ) , Value : : term ( ) } ] ] )
- > { 'ok' , State : : #state { } }
) .
2006-04-07 15:39:48 +02:00
init ( [ ServerHost , Opts ] ) - >
2008-04-28 15:02:07 +02:00
? DEBUG ( " pubsub init ~p ~p " , [ ServerHost , Opts ] ) ,
2011-10-20 11:13:36 +02:00
Host = gen_mod : get_opt_host ( ServerHost , Opts , " pubsub.@HOST@ " ) ,
2010-10-19 17:08:59 +02:00
Access = gen_mod : get_opt ( 'access_createnode' , Opts , 'all' ) ,
PepOffline = gen_mod : get_opt ( 'ignore_pep_from_offline' , Opts , true ) ,
IQDisc = gen_mod : get_opt ( 'iqdisc' , Opts , 'one_queue' ) ,
LastItemCache = gen_mod : get_opt ( 'last_item_cache' , Opts , false ) ,
MaxItemsNode = gen_mod : get_opt ( 'max_items_node' , Opts , ? MAXITEMS ) ,
2009-01-12 19:17:05 +01:00
ServerHostB = list_to_binary ( ServerHost ) ,
2009-05-01 01:17:38 +02:00
pubsub_index : init ( Host , ServerHost , Opts ) ,
2010-10-19 17:08:59 +02:00
ets : new ( gen_mod : get_module_proc ( Host , 'config' ) , [ 'set' , 'named_table' ] ) ,
ets : new ( gen_mod : get_module_proc ( ServerHost , 'config' ) , [ 'set' , 'named_table' ] ) ,
2009-04-24 12:32:41 +02:00
{ Plugins , NodeTree , PepMapping } = init_plugins ( Host , ServerHost , Opts ) ,
2010-05-28 11:32:51 +02:00
mnesia : create_table ( pubsub_last_item , [ { ram_copies , [ node ( ) ] } , { attributes , record_info ( fields , pubsub_last_item ) } ] ) ,
2010-04-14 01:08:22 +02:00
mod_disco : register_feature ( ServerHostB , ? NS_PUBSUB_s ) ,
2010-10-19 17:08:59 +02:00
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 } ) ,
2010-03-05 16:09:06 +01:00
ejabberd_hooks : add ( sm_remove_connection_hook , ServerHostB , ? MODULE , on_user_offline , 75 ) ,
2010-08-04 18:30:22 +02:00
ejabberd_hooks : add ( disco_local_identity , ServerHostB , ? MODULE , disco_local_identity , 75 ) ,
ejabberd_hooks : add ( disco_local_features , ServerHostB , ? MODULE , disco_local_features , 75 ) ,
ejabberd_hooks : add ( disco_local_items , ServerHostB , ? MODULE , disco_local_items , 75 ) ,
2009-05-11 19:27:55 +02:00
ejabberd_hooks : add ( presence_probe_hook , ServerHostB , ? MODULE , presence_probe , 80 ) ,
2009-05-08 02:02:08 +02:00
ejabberd_hooks : add ( roster_in_subscription , ServerHostB , ? MODULE , in_subscription , 50 ) ,
2009-03-10 12:13:46 +01:00
ejabberd_hooks : add ( roster_out_subscription , ServerHostB , ? MODULE , out_subscription , 50 ) ,
2009-01-12 19:17:05 +01:00
ejabberd_hooks : add ( remove_user , ServerHostB , ? MODULE , remove_user , 50 ) ,
2009-05-07 02:54:44 +02:00
ejabberd_hooks : add ( anonymous_purge_hook , ServerHostB , ? MODULE , remove_user , 50 ) ,
2009-04-10 15:21:37 +02:00
case lists : member ( ? PEPNODE , Plugins ) of
2009-04-08 21:30:24 +02:00
true - >
2010-12-02 14:05:19 +01:00
ejabberd_hooks : add ( caps_update , ServerHostB , ? MODULE , caps_update , 80 ) ,
2010-08-04 18:30:22 +02:00
ejabberd_hooks : add ( disco_sm_identity , ServerHostB , ? MODULE , disco_sm_identity , 75 ) ,
ejabberd_hooks : add ( disco_sm_features , ServerHostB , ? MODULE , disco_sm_features , 75 ) ,
ejabberd_hooks : add ( disco_sm_items , ServerHostB , ? MODULE , disco_sm_items , 75 ) ,
gen_iq_handler : add_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB , ? MODULE , iq_sm , IQDisc ) ,
gen_iq_handler : add_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB_OWNER , ? MODULE , iq_sm , IQDisc ) ;
2009-04-08 21:30:24 +02:00
false - >
ok
end ,
2009-08-25 19:14:30 +02:00
ejabberd_router : register_route ( Host ) ,
update_node_database ( Host , ServerHost ) ,
update_state_database ( Host , ServerHost ) ,
2009-09-25 15:14:59 +02:00
init_nodes ( Host , ServerHost , NodeTree , Plugins ) ,
2009-04-23 01:01:51 +02:00
State = #state { host = Host ,
2010-10-19 17:08:59 +02:00
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 } ,
2010-01-13 11:25:06 +01:00
init_send_loop ( ServerHost , State ) ,
{ ok , State } .
2010-10-19 17:08:59 +02:00
- spec ( init_send_loop / 2 : :
(
ServerHost : : string ( ) ,
State : : #state { } )
- > pid ( )
) .
2010-01-13 11:25:06 +01:00
init_send_loop ( ServerHost , State ) - >
Proc = gen_mod : get_module_proc ( ServerHost , ? LOOPNAME ) ,
SendLoop = spawn ( ? MODULE , send_loop , [ State ] ) ,
register ( Proc , SendLoop ) ,
SendLoop .
2007-12-01 06:16:30 +01:00
2008-04-01 12:11:39 +02:00
%% @spec (Host, ServerHost, Opts) -> Plugins
2007-12-01 06:16:30 +01:00
%% 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>
2010-05-17 22:09:01 +02:00
%% <p>See {@link node_flat:init/1} for an example implementation.</p>
2010-10-19 17:08:59 +02:00
- spec ( init_plugins / 3 : :
(
Host : : string ( ) ,
ServerHost : : string ( ) ,
Opts : : [ { Option : : atom ( ) , Value : : term ( ) } ] )
- > { Plugins : : [ Plugin : : nodeType ( ) ] ,
TreePlugin : : atom ( ) ,
PepMapping : : [ { Namespace : : string ( ) , NodeId : : string ( ) } ] }
) .
2007-12-01 06:16:30 +01:00
init_plugins ( Host , ServerHost , Opts ) - >
TreePlugin = list_to_atom ( ? TREE_PREFIX ++
gen_mod : get_opt ( nodetree , Opts , ? STDTREE ) ) ,
2008-04-28 15:02:07 +02:00
? DEBUG ( " ** tree plugin is ~p " , [ TreePlugin ] ) ,
2007-12-01 06:16:30 +01:00
TreePlugin : init ( Host , ServerHost , Opts ) ,
2009-05-20 00:29:28 +02:00
Plugins = gen_mod : get_opt ( plugins , Opts , [ ? STDNODE ] ) ,
PepMapping = gen_mod : get_opt ( pep_mapping , Opts , [ ] ) ,
? DEBUG ( " ** PEP Mapping : ~p ~n " , [ PepMapping ] ) ,
2010-11-09 14:32:40 +01:00
PluginsOK = lists : foldl ( fun ( Name , Acc ) - >
2010-11-08 11:18:33 +01:00
Plugin = list_to_atom ( ? PLUGIN_PREFIX ++ Name ) ,
case catch apply ( Plugin , init , [ Host , ServerHost , Opts ] ) of
{ 'EXIT' , _ Error } - >
Acc ;
_ - >
? DEBUG ( " ** init ~s plugin " , [ Name ] ) ,
[ Name | Acc ]
end
end , [ ] , Plugins ) ,
2010-11-09 14:32:40 +01:00
{ lists : reverse ( PluginsOK ) , TreePlugin , PepMapping } .
2007-12-01 06:16:30 +01:00
2010-10-19 17:08:59 +02:00
- spec ( terminate_plugins / 4 : :
(
Host : : string ( ) ,
ServerHost : : string ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] ,
TreePlugin : : atom ( ) )
- > 'ok'
) .
2007-12-01 06:16:30 +01:00
terminate_plugins ( Host , ServerHost , Plugins , TreePlugin ) - >
lists : foreach ( fun ( Name ) - >
2008-04-28 15:02:07 +02:00
? DEBUG ( " ** terminate ~s plugin " , [ Name ] ) ,
2007-12-01 06:16:30 +01:00
Plugin = list_to_atom ( ? PLUGIN_PREFIX ++ Name ) ,
Plugin : terminate ( Host , ServerHost )
end , Plugins ) ,
TreePlugin : terminate ( Host , ServerHost ) ,
ok .
2010-10-19 17:08:59 +02:00
- spec ( init_nodes / 4 : :
(
Host : : string ( ) ,
ServerHost : : string ( ) ,
NodeTree : : atom ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] )
- > 'ok'
) .
2009-09-25 15:14:59 +02:00
init_nodes ( Host , ServerHost , _ NodeTree , Plugins ) - >
2009-10-21 00:09:43 +02:00
%% TODO, this call should be done plugin side
2009-09-25 15:14:59 +02:00
case lists : member ( " hometree " , Plugins ) of
2010-10-19 17:08:59 +02:00
true - >
create_node ( Host , ServerHost , string_to_node ( " /home " ) , service_jid ( Host ) , " hometree " ) ,
create_node ( Host , ServerHost , string_to_node ( " /home/ " ++ ServerHost ) , service_jid ( Host ) , " hometree " ) ;
false - >
ok
2009-09-25 15:14:59 +02:00
end .
2007-12-01 06:16:30 +01:00
2010-10-19 17:08:59 +02:00
- spec ( update_node_database / 2 : :
(
Host : : string ( ) ,
ServerHost : : string ( ) )
- > { 'aborted' , _ } | { 'atomic' , _ }
) .
2009-08-25 19:14:30 +02:00
update_node_database ( Host , ServerHost ) - >
2009-02-19 12:29:01 +01:00
mnesia : del_table_index ( pubsub_node , type ) ,
mnesia : del_table_index ( pubsub_node , parentid ) ,
2007-12-01 06:16:30 +01:00
case catch mnesia : table_info ( pubsub_node , attributes ) of
[ host_node , host_parent , info ] - >
2009-08-25 19:14:30 +02:00
? INFO_MSG ( " upgrade node pubsub tables " , [ ] ) ,
2007-12-01 06:16:30 +01:00
F = fun ( ) - >
2009-08-25 21:54:44 +02:00
{ Result , LastIdx } = lists : foldl (
2010-10-19 17:08:59 +02:00
fun ( { pubsub_node , NodeId , ParentId , { nodeinfo , Items , Options , Entities } } , { RecList , Nidx } ) - >
ItemsList =
lists : foldl (
fun ( { item , ItemName , Publisher , Payload } , Acc ) - >
C = { unknown , Publisher } ,
M = { now ( ) , Publisher } ,
mnesia : write (
#pubsub_item { id = { ItemName , Nidx } ,
creation = C ,
modification = M ,
payload = Payload } ) ,
[ { Publisher , ItemName } | 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 , Nidx } ,
UsrItems ,
Aff ,
Sub } ) ,
case Aff of
owner - > [ JID | Acc ] ;
_ - > Acc
end
end , [ ] , Entities ) ,
mnesia : delete ( { pubsub_node , NodeId } ) ,
{ [ #pubsub_node { id = NodeId ,
idx = Nidx ,
parents = [ element ( 2 , ParentId ) ] ,
owners = Owners ,
options = Options } |
RecList ] , Nidx + 1 }
end , { [ ] , 1 } ,
mnesia : match_object
( { pubsub_node , { Host , '_' } , '_' , '_' } ) ) ,
2009-08-25 21:54:44 +02:00
mnesia : write ( #pubsub_index { index = node , last = LastIdx , free = [ ] } ) ,
Result
2007-12-01 06:16:30 +01:00
end ,
2008-12-17 14:52:44 +01:00
{ 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 } - >
2009-08-25 21:54:44 +02:00
? INFO_MSG ( " Pubsub node tables updated correctly: ~p " , [ Result ] ) ;
2008-12-17 14:52:44 +01:00
{ aborted , Reason } - >
2009-08-25 21:54:44 +02:00
? ERROR_MSG ( " Problem updating Pubsub node tables: ~n ~p " , [ Reason ] )
2008-12-17 14:52:44 +01:00
end ;
2009-05-01 01:17:38 +02:00
[ nodeid , parentid , type , owners , options ] - >
F = fun ( { pubsub_node , NodeId , { _ , Parent } , Type , Owners , Options } ) - >
2010-10-19 17:08:59 +02:00
#pubsub_node {
id = NodeId ,
idx = 0 ,
parents = [ Parent ] ,
type = Type ,
owners = Owners ,
options = Options }
2009-05-01 01:17:38 +02:00
end ,
2010-10-19 17:08:59 +02:00
%% TODO : to change nodeid/id and id/idx or not to change ?
2009-08-25 19:14:30 +02:00
mnesia : transform_table ( pubsub_node , F , [ nodeid , id , parents , type , owners , options ] ) ,
2009-05-01 01:17:38 +02:00
FNew = fun ( ) - >
2010-10-19 17:08:59 +02:00
LastIdx = lists : foldl ( fun ( #pubsub_node { id = NodeId } = PubsubNode , Nidx ) - >
mnesia : write ( PubsubNode #pubsub_node { idx = Nidx } ) ,
lists : foreach ( fun ( #pubsub_state { id = StateId } = State ) - >
{ JID , _ } = StateId ,
mnesia : delete ( { pubsub_state , StateId } ) ,
mnesia : write ( State #pubsub_state { id = { JID , Nidx } } )
end , mnesia : match_object ( #pubsub_state { id = { '_' , NodeId } , _ = '_' } ) ) ,
lists : foreach ( fun ( #pubsub_item { id = ItemId } = Item ) - >
{ ItemName , _ } = ItemId ,
{ M1 , M2 } = Item #pubsub_item.modification ,
{ C1 , C2 } = Item #pubsub_item.creation ,
mnesia : delete ( { pubsub_item , ItemId } ) ,
mnesia : write ( Item #pubsub_item { id = { ItemName , Nidx } ,
modification = { M2 , M1 } ,
creation = { C2 , C1 } } )
end , mnesia : match_object ( #pubsub_item { id = { '_' , NodeId } , _ = '_' } ) ) ,
Nidx + 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 ,
2009-05-01 01:17:38 +02:00
case mnesia : transaction ( FNew ) of
{ atomic , Result } - >
2009-08-25 21:54:44 +02:00
rename_default_nodeplugin ( ) ,
? INFO_MSG ( " Pubsub node tables updated correctly: ~p " , [ Result ] ) ;
2009-08-25 19:14:30 +02:00
{ aborted , Reason } - >
2009-08-25 21:54:44 +02:00
? ERROR_MSG ( " Problem updating Pubsub node tables: ~n ~p " , [ Reason ] )
2009-08-25 19:14:30 +02:00
end ;
[ nodeid , id , parent , type , owners , options ] - >
F = fun ( { pubsub_node , NodeId , Id , Parent , Type , Owners , Options } ) - >
2010-10-19 17:08:59 +02:00
#pubsub_node {
id = NodeId ,
idx = Id ,
parents = [ Parent ] ,
type = Type ,
owners = Owners ,
options = Options }
2009-08-25 19:14:30 +02:00
end ,
2010-10-19 17:08:59 +02:00
%% TODO : to change nodeid/id and id/idx or not to change ?
2009-08-25 21:54:44 +02:00
mnesia : transform_table ( pubsub_node , F , [ nodeid , id , parents , type , owners , options ] ) ,
rename_default_nodeplugin ( ) ;
2009-08-25 19:14:30 +02:00
_ - >
ok
2009-10-21 00:09:43 +02:00
end ,
mnesia : transaction ( fun ( ) - >
2010-10-19 17:08:59 +02:00
case catch mnesia : first ( pubsub_node ) of
{ _ , L } when is_list ( L ) - >
lists : foreach (
fun ( { H , N } ) when is_list ( N ) - >
[ Node ] = mnesia : read ( { pubsub_node , { H , N } } ) ,
Type = Node #pubsub_node.type ,
BN = element ( 2 , node_call ( Type , path_to_node , [ N ] ) ) ,
BP = case [ element ( 2 , node_call ( Type , path_to_node , [ P ] ) ) | | P < - Node #pubsub_node.parents ] of
[ < < > > ] - > [ ] ;
Parents - > Parents
end ,
mnesia : write ( Node #pubsub_node { id = { H , BN } , parents = BP } ) ,
mnesia : delete ( { pubsub_node , { H , N } } ) ;
( _ ) - >
ok
end , mnesia : all_keys ( pubsub_node ) ) ;
_ - >
ok
end
end ) .
- spec ( rename_default_nodeplugin / 0 : : ( ) - > 'ok' ) .
2009-10-21 00:09:43 +02:00
2009-08-25 21:54:44 +02:00
rename_default_nodeplugin ( ) - >
lists : foreach ( fun ( Node ) - >
2010-10-19 17:08:59 +02:00
mnesia : dirty_write ( Node #pubsub_node { type = " hometree " } )
end , mnesia : dirty_match_object ( #pubsub_node { type = " default " , _ = '_' } ) ) .
- spec ( update_state_database / 2 : :
(
Host : : string ( ) ,
ServerHost : : string ( ) )
- > 'ok'
) .
2009-08-25 19:14:30 +02:00
update_state_database ( _ Host , _ ServerHost ) - >
case catch mnesia : table_info ( pubsub_state , attributes ) of
[ stateid , items , affiliation , subscription ] - >
? INFO_MSG ( " upgrade state pubsub tables " , [ ] ) ,
2010-09-10 19:45:28 +02:00
F = fun ( { pubsub_state , { JID , NodeId } , Items , Aff , Sub } , Acc ) - >
2009-08-25 19:14:30 +02:00
Subs = case Sub of
none - >
[ ] ;
_ - >
2010-09-10 19:45:28 +02:00
{ result , SubId } = pubsub_subscription : subscribe_node ( JID , NodeId , [ ] ) ,
[ { Sub , SubId } ]
2009-08-25 19:14:30 +02:00
end ,
2010-09-10 19:45:28 +02:00
NewState = #pubsub_state { id = { JID , NodeId } ,
2009-08-25 19:14:30 +02:00
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 state tables updated correctly: ~p " ,
[ Result ] ) ;
2009-05-01 01:17:38 +02:00
{ aborted , Reason } - >
2009-08-25 19:14:30 +02:00
? ERROR_MSG ( " Problem updating Pubsub state tables: ~n ~p " ,
[ Reason ] )
2009-05-01 01:17:38 +02:00
end ;
2007-12-01 06:16:30 +01:00
_ - >
ok
end .
2010-10-19 17:08:59 +02:00
- spec ( send_loop / 1 : :
(
State : : #state { } )
- > 'ok'
) .
2009-04-23 01:01:51 +02:00
send_loop ( State ) - >
receive
2010-10-19 17:08:59 +02:00
{ 'presence' , #jid { node = User , domain = Server , resource = Resource } = JID , Pid } - >
Host = State #state.host ,
ServerHost = State #state.server_host ,
LJID = { User , Server , Resource } ,
BJID = { User , Server , undefined } ,
%% for each node From is subscribed to
%% and if the node is so configured, send the last published item to From
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 ) - >
2011-05-30 14:04:42 +02:00
#pubsub_node { id = { H , NodeId } , type = Type , idx = Nidx , options = Options } = Node ,
2010-10-19 17:08:59 +02:00
case get_option ( Options , 'send_last_published_item' ) of
'on_sub_and_presence' - >
2011-05-30 14:04:42 +02:00
send_items ( H , NodeId , Nidx , Type , LJID , 'last' ) ;
2010-10-19 17:08:59 +02:00
_ - >
ok
end ;
true - >
% resource not concerned about that subscription
ok
end ;
( _ ) - >
ok
end , Subscriptions )
end , State #state.plugins ) ,
%% and force send the last PEP events published by its offline and local contacts
%% only if pubsub is explicitely configured for that.
%% this is a hack in a sense that PEP should only be based on presence
%% and is not able to "store" events of remote users (via s2s)
%% this makes that hack only work for local domain by now
if not State #state.ignore_pep_from_offline - >
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 = exmpp_jid : make ( 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
2009-04-23 01:01:51 +02:00
end ;
2010-10-19 17:08:59 +02:00
true - >
2009-04-23 01:01:51 +02:00
ok
2010-10-19 17:08:59 +02:00
end ,
send_loop ( State ) ;
{ 'presence' , User , Server , Resources , #jid { node = U , domain = S } = JID } - >
%% get resources caps and check if processing is needed
spawn ( fun ( ) - >
Host = State #state.host ,
Owner = { U , S , undefined } ,
2011-05-30 14:04:42 +02:00
lists : foreach ( fun ( #pubsub_node { id = { _ , NodeId } , type = Type , idx = Nidx , options = Options } ) - >
2010-10-19 17:08:59 +02:00
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' - >
RosterGroups = get_option ( Options , 'roster_groups_allowed' , [ ] ) ,
element ( 2 , get_roster_info ( U , S , LJID , RosterGroups ) )
end ,
2011-05-30 14:04:42 +02:00
if Subscribed - > send_items ( Owner , NodeId , Nidx , Type , LJID , 'last' ) ;
2010-10-19 17:08:59 +02:00
true - > ok
end
end , Resources ) ;
_ - >
ok
end
end , tree_action ( Host , get_nodes , [ Owner , JID ] ) )
end ) ,
send_loop ( State ) ;
stop - >
2009-04-23 01:01:51 +02:00
ok
end .
2007-12-01 06:16:30 +01:00
%% -------
2007-12-11 17:19:17 +01:00
%% disco hooks handling functions
2007-12-01 06:16:30 +01:00
%%
2010-10-19 17:08:59 +02:00
- spec ( disco_local_identity / 5 : :
(
2010-11-10 21:27:53 +01:00
Acc : : [ ] | [ Identity : : #xmlel { } ] ,
2010-10-19 17:08:59 +02:00
From : : jidEntity ( ) ,
To : : jidComponent ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
2010-11-10 21:27:53 +01:00
- > Identities : : [ ] | [ Identity : : #xmlel { } ]
2010-10-19 17:08:59 +02:00
) .
disco_local_identity ( Acc , _ From , #jid { domain = Host } = _ To , < < > > = _ NodeId , _ Lang ) - >
case lists : member ( ? PEPNODE , plugins ( Host ) ) of
2010-08-04 18:30:22 +02:00
true - >
[ #xmlel { name = 'identity' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " pubsub " > > ) , ? XMLATTR ( < < " type " > > , < < " pep " > > ) ] }
2010-10-19 17:08:59 +02:00
| Acc ] ;
2010-08-04 18:30:22 +02:00
false - > Acc
end ;
2010-10-19 17:08:59 +02:00
disco_local_identity ( Acc , _ From , _ To , _ NodeId , _ Lang ) - >
2007-12-01 06:16:30 +01:00
Acc .
2010-10-19 17:08:59 +02:00
- spec ( disco_local_features / 5 : :
(
Acc : : 'empty' | { 'result' , Features : : [ ] | [ Feature : : atom ( ) | string ( ) | binary ( ) ] } ,
From : : jidEntity ( ) ,
To : : jidComponent ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
- > { 'result' , Features : : [ ] | [ Feature : : atom ( ) | string ( ) | binary ( ) ] }
) .
disco_local_features ( Acc , _ From , #jid { domain = Host } = _ To , < < > > = _ NodeId , _ Lang ) - >
OtherFeatures = case Acc of
{ result , Features } - > Features ;
_ - > [ ]
end ,
{ result , OtherFeatures ++
[ ? NS_PUBSUB_s ++ " # " ++ Feature | | Feature < - features ( Host , < < > > ) ] } ;
disco_local_features ( Acc , _ From , _ To , _ NodeId , _ Lang ) - >
2007-12-21 01:08:59 +01:00
Acc .
2007-12-11 17:19:17 +01:00
2010-10-19 17:08:59 +02:00
- spec ( disco_local_items / 5 : :
(
2010-11-10 21:27:53 +01:00
Acc : : { 'result' , Items : : [ ] | [ Item : : #xmlel { } ] } ,
2010-10-19 17:08:59 +02:00
From : : jidEntity ( ) ,
To : : jidComponent ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
2010-11-10 21:27:53 +01:00
- > { 'result' , Items : : [ ] | [ Item : : #xmlel { } ] }
2010-10-19 17:08:59 +02:00
| { 'error' , _ } %% TODO
) .
2009-01-08 15:54:00 +01:00
disco_local_items ( Acc , _ From , _ To , < < > > , _ Lang ) - >
2007-12-11 17:19:17 +01:00
Acc ;
disco_local_items ( Acc , _ From , _ To , _ Node , _ Lang ) - >
Acc .
2010-08-04 18:30:22 +02:00
2010-10-19 17:08:59 +02:00
- spec ( disco_sm_identity / 5 : :
(
2010-11-10 21:27:53 +01:00
Acc : : [ ] | [ Identity : : #xmlel { } ] ,
2010-10-19 17:08:59 +02:00
From : : jidEntity ( ) ,
To : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
2010-11-10 21:27:53 +01:00
- > Identities : : [ ] | [ Identity : : #xmlel { } ]
2010-10-19 17:08:59 +02:00
) .
disco_sm_identity ( Acc , From , To , NodeId , _ Lang ) - >
disco_identity ( To , NodeId , From ) ++ Acc .
- spec ( disco_identity / 3 : :
(
Host : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
2010-11-10 21:27:53 +01:00
- > Identities : : [ ] | [ Identity : : #xmlel { } ]
2010-10-19 17:08:59 +02:00
) .
disco_identity ( _ Host , < < > > = _ NodeId , _ From ) - >
2010-08-04 18:30:22 +02:00
[ #xmlel { name = 'identity' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " pubsub " > > ) , ? XMLATTR ( < < " type " > > , < < " pep " > > ) ] } ] ;
2010-10-19 17:08:59 +02:00
disco_identity ( #jid { node = U , domain = S , resource = R } = Host , NodeId , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { idx = Nidx , type = Type , options = Options , owners = Owners } ) - >
case get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) of
2010-10-19 17:08:59 +02:00
{ result , _ } - >
{ result ,
[ #xmlel { name = 'identity' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " pubsub " > > ) , ? XMLATTR ( < < " type " > > , < < " pep " > > ) ] } ,
2010-10-19 17:08:59 +02:00
#xmlel { name = 'identity' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " pubsub " > > ) , ? XMLATTR ( < < " type " > > , < < " leaf " > > )
2010-10-19 17:08:59 +02:00
| case get_option ( Options , 'title' ) of
false - > [ ] ;
2010-11-29 20:44:31 +01:00
Title - > [ ? XMLATTR ( < < " name " > > , Title ) ]
2010-10-19 17:08:59 +02:00
end
] } ] } ;
{ error , _ } - > { result , [ ] }
end
end ,
case transaction ( { U , S , R } , NodeId , Action , sync_dirty ) of
{ result , { _ , Identities } } - > Identities ;
_ - > _ Identities = [ ]
2007-12-01 06:16:30 +01:00
end .
2010-10-19 17:08:59 +02:00
- spec ( disco_sm_features / 5 : :
(
Acc : : 'empty' | { 'result' , Features : : [ ] | [ Feature : : atom ( ) | string ( ) | binary ( ) ] } ,
From : : jidEntity ( ) ,
To : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
- > { 'result' , Features : : [ ] | [ Feature : : atom ( ) | string ( ) | binary ( ) ] }
) .
disco_sm_features ( 'empty' = _ Acc , From , To , NodeId , Lang ) - >
disco_sm_features ( { result , [ ] } , From , To , NodeId , Lang ) ;
disco_sm_features ( { result , OtherFeatures } = _ Acc , From , To , NodeId , _ Lang ) - >
{ result , disco_features ( To , NodeId , From ) ++ OtherFeatures } .
- spec ( disco_features / 3 : :
(
Host : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
- > Features : : [ ] | [ Feature : : atom ( ) | string ( ) | binary ( ) ]
) .
2010-08-04 18:30:22 +02:00
2010-10-19 17:08:59 +02:00
disco_features ( _ Host , < < > > = _ NodeId , _ From ) - >
2010-08-04 18:30:22 +02:00
[ ? NS_PUBSUB_s
2010-10-19 17:08:59 +02:00
| [ ? NS_PUBSUB_s ++ " # " ++ Feature | | Feature < - features ( " pep " ) ] ] ;
disco_features ( #jid { node = U , domain = S , resource = R } = Host , NodeId , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { idx = Nidx , type = Type , options = Options , owners = Owners } ) - >
case get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) of
2010-10-19 17:08:59 +02:00
{ result , _ } - >
{ result , [ ? NS_PUBSUB_s
| [ ? NS_PUBSUB_s ++ " # " ++ Feature | | Feature < - features ( " pep " ) ] ] } ;
_ - > { result , [ ] }
end
end ,
case transaction ( { U , S , R } , NodeId , Action , sync_dirty ) of
{ result , { _ , Features } } - > Features ;
_ - > _ Features = [ ]
end .
2010-08-04 18:30:22 +02:00
2010-10-19 17:08:59 +02:00
- spec ( disco_sm_items / 5 : :
(
Acc : : 'empty'
2010-11-10 21:27:53 +01:00
| { 'result' , Items : : [ ] | [ Item : : #xmlel { } ] } ,
2010-10-19 17:08:59 +02:00
From : : jidEntity ( ) ,
To : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
Lang : : binary ( ) )
2010-11-10 21:27:53 +01:00
- > { 'result' , Items : : [ ] | [ Item : : #xmlel { } ] }
2010-10-19 17:08:59 +02:00
) .
disco_sm_items ( 'empty' = _ Acc , From , To , NodeId , Lang ) - >
disco_sm_items ( { result , [ ] } , From , To , NodeId , Lang ) ;
disco_sm_items ( { result , OtherItems } = _ Acc , From , To , NodeId , _ Lang ) - >
{ result , disco_items ( To , NodeId , From ) ++ OtherItems } .
- spec ( disco_items / 3 : :
(
Host : : jidContact ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
2010-11-10 21:27:53 +01:00
- > Items : : [ ] | [ Item : : #xmlel { } ]
2010-10-19 17:08:59 +02:00
) .
disco_items ( #jid { raw = JID , node = U , domain = S , resource = R } = Host , < < > > , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { id = { _ , NodeId } , options = Options , type = Type , idx = Nidx , owners = Owners } , Acc ) - >
case get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) of
2010-10-19 17:08:59 +02:00
{ result , _ } - >
[ #xmlel { name = 'item' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , JID ) ,
? XMLATTR ( < < " node " > > , NodeId ) |
2010-10-19 17:08:59 +02:00
case get_option ( Options , 'title' ) of
false - > [ ] ;
2010-11-29 20:44:31 +01:00
[ Title ] - > [ ? XMLATTR ( < < " title " > > , Title ) ]
2010-10-19 17:08:59 +02:00
end ] }
| Acc ] ;
_ - > Acc
end
end ,
case transaction ( { U , S , R } , Action , sync_dirty ) of
{ result , Items } - > Items
%_ -> _Items = []
2010-08-04 18:30:22 +02:00
end ;
2007-12-01 06:16:30 +01:00
2010-10-19 17:08:59 +02:00
disco_items ( #jid { raw = JID , node = U , domain = S , resource = R } = Host , NodeId , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { idx = Nidx , type = Type , options = Options , owners = Owners } ) - >
case get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) of
2010-10-19 17:08:59 +02:00
{ result , Items } - >
{ result ,
[ #xmlel { name = 'item' , ns = ? NS_DISCO_INFO ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , JID ) ,
? XMLATTR ( < < " name " > > , ItemId ) ] }
2010-10-19 17:08:59 +02:00
| | #pubsub_item { id = { ItemId , _ } } < - Items ] } ;
_ - > { result , [ ] }
end
end ,
case transaction ( { U , S , R } , NodeId , Action , sync_dirty ) of
2010-08-04 18:30:22 +02:00
{ result , { _ , Result } } - > Result ;
_ - > [ ]
2010-10-19 17:08:59 +02:00
end .
2007-12-11 17:19:17 +01:00
%% -------
%% presence hooks handling functions
%%
2010-12-24 16:21:28 +01:00
caps_update ( From , To , _ Features ) - >
Pid = ejabberd_sm : get_session_pid ( From ) ,
2010-12-02 14:05:19 +01:00
presence_probe ( From , To , Pid ) .
2010-10-19 17:08:59 +02:00
- spec ( presence_probe / 3 : :
(
Peer : : jidEntity ( ) ,
JID : : jidEntity ( ) ,
Pid : : pid ( ) )
- > 'ok'
| { 'presence' , JID : : jidEntity ( ) , Pid : : pid ( ) }
| { 'presence' , User : : binary ( ) , Server : : binary ( ) , [ Resource : : binary ( ) ] , JID : : jidEntity ( ) }
) .
presence_probe ( #jid { node = User , domain = Server , resource = Resource } = Peer , #jid { domain = Host } = JID , Pid ) - >
2009-12-08 19:59:00 +01:00
case exmpp_jid : full_compare ( Peer , JID ) of
2010-01-12 16:14:57 +01:00
true - > %% JID are equals
2010-10-19 17:08:59 +02:00
presence ( Server , { 'presence' , JID , Pid } ) ,
presence ( Server , { 'presence' , User , Server , [ Resource ] , JID } ) ;
2010-01-12 16:14:57 +01:00
false - >
case exmpp_jid : bare_compare ( Peer , JID ) of
true - >
%% ignore presence_probe from other ressources for the current user
%% this way, we do not send duplicated last items if user already connected with other clients
ok ;
false - >
2010-10-19 17:08:59 +02:00
presence ( Host , { 'presence' , User , Server , [ Resource ] , JID } )
2010-01-12 16:14:57 +01:00
end
2009-12-08 19:59:00 +01:00
end .
2010-09-10 19:45:28 +02:00
2010-10-19 17:08:59 +02:00
- spec ( presence / 2 : :
(
ServerHost : : string ( ) | binary ( ) ,
Presence : : { 'presence' , JID : : jidEntity ( ) , Pid : : pid ( ) }
| { 'presence' , User : : binary ( ) , Server : : binary ( ) , [ Resource : : binary ( ) ] , JID : : jidEntity ( ) } )
- > { 'presence' , JID : : jidEntity ( ) , Pid : : pid ( ) }
| { 'presence' , User : : binary ( ) , Server : : binary ( ) , [ Resource : : binary ( ) ] , JID : : jidEntity ( ) }
) .
2010-01-13 15:53:05 +01:00
presence ( ServerHost , Presence ) when is_binary ( ServerHost ) - >
presence ( binary_to_list ( ServerHost ) , Presence ) ;
2010-01-12 16:14:57 +01:00
presence ( ServerHost , Presence ) - >
2010-01-13 11:25:06 +01:00
SendLoop = case whereis ( gen_mod : get_module_proc ( ServerHost , ? LOOPNAME ) ) of
2010-10-19 17:08:59 +02:00
undefined - >
% in case send_loop process died, we rebuild a minimal State record and respawn it
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 ,
2010-01-13 11:25:06 +01:00
SendLoop ! Presence .
2006-02-02 06:00:27 +01:00
2009-03-04 00:26:07 +01:00
%% -------
%% subscription hooks handling functions
%%
2009-03-10 12:13:46 +01:00
out_subscription ( User , Server , JID , subscribed ) - >
2009-06-01 18:26:00 +02:00
Owner = exmpp_jid : make ( User , Server , " " ) ,
2009-03-10 12:13:46 +01:00
{ U , S , R } = jlib : short_prepd_jid ( JID ) ,
Rs = case R of
2010-10-19 17:08:59 +02:00
undefined - > user_resources ( U , S ) ;
_ - > [ R ]
end ,
2010-10-18 18:41:29 +02:00
presence ( Server , { presence , U , S , Rs , Owner } ) ,
true ;
2009-03-10 12:13:46 +01:00
out_subscription ( _ , _ , _ , _ ) - >
2010-10-18 18:41:29 +02:00
true .
2009-05-29 01:21:50 +02:00
in_subscription ( _ , User , Server , Owner , unsubscribed , _ ) - >
2010-10-18 18:41:29 +02:00
unsubscribe_user ( exmpp_jid : make ( User , Server , " " ) , Owner ) ,
true ;
2009-05-08 02:02:08 +02:00
in_subscription ( _ , _ , _ , _ , _ , _ ) - >
2010-10-18 18:41:29 +02:00
true .
2009-03-04 00:26:07 +01:00
2010-01-12 16:14:57 +01:00
unsubscribe_user ( Entity , Owner ) - >
BJID = jlib : short_prepd_bare_jid ( Owner ) ,
Host = host ( element ( 2 , BJID ) ) ,
spawn ( fun ( ) - >
2010-10-19 17:08:59 +02:00
lists : foreach ( fun ( PType ) - >
{ result , Subscriptions } = node_action ( Host , PType , get_entity_subscriptions , [ Host , Entity ] ) ,
lists : foreach ( fun
( { #pubsub_node { options = Options , owners = Owners , idx = Nidx } , subscribed , _ , JID } ) - >
case get_option ( Options , access_model ) of
presence - >
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 , Subscriptions )
end , plugins ( Host ) )
end ) .
2010-01-12 16:14:57 +01:00
2007-12-18 15:45:26 +01:00
%% -------
%% user remove hook handling function
%%
2010-05-10 17:01:20 +02:00
%% @spec(User::binary(), Server::binary()) -> any()
remove_user ( UserB , ServerB ) - >
User = binary_to_list ( UserB ) ,
Server = binary_to_list ( ServerB ) ,
2008-12-05 16:13:09 +01:00
LUser = exmpp_stringprep : nodeprep ( User ) ,
LServer = exmpp_stringprep : nameprep ( Server ) ,
2010-01-12 16:14:57 +01:00
Entity = exmpp_jid : make ( LUser , LServer ) ,
Host = host ( LServer ) ,
2010-03-11 16:07:56 +01:00
HomeTreeBase = string_to_node ( " /home/ " ++ LServer ++ " / " ++ LUser ) ,
2010-01-12 16:14:57 +01:00
spawn ( fun ( ) - >
2010-10-19 17:08:59 +02:00
%% remove user's subscriptions
lists : foreach ( fun ( PType ) - >
{ result , Subscriptions } = node_action ( Host , PType , get_entity_subscriptions , [ Host , Entity ] ) ,
lists : foreach ( fun
( { #pubsub_node { idx = Nidx } , _ , _ , JID } ) - > node_action ( Host , PType , unsubscribe_node , [ Nidx , Entity , JID , all ] )
end , Subscriptions ) ,
{ result , Affiliations } = node_action ( Host , PType , get_entity_affiliations , [ Host , Entity ] ) ,
lists : foreach ( fun
( { #pubsub_node { id = { H , N } , parents = [ ] } , owner } ) - > delete_node ( H , N , Entity ) ;
( { #pubsub_node { id = { H , N } , type = " hometree " } , owner } ) when N == HomeTreeBase - > delete_node ( H , N , Entity ) ;
( { #pubsub_node { idx = Nidx } , publisher } ) - > node_action ( Host , PType , set_affiliation , [ Nidx , Entity , none ] ) ;
( _ ) - > ok
end , Affiliations )
end , plugins ( Host ) )
end ) .
2007-12-18 15:45:26 +01:00
2006-02-02 06:00:27 +01:00
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% Function:
%% handle_call(Request, From, State) -> {reply, Reply, State} |
2006-02-02 06:00:27 +01:00
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% @private
handle_call ( server_host , _ From , State ) - >
{ reply , State #state.server_host , State } ;
handle_call ( plugins , _ From , State ) - >
{ reply , State #state.plugins , State } ;
2008-10-08 14:02:30 +02:00
handle_call ( pep_mapping , _ From , State ) - >
{ reply , State #state.pep_mapping , State } ;
2007-12-01 06:16:30 +01:00
handle_call ( nodetree , _ From , State ) - >
{ reply , State #state.nodetree , State } ;
2006-02-02 06:00:27 +01:00
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
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% @private
2006-02-02 06:00:27 +01:00
handle_cast ( _ Msg , State ) - >
{ noreply , State } .
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% @private
2010-10-19 17:08:59 +02:00
- spec ( handle_info / 2 : :
(
Info : : { 'route' ,
From : : jidEntity ( ) ,
To : : jidComponent ( ) ,
2010-11-10 21:27:53 +01:00
Packet : : #xmlel { } } ,
2010-10-19 17:08:59 +02:00
State : : #state { } )
- > { 'noreply' , State : : #state { } }
) .
%% TODO : what to do when JID recipient contains a resource ?
handle_info ( { route , From ,
#jid { node = undefined , domain = Host , resource = undefined } = To , Packet } ,
2007-12-01 06:16:30 +01:00
#state { server_host = ServerHost ,
access = Access ,
plugins = Plugins } = State ) - >
2010-10-19 17:08:59 +02:00
case catch do_route ( list_to_binary ( ServerHost ) , Access , Plugins , Host , From , To , Packet ) of
2007-12-01 06:16:30 +01:00
{ 'EXIT' , Reason } - > ? ERROR_MSG ( " ~p " , [ Reason ] ) ;
_ - > ok
2006-02-02 06:00:27 +01:00
end ,
2010-10-19 17:08:59 +02:00
{ 'noreply' , State } ;
2006-02-02 06:00:27 +01:00
handle_info ( _ Info , State ) - >
2010-10-19 17:08:59 +02:00
{ 'noreply' , State } . %% TODO : handle other case with recipient full JID
2006-02-02 06:00:27 +01:00
%%--------------------------------------------------------------------
%% 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.
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% @private
2010-10-19 17:08:59 +02:00
- spec ( terminate / 2 : :
(
Reason : : _ ,
State : : #state { } )
- > 'ok'
) .
2007-12-01 06:16:30 +01:00
terminate ( _ Reason , #state { host = Host ,
server_host = ServerHost ,
nodetree = TreePlugin ,
2010-01-13 11:25:06 +01:00
plugins = Plugins } ) - >
2007-12-01 06:16:30 +01:00
ejabberd_router : unregister_route ( Host ) ,
2009-01-12 19:17:05 +01:00
ServerHostB = list_to_binary ( ServerHost ) ,
2009-04-10 15:21:37 +02:00
case lists : member ( ? PEPNODE , Plugins ) of
2010-10-19 17:08:59 +02:00
true - >
2010-12-02 14:05:19 +01:00
ejabberd_hooks : delete ( caps_update , ServerHostB , ? MODULE , caps_update , 80 ) ,
2010-10-19 17:08:59 +02:00
ejabberd_hooks : delete ( disco_sm_identity , ServerHostB , ? MODULE , disco_sm_identity , 75 ) ,
ejabberd_hooks : delete ( disco_sm_features , ServerHostB , ? MODULE , disco_sm_features , 75 ) ,
ejabberd_hooks : delete ( disco_sm_items , ServerHostB , ? MODULE , disco_sm_items , 75 ) ,
gen_iq_handler : remove_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB ) ,
gen_iq_handler : remove_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB_OWNER ) ;
false - >
ok
2009-04-10 15:21:37 +02:00
end ,
2010-03-05 16:09:06 +01:00
ejabberd_hooks : delete ( sm_remove_connection_hook , ServerHostB , ? MODULE , on_user_offline , 75 ) ,
2009-01-12 19:17:05 +01:00
ejabberd_hooks : delete ( disco_local_identity , ServerHostB , ? MODULE , disco_local_identity , 75 ) ,
ejabberd_hooks : delete ( disco_local_features , ServerHostB , ? MODULE , disco_local_features , 75 ) ,
ejabberd_hooks : delete ( disco_local_items , ServerHostB , ? MODULE , disco_local_items , 75 ) ,
2009-05-11 19:27:55 +02:00
ejabberd_hooks : delete ( presence_probe_hook , ServerHostB , ? MODULE , presence_probe , 80 ) ,
2009-05-08 02:02:08 +02:00
ejabberd_hooks : delete ( roster_in_subscription , ServerHostB , ? MODULE , in_subscription , 50 ) ,
2009-03-10 12:13:46 +01:00
ejabberd_hooks : delete ( roster_out_subscription , ServerHostB , ? MODULE , out_subscription , 50 ) ,
2009-01-12 19:17:05 +01:00
ejabberd_hooks : delete ( remove_user , ServerHostB , ? MODULE , remove_user , 50 ) ,
2009-05-07 02:54:44 +02:00
ejabberd_hooks : delete ( anonymous_purge_hook , ServerHostB , ? MODULE , remove_user , 50 ) ,
2009-04-10 15:21:37 +02:00
gen_iq_handler : remove_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB ) ,
gen_iq_handler : remove_iq_handler ( ejabberd_sm , ServerHostB , ? NS_PUBSUB_OWNER ) ,
2010-04-14 01:08:22 +02:00
mod_disco : unregister_feature ( ServerHostB , ? NS_PUBSUB_s ) ,
2010-01-13 11:25:06 +01:00
gen_mod : get_module_proc ( ServerHost , ? LOOPNAME ) ! stop ,
2009-05-07 02:54:44 +02:00
terminate_plugins ( Host , ServerHost , Plugins , TreePlugin ) .
2006-02-02 06:00:27 +01:00
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
2007-12-01 06:16:30 +01:00
%% @private
2006-02-02 06:00:27 +01:00
code_change ( _ OldVsn , State , _ Extra ) - >
{ ok , State } .
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
2010-10-19 17:08:59 +02:00
- spec ( do_route / 7 : :
(
ServerHost : : binary ( ) ,
Access : : atom ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] ,
Host : : hostPubsub ( ) ,
From : : jidEntity ( ) ,
To : : jidComponent ( ) ,
2010-11-10 21:27:53 +01:00
Packet : : #xmlel { } )
2010-10-19 17:08:59 +02:00
- > any ( )
) .
%%% <iq/>
do_route ( ServerHost , Access , Plugins , Host , From , To , #xmlel { name = 'iq' } = Packet ) - >
case exmpp_iq : xmlel_to_iq ( Packet ) of
%% Service discovery : disco#info
#iq { type = 'get' , ns = ? NS_DISCO_INFO , payload = #xmlel { attrs = Attrs } , lang = Lang } - >
2010-11-29 20:44:31 +01:00
NodeId = exmpp_xml : get_attribute_from_list ( Attrs , < < " node " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
Info = ejabberd_hooks : run_fold (
disco_info , ServerHost , [ ] ,
[ ServerHost , ? MODULE , < < > > , " " ] ) ,
Res = case iq_disco_info ( Host , NodeId , From , Lang ) of
{ result , IQRes } - >
Result = #xmlel { ns = ? NS_DISCO_INFO ,
name = 'query' ,
attrs = Attrs ,
children = IQRes ++ Info } ,
exmpp_iq : result ( Packet , Result ) ;
{ error , Error } - >
exmpp_iq : error ( Packet , Error )
end ,
ejabberd_router : route ( To , From , Res ) ;
%% Service discovery : disco#items
#iq { type = 'get' , ns = ? NS_DISCO_ITEMS , payload = #xmlel { attrs = Attrs } } - >
2010-11-29 20:44:31 +01:00
NodeId = exmpp_xml : get_attribute_from_list ( Attrs , < < " node " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
Res = case iq_disco_items ( Host , NodeId , From ) of
{ result , IQRes } - >
Result = #xmlel { ns = ? NS_DISCO_ITEMS ,
name = 'query' ,
attrs = Attrs ,
children = IQRes } ,
exmpp_iq : result ( Packet , Result ) ;
{ error , Error } - >
exmpp_iq : error ( Packet , Error )
end ,
ejabberd_router : route ( To , From , Res ) ;
%% pubsub
#iq { type = IQType , ns = ? NS_PUBSUB , lang = Lang , payload = #xmlel { } = SubEl }
when IQType == 'get' orelse IQType == 'set' - >
Res = case iq_pubsub ( Host , ServerHost , From , IQType , SubEl , Lang , Access , Plugins ) of
{ result , [ ] } - > exmpp_iq : result ( Packet ) ;
{ result , IQRes } - > exmpp_iq : result ( Packet , IQRes ) ;
{ error , Error } - > exmpp_iq : error ( Packet , Error )
end ,
ejabberd_router : route ( To , From , Res ) ;
%% pubsub#owner
#iq { type = IQType , ns = ? NS_PUBSUB_OWNER , lang = Lang , payload = #xmlel { } = SubEl }
when IQType == 'get' orelse IQType == 'set' - >
Res = case iq_pubsub_owner ( Host , ServerHost , From , IQType , SubEl , Lang ) of
{ result , [ ] } - > exmpp_iq : result ( Packet ) ;
{ result , IQRes } - > exmpp_iq : result ( Packet , IQRes ) ;
{ error , Error } - > exmpp_iq : error ( Packet , Error )
end ,
ejabberd_router : route ( To , From , Res ) ; %% TODO : add error handling
%% vCard
#iq { type = 'get' , ns = ? NS_VCARD = XMLNS , lang = Lang } - >
VCard = #xmlel { ns = XMLNS ,
name = 'vCard' ,
children = iq_get_vcard ( Lang ) } ,
Res = exmpp_iq : result ( Packet , VCard ) ,
ejabberd_router : route ( To , From , Res ) ;
%% Ad hoc commands
#iq { type = 'set' , ns = ? NS_ADHOC } = IQ - >
Res = case iq_command ( Host , ServerHost , From , IQ , Access , Plugins ) of
{ error , Error } - > exmpp_iq : error ( Packet , Error ) ;
{ result , IQRes } - > exmpp_iq : result ( Packet , IQRes )
end ,
ejabberd_router : route ( To , From , Res ) ; %%TODO : add error handling
%% Other <iq/>
_ - >
Err = exmpp_iq : error ( Packet , 'feature-not-implemented' ) ,
ejabberd_router : route ( To , From , Err )
end ;
2009-08-25 19:14:30 +02:00
2010-10-19 17:08:59 +02:00
%%% <message/>
do_route ( ServerHost , _ Access , _ Plugins , Host , From , To ,
#xmlel { name = 'message' , children = Els } = Packet ) - >
case exmpp_stanza : is_stanza_error ( Packet ) of
true - >
ok ;
false - >
case exmpp_xml : remove_cdata_from_list ( Els ) of
%% <message><x xmlns='jabber:x:data'/></message>
[ #xmlel { name = 'x' , ns = ? NS_DATA_FORMS } ] - >
case find_authorization_response ( Packet ) of
none - > ok ;
invalid - >
ejabberd_router : route ( To , From , exmpp_message : error ( Packet , 'bad-request' ) ) ;
XFields - >
handle_authorization_response ( Host , From , To , Packet , XFields )
2003-07-08 22:11:27 +02:00
end ;
2010-10-19 17:08:59 +02:00
%% Pubsub vanilla <message/>
[ #xmlel { name = 'pubsub' , ns = ? NS_PUBSUB } = Pubsub ] - >
case exmpp_xml : get_element ( Pubsub , 'publish' ) of
undefined - >
2007-12-01 06:16:30 +01:00
ok ;
2010-10-19 17:08:59 +02:00
Publish - >
case exmpp_xml : get_element ( Publish , 'item' ) of
undefined - >
ok ;
#xmlel { attrs = Attrs , children = Els } = _ Item - >
2010-11-29 20:44:31 +01:00
NodeId = exmpp_xml : get_attribute ( Publish , < < " node " > > , < < > > ) ,
ItemId = exmpp_xml : get_attribute_from_list ( Attrs , < < " id " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
case publish_item ( Host , ServerHost , NodeId , From , ItemId , Els ) of
{ result , _ } - >
2010-05-17 10:31:53 +02:00
ok ;
2010-10-19 17:08:59 +02:00
{ error , Reason } - >
ejabberd_router : route ( To , From , exmpp_message : error ( Packet , Reason ) )
end
2007-12-01 06:16:30 +01:00
end
end ;
2010-10-19 17:08:59 +02:00
%% Other <message/>
2003-07-08 22:11:27 +02:00
_ - >
ok
end
2010-10-19 17:08:59 +02:00
end ;
2003-07-08 22:11:27 +02:00
2010-10-19 17:08:59 +02:00
%%<presence/>
do_route ( _ ServerHost , _ Access , _ Plugins , _ Host , _ From , _ To , _ Packet ) - >
true .
%% Other cases ?
%do_route(_, _, _, _, From, To, Packet) ->
% case exmpp_stanza:get_type(Packet) of
% <<"error">> -> ok;
% <<"result">> -> ok;
% _ ->
% Err = exmpp_stanza:reply_with_error(Packet, 'item-not-found'),
% ejabberd_router:route(To, From, Err)
% end.
- spec ( command_disco_info / 3 : :
(
Host : : hostPubsub ( ) , %% Host::host() TODO : implement ad hoc commands for PEP
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
2010-11-10 21:27:53 +01:00
- > { result , Info : : [ #xmlel { } ] }
| { result , Info : : [ #xmlel { }
| #xmlel { } ] }
2010-10-19 17:08:59 +02:00
) .
command_disco_info ( _ Host , ? NS_ADHOC_b = _ NodeId , _ From ) - >
{ result ,
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'identity' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " automation " > > ) ,
? XMLATTR ( < < " type " > > , < < " command-list " > > ) ] } ] } ;
2010-10-19 17:08:59 +02:00
command_disco_info ( _ Host , ? NS_PUBSUB_GET_PENDING_b = _ NodeId , _ From ) - >
{ result ,
% Identity
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'identity' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " automation " > > ) ,
? XMLATTR ( < < " type " > > , < < " command-node " > > ) ] } ,
2010-10-19 17:08:59 +02:00
% Features
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_ADHOC ) ] } ] } .
2010-10-19 17:08:59 +02:00
- spec ( node_disco_info / 3 : :
(
Host : : hostPubsub ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
2010-11-10 21:27:53 +01:00
- > { 'result' , [ #xmlel { }
| #xmlel { } ] }
2010-10-19 17:08:59 +02:00
| { 'error' , _ }
) .
node_disco_info ( Host , NodeId , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { type = Plugin , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
Types = case tree_call ( Host , get_subnodes , [ Host , NodeId , From ] ) of
[ ] - > [ " leaf " ] ;
_ - >
2011-05-30 14:04:42 +02:00
case node_call ( Plugin , get_items , [ Nidx , From ] ) of
2010-10-19 17:08:59 +02:00
{ result , [ ] } - > [ " collection " ] ;
{ result , _ } - > [ " leaf " , " collection " ] ;
_ - > [ ]
end
end ,
%% TODO: add meta-data info (spec section 5.4)
{ result ,
%% Identities
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'identity' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , < < " pubsub " > > ) ,
? XMLATTR ( < < " type " > > , Type ) ] } | | Type < - Types ]
2010-10-19 17:08:59 +02:00
++
%% Features
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_PUBSUB_b ) ] } |
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , list_to_binary ( ? NS_PUBSUB_s ++ " # " ++ Type ) ) ] }
2010-10-19 17:08:59 +02:00
| | Type < - features ( Plugin ) ] ] }
end ,
case transaction ( Host , NodeId , Action , sync_dirty ) of
2009-05-01 01:17:38 +02:00
{ result , { _ , Result } } - > { result , Result } ;
2010-10-19 17:08:59 +02:00
Error - > Error
2009-05-01 01:17:38 +02:00
end .
2003-07-08 22:11:27 +02:00
2010-10-19 17:08:59 +02:00
- spec ( iq_disco_info / 4 : :
(
Host : : hostPubsub ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) ,
Lang : : binary ( ) )
2010-11-10 21:27:53 +01:00
- > { 'result' , [ #xmlel { }
| #xmlel { } ] }
2010-10-19 17:08:59 +02:00
| { 'error' , _ }
) .
iq_disco_info ( Host , < < > > = _ NodeId , _ From , Lang ) - >
{ result ,
%% Identities
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'identity' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " category " > > , " pubsub " ) ,
? XMLATTR ( < < " type " > > , " service " ) ,
? XMLATTR ( < < " name " > > , translate : translate ( Lang , " Publish-Subscribe " ) ) ] } ,
2010-10-19 17:08:59 +02:00
%% Features
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_DISCO_INFO_b ) ] } ,
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_DISCO_ITEMS_b ) ] } ,
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_PUBSUB_b ) ] } ,
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_ADHOC_b ) ] } ,
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , ? NS_VCARD_b ) ] } ]
2010-10-19 17:08:59 +02:00
++
[ #xmlel { ns = ? NS_DISCO_INFO ,
name = 'feature' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " var " > > , list_to_binary ( ? NS_PUBSUB_s ++ " # " ++ Feature ) ) ] }
2010-10-19 17:08:59 +02:00
| | Feature < - features ( Host , < < > > ) ] } ;
iq_disco_info ( Host , NodeId , From , _ Lang )
when NodeId == ? NS_ADHOC_b orelse NodeId == ? NS_PUBSUB_GET_PENDING_b - >
command_disco_info ( Host , NodeId , From ) ;
iq_disco_info ( Host , NodeId , From , _ Lang ) - >
node_disco_info ( Host , NodeId , From ) .
- spec ( iq_disco_items / 3 : :
(
Host : : hostPubsub ( ) ,
NodeId : : nodeId ( ) ,
From : : jidEntity ( ) )
2010-11-10 21:27:53 +01:00
- > { 'result' , [ ] | [ #xmlel { } ] }
2010-10-19 17:08:59 +02:00
| { 'error' , _ }
) .
iq_disco_items ( Host , < < > > = _ NodeId , From ) - >
2009-10-27 15:24:33 +01:00
case tree_action ( Host , get_subnodes , [ Host , < < > > , From ] ) of
Nodes when is_list ( Nodes ) - >
{ result , lists : map (
2010-10-19 17:08:59 +02:00
fun ( #pubsub_node { id = { _ , SubNodeId } , options = Options } ) - >
Attrs =
case get_option ( Options , 'title' ) of
false - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , Host ) | nodeAttr ( SubNodeId ) ] ;
2010-10-19 17:08:59 +02:00
Title - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , Host ) , ? XMLATTR ( < < " name " > > , Title ) | nodeAttr ( SubNodeId ) ]
2010-10-19 17:08:59 +02:00
end ,
#xmlel { ns = ? NS_DISCO_ITEMS , name = 'item' , attrs = Attrs }
end , Nodes ) } ;
2009-10-27 15:24:33 +01:00
Other - >
Other
end ;
2010-10-19 17:08:59 +02:00
iq_disco_items ( Host , ? NS_ADHOC_b = _ NodeId , _ From ) - >
2009-11-04 21:17:08 +01:00
%% TODO: support localization of this string
2010-10-19 17:08:59 +02:00
{ result ,
[ #xmlel { ns = ? NS_DISCO_ITEMS ,
name = 'item' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , Host ) ,
? XMLATTR ( < < " node " > > , ? NS_PUBSUB_GET_PENDING_b ) ,
? XMLATTR ( < < " name " > > , " Get Pending " ) ] } ] } ;
2010-10-19 17:08:59 +02:00
iq_disco_items ( _ Host , ? NS_PUBSUB_GET_PENDING_b = _ NodeId , _ From ) - >
%% TODO
{ result , [ ] } ;
iq_disco_items ( Host , NodeId , From ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { idx = Nidx , type = Type , options = Options , owners = Owners } ) - >
NodeItems = case get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) of
2010-10-19 17:08:59 +02:00
{ result , R } - > R ;
_ - > [ ]
end ,
Nodes = lists : map (
fun ( #pubsub_node { id = { _ , SubNodeId } , options = SubOptions } ) - >
Attrs =
case get_option ( SubOptions , 'title' ) of
false - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , Host ) | nodeAttr ( SubNodeId ) ] ;
2010-10-19 17:08:59 +02:00
Title - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , Host ) , ? XMLATTR ( < < " name " > > , Title ) | nodeAttr ( SubNodeId ) ]
2010-10-19 17:08:59 +02:00
end ,
#xmlel { ns = ? NS_DISCO_ITEMS ,
name = 'item' ,
attrs = Attrs }
end , tree_call ( Host , get_subnodes , [ Host , NodeId , From ] ) ) ,
Items = lists : map (
fun ( #pubsub_item { id = { RN , _ } } ) - >
{ result , Name } = node_call ( Type , get_item_name , [ Host , NodeId , RN ] ) ,
#xmlel { ns = ? NS_DISCO_ITEMS ,
name = 'item' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , Host ) ,
? XMLATTR ( < < " name " > > , Name ) ] }
2010-10-19 17:08:59 +02:00
end , NodeItems ) ,
{ result , Nodes ++ Items }
end ,
case transaction ( Host , NodeId , Action , sync_dirty ) of
{ result , { _ , Result } } - > { result , Result } ;
Other - > Other
2003-07-08 22:11:27 +02:00
end .
2010-10-19 17:08:59 +02:00
2010-09-10 19:45:28 +02:00
get_allowed_items_call ( Host , Nidx , From , Type , Options , Owners ) - >
2010-08-04 18:30:22 +02:00
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 ) ,
2010-09-10 19:45:28 +02:00
node_call ( Type , get_items , [ Nidx , From , AccessModel , PresenceSubscription , RosterGroup , undefined ] ) .
2010-08-04 18:30:22 +02:00
get_presence_and_roster_permissions ( Host , From , Owners , AccessModel , AllowedGroups ) - >
if ( AccessModel == presence ) or ( AccessModel == roster ) - >
2010-10-19 17:08:59 +02:00
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 ( iq_sm / 3 : :
(
From : : jidEntity ( ) ,
To : : jidContact ( ) ,
2010-11-10 21:27:53 +01:00
IQ : : #iq { type : : 'get' | 'set' } )
- > #iq { type : : 'result' | 'error' }
2010-10-19 17:08:59 +02:00
) .
iq_sm ( From , #jid { node = U , domain = S , resource = undefined = R } = _ To ,
#iq { type = Type , payload = #xmlel { } = SubEl , ns = XMLNS , lang = Lang } = IQ )
when ( Type == 'get' orelse Type == 'set' ) - >
Result = case XMLNS of
? NS_PUBSUB - > iq_pubsub ( { U , S , R } , S , From , Type , SubEl , Lang ) ;
? NS_PUBSUB_OWNER - > iq_pubsub_owner ( { U , S , R } , S , From , Type , SubEl , Lang )
end ,
case Result of
{ result , [ ] } - > exmpp_iq : result ( IQ ) ;
{ result , IQResult } - > exmpp_iq : result ( IQ , IQResult ) ;
{ error , Error } - > exmpp_iq : error ( IQ , Error )
2007-12-01 06:16:30 +01:00
end .
2010-10-19 17:08:59 +02:00
%%TODO : other IQ type and other cases
%iq_sm(_,_,IQ) -> exmpp_iq:result(IQ).
- spec ( iq_get_vcard / 1 : :
(
Lang : : binary ( ) )
- > Vcard : : [ #xmlel { } ]
) .
2003-07-08 22:11:27 +02:00
2007-12-01 06:16:30 +01:00
iq_get_vcard ( Lang ) - >
2008-12-05 16:13:09 +01:00
[ #xmlel { ns = ? NS_VCARD , name = 'FN' , children = [ #xmlcdata { cdata = < < " ejabberd/mod_pubsub " > > } ] } ,
#xmlel { ns = ? NS_VCARD , name = 'URL' , children = [ #xmlcdata { cdata = list_to_binary ( ? EJABBERD_URI ) } ] } ,
#xmlel { ns = ? NS_VCARD , name = 'DESC' , children =
2010-10-19 17:08:59 +02:00
[ #xmlcdata { cdata = list_to_binary (
translate : translate ( Lang ,
" ejabberd Publish-Subscribe module " ) ++
2011-02-14 13:47:22 +01:00
" \n Copyright (c) 2004-2011 ProcessOne " ) } ] } ] .
2010-10-19 17:08:59 +02:00
- spec ( iq_pubsub / 6 : :
(
Host : : host ( ) , % hostPubsub() | hostPEP()
ServerHost : : binary ( ) ,
From : : jidEntity ( ) ,
IQType : : 'get' | 'set' ,
2010-11-10 21:27:53 +01:00
SubEl : : #xmlel { } ,
2010-10-19 17:08:59 +02:00
Lang : : binary ( ) )
- > { 'result' , Result : : [ ] | #xmlel { } } | { 'error' , _ }
) .
2007-12-01 06:16:30 +01:00
iq_pubsub ( Host , ServerHost , From , IQType , SubEl , Lang ) - >
2010-10-19 17:08:59 +02:00
iq_pubsub ( Host , ServerHost , From , IQType , SubEl , Lang , 'all' , plugins ( ServerHost ) ) .
- spec ( iq_pubsub / 8 : :
(
Host : : host ( ) , % hostPubsub() | hostPEP()
ServerHost : : binary ( ) ,
From : : jidEntity ( ) ,
IQType : : 'get' | 'set' ,
2010-11-10 21:27:53 +01:00
SubEl : : #xmlel { } ,
2010-10-19 17:08:59 +02:00
Lang : : binary ( ) ,
Access : : atom ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] )
- > { 'result' , Result : : [ ] | #xmlel { } } | { 'error' , _ }
) .
iq_pubsub ( Host , ServerHost , From , IQType , #xmlel { children = Els } , Lang , Access , Plugins ) - >
case exmpp_xml : remove_cdata_from_list ( Els ) of
[ #xmlel { name = Name , attrs = Attrs , children = SubEls } | Rest ] - >
2010-03-22 20:27:13 +01:00
%% Fix bug when owner retrieves his affiliations
2010-11-29 20:44:31 +01:00
NodeId = exmpp_xml : get_attribute_from_list ( Attrs , < < " node " > > , < < > > ) ,
2007-12-01 06:16:30 +01:00
case { IQType , Name } of
2010-10-19 17:08:59 +02:00
{ 'set' , 'create' } - >
2009-09-20 10:14:24 +02:00
Config = case Rest of
2010-10-19 17:08:59 +02:00
[ #xmlel { name = 'configure' , children = C } ] - > C ;
_ - > [ ]
end ,
2009-03-04 02:14:13 +01:00
%% Get the type of the node
2010-11-29 20:44:31 +01:00
Plugin = case exmpp_xml : get_attribute_from_list_as_list ( Attrs , < < " type " > > , " " ) of
2010-10-19 17:08:59 +02:00
" " - > hd ( Plugins ) ;
T - > T
end ,
2009-03-04 02:14:13 +01:00
%% we use Plugins list matching because we do not want to allocate
%% atoms for non existing type, this prevent atom allocation overflow
2010-10-19 17:08:59 +02:00
case lists : member ( Plugin , Plugins ) of
2009-03-04 02:14:13 +01:00
false - >
2010-10-19 17:08:59 +02:00
{ error , extended_error ( 'feature-not-implemented' , unsupported , " create-nodes " ) } ;
2009-03-04 02:14:13 +01:00
true - >
2010-10-19 17:08:59 +02:00
create_node ( Host , ServerHost , NodeId , From , Plugin , Access , Config )
2007-12-01 06:16:30 +01:00
end ;
2010-10-19 17:08:59 +02:00
{ 'set' , 'publish' } - >
case exmpp_xml : remove_cdata_from_list ( SubEls ) of
2008-12-05 16:13:09 +01:00
[ #xmlel { name = 'item' , attrs = ItemAttrs , children = Payload } ] - >
2010-11-29 20:44:31 +01:00
ItemId = exmpp_xml : get_attribute_from_list ( ItemAttrs , < < " id " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
publish_item ( Host , ServerHost , NodeId , From , ItemId ,
exmpp_xml : remove_cdata_from_list ( Payload ) ) ;
2007-12-01 06:16:30 +01:00
[ ] - >
%% Publisher attempts to publish to persistent node with no item
2010-10-19 17:08:59 +02:00
{ error , extended_error ( 'bad-request' , " item-required " ) } ;
2003-07-08 22:11:27 +02:00
_ - >
2007-12-01 06:16:30 +01:00
%% Entity attempts to publish item with multiple payload elements or namespace does not match
2010-10-19 17:08:59 +02:00
{ error , extended_error ( 'bad-request' , " invalid-payload " ) }
2003-07-08 22:11:27 +02:00
end ;
2010-10-19 17:08:59 +02:00
{ 'set' , 'retract' } - >
2010-11-29 20:44:31 +01:00
ForceNotify = case exmpp_xml : get_attribute_from_list ( Attrs , < < " notify " > > , < < > > ) of
2010-10-19 17:08:59 +02:00
< < " 1 " > > - > true ;
< < " true " > > - > true ;
_ - > false
2007-12-01 06:16:30 +01:00
end ,
2010-10-19 17:08:59 +02:00
case exmpp_xml : remove_cdata_from_list ( SubEls ) of
2008-12-05 16:13:09 +01:00
[ #xmlel { name = 'item' , attrs = ItemAttrs } ] - >
2010-11-29 20:44:31 +01:00
ItemId = exmpp_xml : get_attribute_from_list ( ItemAttrs , < < " id " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
delete_item ( Host , NodeId , From , ItemId , ForceNotify ) ;
2003-07-08 22:11:27 +02:00
_ - >
2007-12-01 06:16:30 +01:00
%% Request does not specify an item
2010-10-19 17:08:59 +02:00
{ error , extended_error ( 'bad-request' , " item-required " ) }
2003-07-08 22:11:27 +02:00
end ;
2010-10-19 17:08:59 +02:00
{ 'set' , 'subscribe' } - >
2009-09-23 18:32:06 +02:00
Config = case Rest of
2010-10-19 17:08:59 +02:00
[ #xmlel { name = 'options' , children = C } ] - > C ;
_ - > [ ]
end ,
2010-11-29 20:44:31 +01:00
JID = exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
subscribe_node ( Host , NodeId , From , JID , Config ) ;
{ 'set' , 'unsubscribe' } - >
2010-11-29 20:44:31 +01:00
JID = exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , < < > > ) ,
SubId = exmpp_xml : get_attribute_from_list ( Attrs , < < " subid " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
unsubscribe_node ( Host , NodeId , From , JID , SubId ) ;
{ 'get' , 'items' } - >
2010-11-29 20:44:31 +01:00
MaxItems = exmpp_xml : get_attribute_from_list ( Attrs , < < " max_items " > > , < < > > ) ,
SubId = exmpp_xml : get_attribute_from_list ( Attrs , < < " subid " > > , < < > > ) ,
2010-09-10 19:45:28 +02:00
ItemIds = lists : foldl ( fun
2010-10-19 17:08:59 +02:00
( #xmlel { name = 'item' , attrs = ItemAttrs } , Acc ) - >
2010-11-29 20:44:31 +01:00
case exmpp_xml : get_attribute_from_list ( ItemAttrs , < < " id " > > , < < > > ) of
2010-10-19 17:08:59 +02:00
< < > > - > Acc ;
ItemId - > [ ItemId | Acc ]
end ;
( _ , Acc ) - > Acc
end , [ ] , exmpp_xml : remove_cdata_from_list ( SubEls ) ) ,
get_items ( Host , NodeId , From , SubId , MaxItems , ItemIds ) ;
{ 'get' , 'subscriptions' } - >
get_subscriptions ( Host , NodeId , From , Plugins ) ;
{ 'get' , 'affiliations' } - >
2007-12-01 06:16:30 +01:00
get_affiliations ( Host , From , Plugins ) ;
2010-10-19 17:08:59 +02:00
{ 'get' , 'options' } - >
2010-11-29 20:44:31 +01:00
SubId = exmpp_xml : get_attribute_from_list ( Attrs , < < " subid " > > , < < > > ) ,
JID = exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
get_options ( Host , NodeId , JID , SubId , Lang ) ;
{ 'set' , 'options' } - >
2010-11-29 20:44:31 +01:00
SubId = exmpp_xml : get_attribute_from_list ( Attrs , < < " subid " > > , < < > > ) ,
JID = exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , < < > > ) ,
2010-10-19 17:08:59 +02:00
set_options ( Host , NodeId , JID , SubId , SubEls ) ;
2007-12-01 06:16:30 +01:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'feature-not-implemented' }
2007-12-01 06:16:30 +01:00
end ;
2009-09-23 18:32:06 +02:00
Other - >
? INFO_MSG ( " Too many actions: ~p " , [ Other ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' }
2007-12-01 06:16:30 +01:00
end .
2010-10-19 17:08:59 +02:00
- spec ( iq_pubsub_owner / 6 : :
(
Host : : host ( ) , % hostPubsub() | hostPEP()
ServerHost : : binary ( ) ,
From : : jidEntity ( ) ,
IQType : : 'get' | 'set' ,
2010-11-10 21:27:53 +01:00
SubEl : : #xmlel { } ,
2010-10-19 17:08:59 +02:00
Lang : : binary ( ) )
- > { 'result' , Result : : [ ] | #xmlel { } } | { 'error' , _ }
) .
iq_pubsub_owner ( Host , ServerHost , From , IQType , #xmlel { children = Els } , Lang ) - >
case Action = exmpp_xml : remove_cdata_from_list ( Els ) of
[ #xmlel { name = Name , attrs = Attrs , children = SubEls } ] - >
2010-11-29 20:44:31 +01:00
NodeId = exmpp_xml : get_attribute_from_list ( Attrs , < < " node " > > , < < > > ) ,
2007-12-01 06:16:30 +01:00
case { IQType , Name } of
2010-10-19 17:08:59 +02:00
{ 'get' , 'configure' } - >
get_configure ( Host , ServerHost , NodeId , From , Lang ) ;
{ 'set' , 'configure' } - >
set_configure ( Host , NodeId , From , SubEls , Lang ) ;
{ 'get' , 'default' } - >
get_default ( Host , NodeId , From , Lang ) ;
{ 'set' , 'delete' } - >
delete_node ( Host , NodeId , From ) ;
{ 'set' , 'purge' } - >
purge_node ( Host , NodeId , From ) ;
{ 'get' , 'subscriptions' } - >
get_subscriptions ( Host , NodeId , From ) ;
{ 'set' , 'subscriptions' } - >
set_subscriptions ( Host , NodeId , From , exmpp_xml : remove_cdata_from_list ( SubEls ) ) ;
{ 'get' , 'affiliations' } - >
get_affiliations ( Host , NodeId , From ) ;
{ 'set' , 'affiliations' } - >
set_affiliations ( Host , NodeId , From , exmpp_xml : remove_cdata_from_list ( SubEls ) ) ;
2003-07-08 22:11:27 +02:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'feature-not-implemented' }
2003-07-08 22:11:27 +02:00
end ;
_ - >
2007-12-01 06:16:30 +01:00
? INFO_MSG ( " Too many actions: ~p " , [ Action ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' }
2003-07-08 22:11:27 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( iq_command / 6 : :
(
Host : : hostPubsub ( ) , %% TODO : ad hoc commands for PEP
ServerHost : : binary ( ) ,
From : : jidEntity ( ) ,
2010-11-10 21:27:53 +01:00
IQ : : #iq { type : : 'set' } ,
2010-10-19 17:08:59 +02:00
Access : : atom ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] )
2010-11-10 21:27:53 +01:00
- > { 'result' , [ Result : : #xmlel { } ] }
2010-10-19 17:08:59 +02:00
| { 'error' , Error : : _ }
) .
2009-08-25 19:14:30 +02:00
iq_command ( Host , ServerHost , From , IQ , Access , Plugins ) - >
case adhoc : parse_request ( IQ ) of
2010-10-19 17:08:59 +02:00
#adhoc_request { } = Request - >
case adhoc_request ( Host , ServerHost , From , Request , Access , Plugins ) of
#adhoc_response { } = Response - >
{ result , [ adhoc : produce_response ( Request , Response ) ] } ;
2009-08-25 19:14:30 +02:00
Error - >
Error
end ;
2010-10-19 17:08:59 +02:00
Error - >
Error
2009-08-25 19:14:30 +02:00
end .
%% @doc <p>Processes an Ad Hoc Command.</p>
2010-10-19 17:08:59 +02:00
- spec ( adhoc_request / 6 : :
(
Host : : hostPubsub ( ) , %% TODO : ad hoc commands for PEP
ServerHost : : binary ( ) ,
From : : jidEntity ( ) ,
Request : : #adhoc_request { } ,
Access : : atom ( ) ,
Plugins : : [ Plugin : : nodeType ( ) ] )
- > #adhoc_response { } | { 'error' , Error : : _ }
) .
2009-08-25 19:14:30 +02:00
adhoc_request ( Host , _ ServerHost , Owner ,
#adhoc_request { node = ? NS_PUBSUB_GET_PENDING ,
lang = Lang ,
action = " execute " ,
xdata = false } ,
2010-10-19 17:08:59 +02:00
_ Access , Plugins ) - >
2009-08-25 19:14:30 +02:00
send_pending_node_form ( Host , Owner , Lang , Plugins ) ;
adhoc_request ( Host , _ ServerHost , Owner ,
#adhoc_request { node = ? NS_PUBSUB_GET_PENDING ,
action = " execute " ,
xdata = XData } ,
2010-10-19 17:08:59 +02:00
_ Access , _ Plugins ) - >
2009-08-25 19:14:30 +02:00
ParseOptions = case XData of
2010-10-19 17:08:59 +02:00
#xmlel { name = 'x' } = XEl - >
2009-08-25 19:14:30 +02:00
case jlib : parse_xdata_submit ( XEl ) of
invalid - >
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , 'bad-request' ) } ;
XData2 - >
2009-09-23 18:32:06 +02:00
case set_xoption ( Host , XData2 , [ ] ) of
2009-08-25 19:14:30 +02:00
NewOpts when is_list ( NewOpts ) - >
{ result , NewOpts } ;
Err - >
Err
end
end ;
_ - >
? INFO_MSG ( " Bad XForm: ~p " , [ XData ] ) ,
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , '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 ( 'bad-request' , " bad-payload " ) }
end ;
Error - >
Error
end ;
2009-11-04 21:17:08 +01:00
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 ) ;
2009-08-25 19:14:30 +02:00
adhoc_request ( _ Host , _ ServerHost , _ Owner , Other , _ Access , _ Plugins ) - >
? DEBUG ( " Couldn't process ad hoc command: ~n ~p " , [ Other ] ) ,
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , '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 , exmpp_stanza : error ( ? NS_JABBER_CLIENT , 'feature-not-implemented' ) } ;
Ps - >
XOpts = lists : map ( fun ( Node ) - >
#xmlel { ns = ? NS_DATA_FORMS , name = 'option' ,
children = [
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_DATA_FORMS , name = 'value' ,
children = [
exmpp_xml : cdata ( node_to_string ( Node ) ) ] } ] }
2009-08-25 19:14:30 +02:00
end , get_pending_nodes ( Host , Owner , Ps ) ) ,
2010-11-29 20:44:31 +01:00
XForm = #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs = [ ? XMLATTR ( < < " type " > > , < < " form " > > ) ] ,
2010-10-19 17:08:59 +02:00
children = [
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " type " > > , < < " list-single " > > ) ,
? XMLATTR ( < < " var " > > , < < " pubsub#node " > > ) ] ,
2010-10-19 17:08:59 +02:00
children = lists : usort ( XOpts ) } ] } ,
2009-08-25 19:14:30 +02:00
#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
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 " ,
2010-06-09 07:56:33 +02:00
[ exmpp_jid : to_list ( Owner ) , Host , node_to_string ( Node ) ] ) ,
2009-08-25 19:14:30 +02:00
Action =
2010-09-10 19:45:28 +02:00
fun ( #pubsub_node { idx = Nidx , type = Type } ) - >
2009-08-25 19:14:30 +02:00
case lists : member ( " get-pending " , features ( Type ) ) of
true - >
2010-09-10 19:45:28 +02:00
case node_call ( Type , get_affiliation , [ Nidx , Owner ] ) of
2009-08-25 19:14:30 +02:00
{ result , owner } - >
2010-09-10 19:45:28 +02:00
node_call ( Type , get_node_subscriptions , [ Nidx ] ) ;
2009-08-25 19:14:30 +02:00
_ - >
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , 'forbidden' ) }
end ;
false - >
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , 'feature-not-implemented' ) }
end
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
2009-11-04 18:40:39 +01:00
{ result , { N , Subscriptions } } - >
2010-09-10 19:45:28 +02:00
lists : foreach ( fun ( { J , pending , _ SubId } ) - >
2010-10-19 17:08:59 +02:00
{ U , S , R } = J ,
send_authorization_request ( N , exmpp_jid : make ( U , S , R ) ) ;
( { J , pending } ) - >
{ U , S , R } = J ,
send_authorization_request ( N , exmpp_jid : make ( U , S , R ) ) ;
( _ ) - >
ok
end , Subscriptions ) ,
2009-08-25 19:14:30 +02:00
#adhoc_response { } ;
Err - >
Err
end .
2007-12-01 06:16:30 +01:00
%%% authorization handling
2010-09-10 19:45:28 +02:00
send_authorization_request ( #pubsub_node { owners = Owners , id = { Host , Node } } , Subscriber ) - >
2010-07-01 11:19:48 +02:00
Lang = < < " en " > > , %% TODO fix
2008-12-05 16:13:09 +01:00
{ U , S , R } = Subscriber ,
Stanza = #xmlel { ns = ? NS_JABBER_CLIENT , name = 'message' , children =
2010-11-29 20:44:31 +01:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs = [ ? XMLATTR ( < < " type " > > , < < " form " > > ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'title' , children =
[ #xmlcdata { cdata = list_to_binary ( translate : translate ( Lang , " PubSub subscriber request " ) ) } ] } ,
#xmlel { ns = ? NS_DATA_FORMS , name = 'instructions' , children =
[ #xmlcdata { cdata = list_to_binary ( translate : translate ( Lang , " Choose whether to approve this entity's subscription. " ) ) } ] } ,
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " var " > > , < < " FORM_TYPE " > > ) , ? XMLATTR ( < < " type " > > , < < " hidden " > > ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children = [ #xmlcdata { cdata = list_to_binary ( ? NS_PUBSUB_SUBSCRIBE_AUTH_s ) } ] } ] } ,
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " var " > > , < < " pubsub#node " > > ) , ? XMLATTR ( < < " type " > > , < < " text-single " > > ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , " Node ID " ) ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children =
[ #xmlcdata { cdata = Node } ] } ] } ,
2010-11-29 20:44:31 +01:00
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs = [ ? XMLATTR ( < < " var " > > , < < " pubsub#subscriber_jid " > > ) ,
? XMLATTR ( < < " type " > > , < < " jid-single " > > ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , " Subscriber Address " ) ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children =
[ #xmlcdata { cdata = exmpp_jid : to_binary ( U , S , R ) } ] } ] } ,
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " var " > > , < < " pubsub#allow " > > ) ,
? XMLATTR ( < < " type " > > , < < " boolean " > > ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , " Allow this Jabber ID to subscribe to this pubsub node? " ) ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children = [ #xmlcdata { cdata = < < " false " > > } ] } ] } ] } ] } ,
2009-05-01 01:17:38 +02:00
lists : foreach ( fun ( Owner ) - >
2010-10-19 17:08:59 +02:00
{ U , S , R } = Owner ,
ejabberd_router : route ( service_jid ( Host ) , exmpp_jid : make ( U , S , R ) , Stanza )
end , Owners ) .
2007-12-01 06:16:30 +01:00
find_authorization_response ( Packet ) - >
2008-12-05 16:13:09 +01:00
Els = Packet #xmlel.children ,
XData1 = lists : map ( fun ( #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs = XAttrs } = XEl ) - >
2010-11-29 20:44:31 +01:00
case exmpp_xml : get_attribute_from_list_as_list ( XAttrs , < < " type " > > , " " ) of
2008-12-05 16:13:09 +01:00
" cancel " - >
none ;
2007-12-01 06:16:30 +01:00
_ - >
2008-12-05 16:13:09 +01:00
jlib : parse_xdata_submit ( XEl )
2007-12-01 06:16:30 +01:00
end ;
( _ ) - >
none
2010-07-01 11:19:48 +02:00
end , exmpp_xml : remove_cdata_from_list ( Els ) ) ,
2007-12-01 06:16:30 +01:00
XData = lists : filter ( fun ( E ) - > E / = none end , XData1 ) ,
case XData of
[ invalid ] - > invalid ;
[ ] - > none ;
[ XFields ] when is_list ( XFields ) - >
case lists : keysearch ( " FORM_TYPE " , 1 , XFields ) of
2008-12-09 01:32:36 +01:00
{ value , { _ , [ ? NS_PUBSUB_SUBSCRIBE_AUTH_s ] } } - >
2007-12-01 06:16:30 +01:00
XFields ;
_ - >
invalid
end
end .
2008-12-09 01:32:36 +01:00
%% @spec (Host, JID, Node, Subscription) -> void
%% Host = mod_pubsub:host()
%% JID = jlib:jid()
2009-04-10 15:21:37 +02:00
%% SNode = string()
2009-10-13 23:36:32 +02:00
%% Subscription = atom() | {atom(), mod_pubsub:subid()}
2008-12-09 01:32:36 +01:00
%% Plugins = [Plugin::string()]
%% @doc Send a message to JID with the supplied Subscription
2009-04-10 15:21:37 +02:00
send_authorization_approval ( Host , JID , SNode , Subscription ) - >
2009-08-25 21:54:44 +02:00
SubAttrs = case Subscription of
2010-11-29 20:44:31 +01:00
{ S , SID } - > [ ? XMLATTR ( < < " subscription " > > , subscription_to_string ( S ) ) ,
? XMLATTR ( < < " subid " > > , SID ) ] ;
S - > [ ? XMLATTR ( < < " subscription " > > , subscription_to_string ( S ) ) ]
2009-08-25 21:54:44 +02:00
end ,
2009-01-03 01:29:36 +01:00
Stanza = event_stanza (
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( JID ) ) | nodeAttr ( SNode ) ] ++ SubAttrs
2010-10-19 17:08:59 +02:00
} ] ) ,
2009-12-04 17:32:20 +01:00
ejabberd_router : route ( service_jid ( Host ) , JID , Stanza ) .
2010-10-19 17:08:59 +02:00
2007-12-01 06:16:30 +01:00
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
2008-12-09 01:32:36 +01:00
{ { value , { _ , [ SNode ] } } , { value , { _ , [ SSubscriber ] } } ,
{ value , { _ , [ SAllow ] } } } - >
2009-10-21 00:09:43 +02:00
Node = string_to_node ( SNode ) ,
2009-06-01 18:35:55 +02:00
Subscriber = exmpp_jid : parse ( SSubscriber ) ,
2007-12-01 06:16:30 +01:00
Allow = case SAllow of
" 1 " - > true ;
" true " - > true ;
_ - > false
end ,
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { type = Type , owners = Owners , idx = Nidx } ) - >
2008-12-05 16:13:09 +01:00
IsApprover = lists : member ( jlib : short_prepd_bare_jid ( From ) , Owners ) ,
2010-09-10 19:45:28 +02:00
{ result , Subscriptions } = node_call ( Type , get_subscriptions , [ Nidx , Subscriber ] ) ,
2007-12-01 06:16:30 +01:00
if
not IsApprover - >
2008-12-05 16:13:09 +01:00
{ error , 'forbidden' } ;
2007-12-01 06:16:30 +01:00
true - >
2010-09-10 19:45:28 +02:00
update_auth ( Host , SNode , Type , Nidx ,
2010-10-19 17:08:59 +02:00
Subscriber , Allow ,
Subscriptions )
2007-12-01 06:16:30 +01:00
end
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
{ error , Error } - >
2007-12-22 01:04:05 +01:00
ejabberd_router : route (
2010-10-19 17:08:59 +02:00
To , From ,
exmpp_stanza : reply_with_error ( Packet , Error ) ) ;
2009-05-01 01:17:38 +02:00
{ result , _ } - >
2007-12-01 06:16:30 +01:00
%% XXX: notify about subscription state change, section 12.11
2010-07-01 11:19:48 +02:00
ok
2007-12-01 06:16:30 +01:00
end ;
_ - >
ejabberd_router : route (
To , From ,
2008-12-05 16:13:09 +01:00
exmpp_stanza : reply_with_error ( Packet , 'not-acceptable' ) )
2007-12-01 06:16:30 +01:00
end .
2003-07-08 22:11:27 +02:00
2010-09-10 19:45:28 +02:00
update_auth ( Host , Node , Type , Nidx , Subscriber ,
2009-08-25 19:14:30 +02:00
Allow , Subscriptions ) - >
Subscription = lists : filter ( fun ( { pending , _ } ) - > true ;
2010-10-19 17:08:59 +02:00
( _ ) - > false
2009-08-25 19:14:30 +02:00
end , Subscriptions ) ,
case Subscription of
2010-09-10 19:45:28 +02:00
[ { pending , SubId } ] - > %% TODO does not work if several pending
2009-08-25 19:14:30 +02:00
NewSubscription = case Allow of
true - > subscribed ;
false - > none
end ,
node_call ( Type , set_subscriptions ,
2010-09-10 19:45:28 +02:00
[ Nidx , Subscriber , NewSubscription , SubId ] ) ,
2009-08-25 19:14:30 +02:00
send_authorization_approval ( Host , Subscriber , Node ,
NewSubscription ) ,
{ result , ok } ;
_ - >
{ error , exmpp_stanza : error ( ? NS_JABBER_CLIENT , 'unexpected-request' ) }
end .
2003-07-09 20:58:43 +02:00
- define ( XFIELD ( Type , Label , Var , Val ) ,
2010-11-29 20:44:31 +01:00
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs = [ ? XMLATTR ( < < " type " > > , Type ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , Label ) ) ,
? XMLATTR ( < < " var " > > , Var ) ] , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children = [ #xmlcdata { cdata = list_to_binary ( Val ) } ] } ] } ) .
2003-07-08 22:11:27 +02:00
2005-07-20 05:09:34 +02:00
- 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 ) ) .
2009-08-25 19:14:30 +02:00
- define ( STRINGMXFIELD ( Label , Var , Vals ) ,
#xmlel { ns = ? NS_DATA_FORMS ,
name = 'field' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " type " > > , < < " text-multi " > > ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , Label ) ) ,
? XMLATTR ( < < " var " > > , Var )
2010-10-19 17:08:59 +02:00
] ,
children = [ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' ,
children = [ ? XMLCDATA ( V ) ] } | | V < - Vals ] } ) .
2009-08-25 19:14:30 +02:00
2005-07-20 05:09:34 +02:00
- define ( XFIELDOPT ( Type , Label , Var , Val , Opts ) ,
2010-11-29 20:44:31 +01:00
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs = [ ? XMLATTR ( < < " type " > > , Type ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , Label ) ) ,
? XMLATTR ( < < " var " > > , Var ) ] , children =
2010-10-19 17:08:59 +02:00
lists : map ( fun ( Opt ) - >
#xmlel { ns = ? NS_DATA_FORMS , name = 'option' , children =
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children =
[ #xmlcdata { cdata = list_to_binary ( Opt ) } ] } ] }
end , Opts ) ++
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children = [ #xmlcdata { cdata = list_to_binary ( Val ) } ] } ] } ) .
2005-07-20 05:09:34 +02:00
- define ( LISTXFIELD ( Label , Var , Val , Opts ) ,
? XFIELDOPT ( " list-single " , Label , Var , Val , Opts ) ) .
2009-03-20 01:08:38 +01:00
- define ( LISTMXFIELD ( Label , Var , Vals , Opts ) ,
2010-11-29 20:44:31 +01:00
#xmlel { ns = ? NS_DATA_FORMS , name = 'field' , attrs = [ ? XMLATTR ( < < " type " > > , < < " list-multi " > > ) ,
? XMLATTR ( < < " label " > > , translate : translate ( Lang , Label ) ) ,
? XMLATTR ( < < " var " > > , Var ) ] , children =
2010-10-19 17:08:59 +02:00
lists : map ( fun ( Opt ) - >
#xmlel { ns = ? NS_DATA_FORMS , name = 'option' , children =
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children =
[ #xmlcdata { cdata = list_to_binary ( Opt ) } ] } ] }
end , Opts ) ++
lists : map ( fun ( Val ) - >
#xmlel { ns = ? NS_DATA_FORMS , name = 'value' , children =
[ #xmlcdata { cdata = list_to_binary ( Val ) } ] }
end , Vals )
} ) .
2009-03-20 01:08:38 +01:00
2007-12-01 06:16:30 +01:00
%% @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>
2008-04-01 12:11:39 +02:00
%%<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>
2010-09-10 19:45:28 +02:00
%%<li>The requested NodeId already exists.</li>
%%<li>The request did not include a NodeId and "instant nodes" are not supported.</li>
2007-12-01 06:16:30 +01:00
%%</ul>
%%<p>ote: node creation is a particular case, error return code is evaluated at many places:</p>
%%<ul>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
create_node ( Host , ServerHost , Node , Owner , Type ) - >
create_node ( Host , ServerHost , Node , Owner , Type , all , [ ] ) .
2009-10-21 00:09:43 +02:00
create_node ( Host , ServerHost , < < > > , Owner , Type , Access , Configuration ) - >
2007-12-01 06:16:30 +01:00
case lists : member ( " instant-nodes " , features ( Type ) ) of
true - >
2009-10-21 00:09:43 +02:00
NewNode = string_to_node ( randoms : get_string ( ) ) ,
2007-12-01 06:16:30 +01:00
case create_node ( Host , ServerHost ,
NewNode , Owner , Type , Access , Configuration ) of
{ result , [ ] } - >
{ result ,
2008-12-05 16:13:09 +01:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'create' , attrs = nodeAttr ( NewNode ) } ] } ] } ;
2009-10-21 00:09:43 +02:00
Error - >
2010-10-19 17:08:59 +02:00
Error
2007-12-01 06:16:30 +01:00
end ;
false - >
%% Service does not support instant nodes
2008-12-05 16:13:09 +01:00
{ error , extended_error ( 'not-acceptable' , " nodeid-required " ) }
2007-12-01 06:16:30 +01:00
end ;
create_node ( Host , ServerHost , Node , Owner , GivenType , Access , Configuration ) - >
2008-10-08 14:02:30 +02:00
Type = select_type ( ServerHost , Host , Node , GivenType ) ,
2007-12-01 06:16:30 +01:00
%% TODO, check/set node_type = Type
2008-12-05 16:13:09 +01:00
ParseOptions = case exmpp_xml : remove_cdata_from_list ( Configuration ) of
2007-12-01 06:16:30 +01:00
[ ] - >
{ result , node_options ( Type ) } ;
2008-12-05 16:13:09 +01:00
[ #xmlel { name = 'x' } = XEl ] - >
2007-12-01 06:16:30 +01:00
case jlib : parse_xdata_submit ( XEl ) of
invalid - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' } ;
2007-12-01 06:16:30 +01:00
XData - >
2009-09-23 18:32:06 +02:00
case set_xoption ( Host , XData , node_options ( Type ) ) of
2007-12-01 06:16:30 +01:00
NewOpts when is_list ( NewOpts ) - >
{ result , NewOpts } ;
Err - >
Err
end
end ;
_ - >
? INFO_MSG ( " Node ~p ; bad configuration: ~p " , [ Node , Configuration ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' }
2007-12-01 06:16:30 +01:00
end ,
case ParseOptions of
{ result , NodeOptions } - >
CreateNode =
fun ( ) - >
2009-10-21 00:09:43 +02:00
SNode = node_to_string ( Node ) ,
Parent = case node_call ( Type , node_to_path , [ Node ] ) of
2010-10-19 17:08:59 +02:00
{ result , [ SNode ] } - > < < > > ;
{ result , Path } - > element ( 2 , node_call ( Type , path_to_node , [ lists : sublist ( Path , length ( Path ) - 1 ) ] ) )
end ,
2009-10-21 00:09:43 +02:00
Parents = case Parent of
2010-10-19 17:08:59 +02:00
< < > > - > [ ] ;
_ - > [ Parent ]
end ,
2007-12-01 06:16:30 +01:00
case node_call ( Type , create_node_permission , [ Host , ServerHost , Node , Parent , Owner , Access ] ) of
{ result , true } - >
2009-10-21 00:09:43 +02:00
case tree_call ( Host , create_node , [ Host , Node , Type , Owner , NodeOptions , Parents ] ) of
2009-05-01 01:17:38 +02:00
{ ok , NodeId } - >
2010-05-28 13:27:28 +02:00
case node_call ( Type , create_node , [ NodeId , Owner ] ) of
2011-02-08 19:08:38 +01:00
{ result , Result } - > { result , { NodeId , Result } } ;
2010-05-28 13:27:28 +02:00
Error - > Error
end ;
2009-05-01 01:17:38 +02:00
{ error , { virtual , NodeId } } - >
2010-05-28 13:27:28 +02:00
case node_call ( Type , create_node , [ NodeId , Owner ] ) of
2010-06-02 14:38:04 +02:00
{ result , Result } - > { result , { NodeId , [ ] , Result } } ;
2010-05-28 13:27:28 +02:00
Error - > Error
end ;
2007-12-01 06:16:30 +01:00
Error - >
Error
end ;
2003-07-08 22:11:27 +02:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'forbidden' }
2003-07-08 22:11:27 +02:00
end
end ,
2009-08-25 19:14:30 +02:00
Reply = #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'create' , attrs = nodeAttr ( Node ) } ] } ,
2007-12-01 06:16:30 +01:00
case transaction ( CreateNode , transaction ) of
2011-02-08 19:08:38 +01:00
{ result , { NodeId , { Result , broadcast } } } - >
broadcast_created_node ( Host , Node , NodeId , Type , NodeOptions ) ,
2007-12-01 06:16:30 +01:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
2003-07-08 22:11:27 +02:00
end ;
2011-02-08 19:08:38 +01:00
{ result , { _ NodeId , default } } - >
2007-12-01 06:16:30 +01:00
{ result , Reply } ;
2011-02-08 19:08:38 +01:00
{ result , { _ NodeId , Result } } - >
2009-05-01 01:17:38 +02:00
{ result , Result } ;
Error - >
%% in case we change transaction to sync_dirty...
2009-08-25 21:54:44 +02:00
%% node_call(Type, delete_node, [Host, Node]),
%% tree_call(Host, delete_node, [Host, Node]),
2009-05-01 01:17:38 +02:00
Error
2007-12-01 06:16:30 +01:00
end ;
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2007-12-01 06:16:30 +01:00
%% @spec (Host, Node, Owner) ->
%% {error, Reason} | {result, []}
%% Host = host()
%% Node = pubsubNode()
%% Owner = jid()
%% Reason = stanzaError()
%% @doc <p>Delete specified node and all childs.</p>
%%<p>There are several reasons why the node deletion request might fail:</p>
%%<ul>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
2009-11-05 18:36:54 +01:00
delete_node ( _ Host , < < > > , _ Owner ) - >
2007-12-01 06:16:30 +01:00
%% Node is the root
2008-12-05 16:13:09 +01:00
{ error , 'not-allowed' } ;
2007-12-01 06:16:30 +01:00
delete_node ( Host , Node , Owner ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
case node_call ( Type , get_affiliation , [ Nidx , Owner ] ) of
{ result , owner } - >
Removed = tree_call ( Host , delete_node , [ Host , Node ] ) ,
case node_call ( Type , delete_node , [ Removed ] ) of
2011-02-08 19:08:38 +01:00
{ result , Res } - > { result , Res } ;
2010-10-19 17:08:59 +02:00
Error - > Error
end ;
_ - >
%% Entity is not an owner
{ error , 'forbidden' }
end
2007-12-01 06:16:30 +01:00
end ,
Reply = [ ] ,
case transaction ( Host , Node , Action , transaction ) of
2011-02-08 19:08:38 +01:00
{ result , { _ , { Result , broadcast , Removed } } } - >
2009-08-25 21:54:44 +02:00
lists : foreach ( fun ( { RNode , _ RSubscriptions } ) - >
2010-10-19 17:08:59 +02:00
{ RH , RN } = RNode #pubsub_node.id ,
Nidx = RNode #pubsub_node.idx ,
Type = RNode #pubsub_node.type ,
Options = RNode #pubsub_node.options ,
2011-02-08 19:08:38 +01:00
broadcast_removed_node ( RH , RN , Nidx , Type , Options ) ,
2010-10-19 17:08:59 +02:00
unset_cached_item ( RH , Nidx )
end , Removed ) ,
2007-12-01 06:16:30 +01:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
end ;
2011-02-08 19:08:38 +01:00
{ result , { _ , { Result , _ Removed } } } - >
2007-12-01 06:16:30 +01:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
end ;
2011-02-08 19:08:38 +01:00
{ result , { _ , default } } - >
2007-12-01 06:16:30 +01:00
{ result , Reply } ;
2011-02-08 19:08:38 +01:00
{ result , { _ , Result } } - >
2009-05-01 01:17:38 +02:00
{ result , Result } ;
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2009-08-25 19:14:30 +02:00
%% @spec (Host, Node, From, JID, Configuration) ->
2007-12-01 06:16:30 +01:00
%% {error, Reason::stanzaError()} |
%% {result, []}
%% Host = host()
%% Node = pubsubNode()
%% From = jid()
%% JID = jid()
2010-05-17 22:09:01 +02:00
%% @see node_flat:subscribe_node/5
2008-04-01 12:11:39 +02:00
%% @doc <p>Accepts or rejects subcription requests on a PubSub node.</p>
2007-12-01 06:16:30 +01:00
%%<p>There are several reasons why the subscription request might fail:</p>
%%<ul>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
2009-08-25 19:14:30 +02:00
subscribe_node ( Host , Node , From , JID , Configuration ) - >
2009-10-12 12:23:51 +02:00
SubOpts = case pubsub_subscription : parse_options_xform ( Configuration ) of
2010-10-19 17:08:59 +02:00
{ result , GoodSubOpts } - > GoodSubOpts ;
_ - > invalid
end ,
2008-12-05 16:13:09 +01:00
Subscriber = try
2010-10-19 17:08:59 +02:00
jlib : short_prepd_jid ( exmpp_jid : parse ( JID ) )
catch
_ : _ - >
{ undefined , undefined , undefined }
end ,
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { options = Options , owners = Owners , type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
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 - >
%% Node does not support subscriptions
{ error , extended_error ( 'feature-not-implemented' , unsupported , " subscribe " ) } ;
not SubscribeConfig - >
%% Node does not support subscriptions
{ error , extended_error ( 'feature-not-implemented' , unsupported , " subscribe " ) } ;
HasOptions andalso not OptionsFeature - >
%% Node does not support subscription options
{ error , extended_error ( 'feature-not-implemented' , unsupported , " subscription-options " ) } ;
SubOpts == invalid - >
%% Passed invalit options submit form
{ error , extended_error ( 'bad-request' , " invalid-options " ) } ;
true - >
node_call ( Type , subscribe_node ,
[ Nidx , From , Subscriber ,
2007-12-01 06:16:30 +01:00
AccessModel , SendLast ,
2009-08-25 19:14:30 +02:00
PresenceSubscription , RosterGroup ,
SubOpts ] )
2010-10-19 17:08:59 +02:00
end
end ,
2007-12-01 06:16:30 +01:00
Reply = fun ( Subscription ) - >
%% TODO, this is subscription-notification, should depends on node features
2009-08-25 21:54:44 +02:00
SubAttrs = case Subscription of
{ subscribed , SubId } - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " subscription " > > , subscription_to_string ( subscribed ) ) ,
2010-12-07 13:53:58 +01:00
? XMLATTR ( < < " subid " > > , SubId ) ,
? XMLATTR ( < < " node " > > , Node ) ] ;
2009-08-25 21:54:44 +02:00
Other - >
2010-12-07 13:53:58 +01:00
[ ? XMLATTR ( < < " subscription " > > , subscription_to_string ( Other ) ) ,
? XMLATTR ( < < " node " > > , Node ) ]
2009-08-25 21:54:44 +02:00
end ,
2007-12-01 06:16:30 +01:00
Fields =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , JID ) | SubAttrs ] ,
2009-08-25 19:14:30 +02:00
#xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' , attrs = Fields } ] }
2007-12-01 06:16:30 +01:00
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
2009-08-25 21:54:44 +02:00
{ result , { TNode , { Result , subscribed , SubId , send_last } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
2010-09-10 19:45:28 +02:00
send_items ( Host , Node , Nidx , Type , Subscriber , last ) ,
2010-05-12 12:01:54 +02:00
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " subscribed " ) ,
2007-12-01 06:16:30 +01:00
case Result of
2009-08-25 21:54:44 +02:00
default - > { result , Reply ( { subscribed , SubId } ) } ;
2007-12-01 06:16:30 +01:00
_ - > { result , Result }
end ;
2010-05-12 12:01:54 +02:00
{ result , { TNode , { default , subscribed , SubId } } } - >
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " subscribed " ) ,
2009-08-25 21:54:44 +02:00
{ result , Reply ( { subscribed , SubId } ) } ;
2010-05-12 12:01:54 +02:00
{ result , { TNode , { Result , subscribed , _ SubId } } } - >
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " subscribed " ) ,
2009-08-25 21:54:44 +02:00
{ result , Result } ;
{ result , { TNode , { default , pending , _ SubId } } } - >
send_authorization_request ( TNode , Subscriber ) ,
2010-05-12 12:01:54 +02:00
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " pending " ) ,
2009-08-25 21:54:44 +02:00
{ result , Reply ( pending ) } ;
{ result , { TNode , { Result , pending } } } - >
send_authorization_request ( TNode , Subscriber ) ,
2010-05-12 12:01:54 +02:00
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " pending " ) ,
2009-08-25 21:54:44 +02:00
{ result , Result } ;
2009-05-01 01:17:38 +02:00
{ result , { _ , Result } } - >
2007-12-01 06:16:30 +01:00
%% this case should never occure anyway
2009-05-01 01:17:38 +02:00
{ result , Result } ;
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2008-04-01 12:11:39 +02:00
%% @spec (Host, Noce, From, JID, SubId) -> {error, Reason} | {result, []}
2007-12-01 06:16:30 +01:00
%% 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>
2008-04-01 12:11:39 +02:00
%%<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>
2009-01-08 18:27:47 +01:00
unsubscribe_node ( Host , Node , From , JID , SubId ) when is_list ( JID ) - >
2009-06-01 18:35:55 +02:00
Subscriber = try jlib : short_prepd_jid ( exmpp_jid : parse ( JID ) )
2010-10-19 17:08:59 +02:00
catch
_ : _ - >
{ undefined , undefined , undefined }
end ,
2009-01-08 18:27:47 +01:00
unsubscribe_node ( Host , Node , From , Subscriber , SubId ) ;
unsubscribe_node ( Host , Node , From , Subscriber , SubId ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
node_call ( Type , unsubscribe_node , [ Nidx , From , Subscriber , SubId ] )
end ,
2009-05-01 01:17:38 +02:00
case transaction ( Host , Node , Action , sync_dirty ) of
2010-05-12 12:01:54 +02:00
{ result , { TNode , default } } - >
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " none " ) ,
2007-12-01 06:16:30 +01:00
{ result , [ ] } ;
2010-05-12 12:01:54 +02:00
{ result , { TNode , Result } } - >
notify_owners ( get_option ( TNode #pubsub_node.options , notify_sub ) , Subscriber , Host , Node , TNode #pubsub_node.owners , " none " ) ,
2009-05-01 01:17:38 +02:00
{ result , Result } ;
Error - >
Error
2007-12-01 06:16:30 +01:00
end .
2003-07-08 22:11:27 +02:00
2007-12-01 06:16:30 +01:00
%% @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>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
2011-02-21 12:08:58 +01:00
publish_item ( Host , ServerHost , Node , Publisher , < < > > , Payload ) - >
2007-12-01 06:16:30 +01:00
%% if publisher does not specify an ItemId, the service MUST generate the ItemId
publish_item ( Host , ServerHost , Node , Publisher , uniqid ( ) , Payload ) ;
publish_item ( Host , ServerHost , Node , Publisher , ItemId , Payload ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { options = Options , type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
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 ) ,
2011-05-31 16:09:27 +02:00
MaxItems = case PersistItems of
2011-06-01 23:29:45 +02:00
false - > 0 ;
true - > max_items ( Host , Options )
2011-05-31 16:09:27 +02:00
end ,
2010-10-19 17:08:59 +02:00
{ PayloadCount , PayloadNS } = payload_els_ns ( Payload ) ,
2011-02-14 16:48:54 +01:00
PayloadSize = size ( term_to_binary ( Payload ) ) - 2 , % size(term_to_binary([])) == 2
2010-10-19 17:08:59 +02:00
PayloadMaxSize = get_option ( Options , max_payload_size ) ,
InvalidNS = case get_option ( Options , type ) of
false - > false ;
[ [ ] ] - > false ;
[ ConfiguredNS ] - > ConfiguredNS =/= PayloadNS
end ,
% pubsub#deliver_payloads true
% pubsub#persist_items true -> 1 item; false -> 0 item
if
not PublishFeature - >
%% Node does not support item publication
{ error , extended_error ( 'feature-not-implemented' , unsupported , " publish " ) } ;
PayloadSize > PayloadMaxSize - >
%% Entity attempts to publish very large payload
{ error , extended_error ( 'not-acceptable' , " payload-too-big " ) } ;
( PayloadCount == 0 ) and ( Payload == [ ] ) - >
%% Publisher attempts to publish to payload node with no payload
{ error , extended_error ( 'bad-request' , " payload-required " ) } ;
( PayloadCount > 1 ) or ( PayloadCount == 0 ) or InvalidNS - >
%% Entity attempts to publish item with multiple payload elements
%% or with wrong payload NS
{ error , extended_error ( 'bad-request' , " invalid-payload " ) } ;
( DeliverPayloads == 0 ) and ( PersistItems == 0 ) and ( PayloadSize > 0 ) - >
%% Publisher attempts to publish to transient notification node with item
{ error , extended_error ( 'bad-request' , " item-forbidden " ) } ;
( ( DeliverPayloads == 1 ) or ( PersistItems == 1 ) ) and ( PayloadSize == 0 ) - >
%% Publisher attempts to publish to persistent node with no item
{ error , extended_error ( 'bad-request' , " item-required " ) } ;
true - >
node_call ( Type , publish_item , [ Nidx , Publisher , PublishModel , MaxItems , ItemId , Payload ] )
end
end ,
2010-12-24 16:21:28 +01:00
%%ServerHostS = binary_to_list(ServerHost),
ejabberd_hooks : run ( pubsub_publish_item , ServerHost , [ ServerHost , Node , Publisher , service_jid ( Host ) , ItemId , Payload ] ) ,
2009-08-25 19:14:30 +02:00
Reply = #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'publish' , attrs = nodeAttr ( Node ) , children =
[ #xmlel { ns = ? NS_PUBSUB , name = 'item' , attrs = itemAttr ( ItemId ) } ] } ] } ,
2007-12-01 06:16:30 +01:00
case transaction ( Host , Node , Action , sync_dirty ) of
2009-10-13 18:42:13 +02:00
{ result , { TNode , { Result , Broadcast , Removed } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2011-08-31 16:42:44 +02:00
case get_option ( Options , deliver_notifications ) of
true - >
BrPayload = case Broadcast of
broadcast - > Payload ;
PluginPayload - > PluginPayload
end ,
broadcast_publish_item ( Host , Node , Nidx , Type , Options , ItemId ,
jlib : short_prepd_jid ( Publisher ) , BrPayload , Removed ) ;
false - >
ok
end ,
2010-09-10 19:45:28 +02:00
set_cached_item ( Host , Nidx , ItemId , Publisher , Payload ) ,
2009-05-01 01:17:38 +02:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
end ;
{ result , { TNode , { default , Removed } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2010-09-10 19:45:28 +02:00
broadcast_retract_items ( Host , Node , Nidx , Type , Options , Removed ) ,
set_cached_item ( Host , Nidx , ItemId , Publisher , Payload ) ,
2009-05-01 01:17:38 +02:00
{ result , Reply } ;
{ result , { TNode , { Result , Removed } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2010-09-10 19:45:28 +02:00
broadcast_retract_items ( Host , Node , Nidx , Type , Options , Removed ) ,
set_cached_item ( Host , Nidx , ItemId , Publisher , Payload ) ,
2009-05-01 01:17:38 +02:00
{ result , Result } ;
{ result , { _ , default } } - >
{ result , Reply } ;
{ result , { _ , Result } } - >
{ result , Result } ;
2008-12-05 16:13:09 +01:00
{ error , 'item-not-found' } - >
2007-12-01 06:16:30 +01:00
%% handles auto-create feature
%% for automatic node creation. we'll take the default node type:
2007-12-10 15:09:31 +01:00
%% first listed into the plugins configuration option, or pep
2008-10-08 14:02:30 +02:00
Type = select_type ( ServerHost , Host , Node ) ,
2007-12-01 06:16:30 +01:00
case lists : member ( " auto-create " , features ( Type ) ) of
true - >
case create_node ( Host , ServerHost , Node , Publisher , Type ) of
2011-09-21 14:40:47 +02:00
{ result , Reply2 } - >
NewNode = exmpp_xml : get_path ( Reply2 , [ { element , 'create' } ,
{ attribute , < < " node " > > } ] ) ,
publish_item ( Host , ServerHost , NewNode , Publisher , ItemId ,
Payload ) ;
2007-12-01 06:16:30 +01:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'item-not-found' }
2007-12-01 06:16:30 +01:00
end ;
false - >
2008-12-05 16:13:09 +01:00
{ error , 'item-not-found' }
2003-07-08 22:11:27 +02:00
end ;
2009-05-01 01:17:38 +02:00
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2007-12-01 06:16:30 +01:00
%% @spec (Host::host(), JID::jid(), Node::pubsubNode(), ItemId::string()) ->
%% {error, Reason::stanzaError()} |
%% {result, []}
%% @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>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
delete_item ( Host , Node , Publisher , ItemId ) - >
delete_item ( Host , Node , Publisher , ItemId , false ) .
delete_item ( _ , " " , _ , _ , _ ) - >
%% Request does not specify a node
2008-12-05 16:13:09 +01:00
{ error , extended_error ( 'bad-request' , " node-required " ) } ;
2007-12-01 06:16:30 +01:00
delete_item ( Host , Node , Publisher , ItemId , ForceNotify ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { options = Options , type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
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('bad-request', "item-required")};
not PersistentFeature - >
%% Node does not support persistent items
{ error , extended_error ( 'feature-not-implemented' , unsupported , " persistent-items " ) } ;
not DeleteFeature - >
%% Service does not support item deletion
{ error , extended_error ( 'feature-not-implemented' , unsupported , " delete-items " ) } ;
true - >
node_call ( Type , delete_item , [ Nidx , Publisher , PublishModel , ItemId ] )
end
2007-12-01 06:16:30 +01:00
end ,
Reply = [ ] ,
case transaction ( Host , Node , Action , sync_dirty ) of
2009-05-01 01:17:38 +02:00
{ result , { TNode , { Result , broadcast } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2010-09-10 19:45:28 +02:00
broadcast_retract_items ( Host , Node , Nidx , Type , Options , [ ItemId ] , ForceNotify ) ,
case get_cached_item ( Host , Nidx ) of
2010-10-19 17:08:59 +02:00
#pubsub_item { id = { ItemId , Nidx } } - > unset_cached_item ( Host , Nidx ) ;
_ - > ok
2009-05-29 01:21:50 +02:00
end ,
2007-12-01 06:16:30 +01:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
2003-07-08 22:11:27 +02:00
end ;
2009-05-01 01:17:38 +02:00
{ result , { _ , default } } - >
2007-12-01 06:16:30 +01:00
{ result , Reply } ;
2009-05-01 01:17:38 +02:00
{ result , { _ , Result } } - >
{ result , Result } ;
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2007-12-01 06:16:30 +01:00
%% @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>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
purge_node ( Host , Node , Owner ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { options = Options , type = Type , idx = Nidx } ) - >
2007-12-01 06:16:30 +01:00
Features = features ( Type ) ,
PurgeFeature = lists : member ( " purge-nodes " , Features ) ,
PersistentFeature = lists : member ( " persistent-items " , Features ) ,
PersistentConfig = get_option ( Options , persist_items ) ,
if
not PurgeFeature - >
%% Service does not support node purging
2008-12-05 16:13:09 +01:00
{ error , extended_error ( 'feature-not-implemented' , unsupported , " purge-nodes " ) } ;
2007-12-01 06:16:30 +01:00
not PersistentFeature - >
%% Node does not support persistent items
2008-12-05 16:13:09 +01:00
{ error , extended_error ( 'feature-not-implemented' , unsupported , " persistent-items " ) } ;
2007-12-01 06:16:30 +01:00
not PersistentConfig - >
%% Node is not configured for persistent items
2008-12-05 16:13:09 +01:00
{ error , extended_error ( 'feature-not-implemented' , unsupported , " persistent-items " ) } ;
2007-12-01 06:16:30 +01:00
true - >
2010-09-10 19:45:28 +02:00
node_call ( Type , purge_node , [ Nidx , Owner ] )
2007-12-01 06:16:30 +01:00
end
end ,
Reply = [ ] ,
case transaction ( Host , Node , Action , sync_dirty ) of
2009-05-01 01:17:38 +02:00
{ result , { TNode , { Result , broadcast } } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2010-09-10 19:45:28 +02:00
broadcast_purge_node ( Host , Node , Nidx , Type , Options ) ,
unset_cached_item ( Host , Nidx ) ,
2007-12-01 06:16:30 +01:00
case Result of
default - > { result , Reply } ;
_ - > { result , Result }
end ;
2009-05-01 01:17:38 +02:00
{ result , { _ , default } } - >
2007-12-01 06:16:30 +01:00
{ result , Reply } ;
2009-05-01 01:17:38 +02:00
{ result , { _ , Result } } - >
{ result , Result } ;
Error - >
Error
2007-12-01 06:16:30 +01:00
end .
2003-07-08 22:11:27 +02:00
2007-12-01 06:16:30 +01:00
%% @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.
2010-09-10 19:45:28 +02:00
get_items ( Host , Node , From , SubId , SMaxItems , ItemIds ) - >
2003-07-08 22:11:27 +02:00
MaxItems =
if
2009-09-23 18:32:06 +02:00
SMaxItems == " " - > get_max_items_node ( Host ) ;
2003-07-08 22:11:27 +02:00
true - >
case catch list_to_integer ( SMaxItems ) of
2008-12-05 16:13:09 +01:00
{ 'EXIT' , _ } - > { error , 'bad-request' } ;
2007-12-01 06:16:30 +01:00
Val - > Val
2003-07-08 22:11:27 +02:00
end
end ,
case MaxItems of
2007-12-01 06:16:30 +01:00
{ error , Error } - >
{ error , Error } ;
2003-07-08 22:11:27 +02:00
_ - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { options = Options , type = Type , idx = Nidx , owners = Owners } ) - >
2010-10-19 17:08:59 +02:00
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 - >
%% Item Retrieval Not Supported
{ error , extended_error ( 'feature-not-implemented' , unsupported , " retrieve-items " ) } ;
not PersistentFeature - >
%% Persistent Items Not Supported
{ error , extended_error ( 'feature-not-implemented' , unsupported , " persistent-items " ) } ;
true - >
node_call ( Type , get_items ,
[ Nidx , From ,
AccessModel , PresenceSubscription , RosterGroup ,
SubId ] )
end
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
2009-05-01 01:17:38 +02:00
{ result , { _ , Items } } - >
2010-09-10 19:45:28 +02:00
SendItems = case ItemIds of
2010-10-19 17:08:59 +02:00
[ ] - >
Items ;
_ - >
lists : filter ( fun ( #pubsub_item { id = { ItemId , _ } } ) - >
lists : member ( ItemId , ItemIds )
end , Items )
end ,
2007-12-01 06:16:30 +01:00
%% Generate the XML response (Item list), limiting the
%% number of items sent to MaxItems:
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'items' , attrs = nodeAttr ( Node ) , children =
itemsEls ( lists : sublist ( SendItems , MaxItems ) ) } ] } } ;
2009-05-01 01:17:38 +02:00
Error - >
Error
2003-07-08 22:11:27 +02:00
end
end .
2010-09-10 19:45:28 +02:00
2010-10-19 17:08:59 +02:00
%% TODO : fix
- spec ( get_items / 2 : :
(
Host : : host ( ) ,
NodeId : : nodeId ( ) )
- > Items : : [ ] | [ Item : : pubsubItem ( ) ]
) .
get_items ( Host , NodeId ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
node_call ( Type , get_items , [ Nidx , service_jid ( Host ) ] )
2010-10-19 17:08:59 +02:00
end ,
case transaction ( Host , NodeId , Action , sync_dirty ) of
{ result , { _ , Items } } - > Items
%Error -> Error
2009-05-29 01:21:50 +02:00
end .
2010-09-10 19:45:28 +02:00
2010-10-19 17:08:59 +02:00
%% TODO : fix
- spec ( get_item / 3 : :
(
Host : : host ( ) ,
NodeId : : nodeId ( ) ,
ItemId : : itemId ( ) )
- > { 'result' , Item : : pubsubItem ( ) } | { 'error' , 'item-not-found' }
) .
get_item ( Host , NodeId , ItemId ) - >
2011-05-30 14:04:42 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
node_call ( Type , get_item , [ Nidx , ItemId ] )
2010-10-19 17:08:59 +02:00
end ,
case transaction ( Host , NodeId , Action , sync_dirty ) of
2009-05-29 01:21:50 +02:00
{ result , { _ , Items } } - > Items ;
Error - > Error
end .
2003-07-08 22:11:27 +02:00
2009-05-01 01:17:38 +02:00
%% @spec (Host, Node, NodeId, Type, LJID, Number) -> any()
2009-08-25 21:54:44 +02:00
%% Host = pubsubHost()
2009-05-07 02:54:44 +02:00
%% Node = pubsubNode()
2009-05-01 01:17:38 +02:00
%% NodeId = pubsubNodeId()
%% Type = pubsubNodeType()
2008-12-20 01:01:26 +01:00
%% LJID = {U, S, []}
2009-01-19 12:59:40 +01:00
%% Number = last | integer()
2008-12-20 01:01:26 +01:00
%% @doc <p>Resend the items of a node to the user.</p>
%% @todo use cache-last-item feature
2011-05-31 16:08:25 +02:00
send_items ( Host , Node , Nidx , Type , { U , S , R } = LJID , 'last' ) - >
2011-05-30 14:04:42 +02:00
case get_cached_item ( Host , Nidx ) of
2009-05-29 01:21:50 +02:00
undefined - >
2011-05-30 14:04:42 +02:00
send_items ( Host , Node , Nidx , Type , LJID , 1 ) ;
2009-05-29 01:21:50 +02:00
LastItem - >
2010-05-28 11:32:51 +02:00
{ ModifNow , ModifUSR } = LastItem #pubsub_item.modification ,
2009-09-23 18:32:06 +02:00
Stanza = event_stanza_with_delay (
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'items' , attrs = nodeAttr ( Node ) ,
children = itemsEls ( [ LastItem ] ) } ] , ModifNow , ModifUSR ) ,
2011-05-31 16:08:25 +02:00
case is_tuple ( Host ) of
false - >
ejabberd_router : route ( service_jid ( Host ) , exmpp_jid : make ( U , S , R ) , Stanza ) ;
true - >
case ejabberd_sm : get_session_pid ( U , S , R ) of
C2SPid when is_pid ( C2SPid ) - >
ejabberd_c2s : broadcast ( C2SPid ,
{ pep_message , < < Node / binary , < < " +notify " > > / binary > > } ,
_ Sender = service_jid ( Host ) ,
Stanza ) ;
_ - >
ok
end
end
2009-05-29 01:21:50 +02:00
end ;
2011-05-31 16:08:25 +02:00
send_items ( Host , Node , Nidx , Type , { U , S , R } = LJID , Number ) - >
2011-05-30 14:04:42 +02:00
ToSend = case node_action ( Host , Type , get_items , [ Nidx , LJID ] ) of
2010-10-19 17:08:59 +02:00
{ result , [ ] } - >
[ ] ;
{ result , Items } - >
case Number of
N when N > 0 - > lists : sublist ( Items , N ) ;
_ - > Items
end ;
_ - >
[ ]
end ,
2009-09-23 18:32:06 +02:00
Stanza = case ToSend of
2010-10-19 17:08:59 +02:00
[ LastItem ] - >
{ ModifNow , ModifUSR } = LastItem #pubsub_item.modification ,
event_stanza_with_delay (
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'items' , attrs = nodeAttr ( Node ) , children =
itemsEls ( ToSend ) } ] , ModifNow , ModifUSR ) ;
_ - >
event_stanza (
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'items' , attrs = nodeAttr ( Node ) , children =
itemsEls ( ToSend ) } ] )
end ,
2011-05-31 16:08:25 +02:00
case is_tuple ( Host ) of
false - >
ejabberd_router : route ( service_jid ( Host ) , exmpp_jid : make ( U , S , R ) , Stanza ) ;
true - >
case ejabberd_sm : get_session_pid ( U , S , R ) of
C2SPid when is_pid ( C2SPid ) - >
ejabberd_c2s : broadcast ( C2SPid ,
{ pep_message , < < Node / binary , < < " +notify " > > / binary > > } ,
_ Sender = service_jid ( Host ) ,
Stanza ) ;
_ - >
ok
end
end .
2007-12-01 06:16:30 +01:00
%% @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>
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 - >
%% Service does not support retreive affiliatons
2008-12-05 16:13:09 +01:00
{ { error , extended_error ( 'feature-not-implemented' , unsupported , " retrieve-affiliations " ) } , Acc } ;
2007-12-01 06:16:30 +01:00
true - >
2009-05-01 01:17:38 +02:00
{ result , Affiliations } = node_action ( Host , Type , get_entity_affiliations , [ Host , JID ] ) ,
2007-12-01 06:16:30 +01:00
{ Status , [ Affiliations | Acc ] }
end
end , { ok , [ ] } , Plugins ) ,
case Result of
{ ok , Affiliations } - >
Entities = lists : flatmap (
fun ( { _ , none } ) - > [ ] ;
2010-09-10 19:45:28 +02:00
( { #pubsub_node { id = { _ , Node } } , Affiliation } ) - >
2008-12-05 16:13:09 +01:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'affiliation' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " node " > > , node_to_string ( Node ) ) ,
? XMLATTR ( < < " affiliation " > > , affiliation_to_string ( Affiliation ) ) ] } ]
2007-12-11 17:19:17 +01:00
end , lists : usort ( lists : flatten ( Affiliations ) ) ) ,
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'affiliations' , children =
Entities } ] } } ;
2007-12-01 06:16:30 +01:00
{ Error , _ } - >
Error
end ;
get_affiliations ( Host , Node , JID ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
Features = features ( Type ) ,
RetrieveFeature = lists : member ( " modify-affiliations " , Features ) ,
{ result , Affiliation } = node_call ( Type , get_affiliation , [ Nidx , JID ] ) ,
if
not RetrieveFeature - >
%% Service does not support modify affiliations
{ error , extended_error ( 'feature-not-implemented' , unsupported , " modify-affiliations " ) } ;
Affiliation / = owner - >
%% Entity is not an owner
{ error , 'forbidden' } ;
true - >
node_call ( Type , get_node_affiliations , [ Nidx ] )
end
end ,
2007-12-01 06:16:30 +01:00
case transaction ( Host , Node , Action , sync_dirty ) of
2009-05-01 01:17:38 +02:00
{ result , { _ , Affiliations } } - >
2007-12-01 06:16:30 +01:00
Entities = lists : flatmap (
fun ( { _ , none } ) - > [ ] ;
2008-12-05 16:13:09 +01:00
( { { AU , AS , AR } , Affiliation } ) - >
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'affiliation' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( AU , AS , AR ) ) ,
? XMLATTR ( < < " affiliation " > > , affiliation_to_string ( Affiliation ) ) ] } ]
2007-12-01 06:16:30 +01:00
end , Affiliations ) ,
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'affiliations' , attrs = nodeAttr ( Node ) , children =
Entities } ] } } ;
2009-05-01 01:17:38 +02:00
Error - >
Error
2003-07-08 22:11:27 +02:00
end .
2007-12-01 06:16:30 +01:00
set_affiliations ( Host , Node , From , EntitiesEls ) - >
2008-12-05 16:13:09 +01:00
Owner = jlib : short_prepd_bare_jid ( From ) ,
2003-07-08 22:11:27 +02:00
Entities =
lists : foldl (
fun ( El , Acc ) - >
case Acc of
error - >
error ;
_ - >
case El of
2008-12-05 16:13:09 +01:00
#xmlel { name = 'affiliation' , attrs = Attrs } - >
JID = try
2010-10-19 17:08:59 +02:00
exmpp_jid : parse (
2010-11-29 20:44:31 +01:00
exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , " " ) )
2010-10-19 17:08:59 +02:00
catch
_ : _ - > error
end ,
2007-12-01 06:16:30 +01:00
Affiliation = string_to_affiliation (
2010-11-29 20:44:31 +01:00
exmpp_xml : get_attribute_from_list_as_list ( Attrs , < < " affiliation " > > , " " ) ) ,
2003-07-08 22:11:27 +02:00
if
( JID == error ) or
2007-12-01 06:16:30 +01:00
( Affiliation == false ) - >
2003-07-08 22:11:27 +02:00
error ;
true - >
2009-08-25 19:42:25 +02:00
[ { JID , Affiliation } | Acc ]
2003-07-08 22:11:27 +02:00
end
end
end
end , [ ] , EntitiesEls ) ,
case Entities of
error - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' } ;
2003-07-08 22:11:27 +02:00
_ - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { owners = Owners , type = Type , idx = Nidx } = N ) - >
2010-10-19 17:08:59 +02:00
case lists : member ( Owner , Owners ) of
true - >
OwnerJID = exmpp_jid : make ( Owner ) ,
FilteredEntities = case Owners of
[ Owner ] - > [ E | | E < - Entities , element ( 1 , E ) =/= OwnerJID ] ;
_ - > Entities
end ,
lists : foreach (
fun ( { JID , Affiliation } ) - >
{ result , _ } = node_call ( Type , set_affiliation , [ Nidx , JID , Affiliation ] ) ,
case Affiliation of
owner - >
NewOwner = jlib : short_prepd_bare_jid ( JID ) ,
NewOwners = [ NewOwner | Owners ] ,
tree_call ( Host , set_node , [ N #pubsub_node { owners = NewOwners } ] ) ;
none - >
OldOwner = jlib : short_prepd_bare_jid ( 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 , 'forbidden' }
end
2007-12-01 06:16:30 +01:00
end ,
2009-05-01 01:17:38 +02:00
case transaction ( Host , Node , Action , sync_dirty ) of
{ result , { _ , Result } } - > { result , Result } ;
Other - > Other
end
2003-07-08 22:11:27 +02:00
end .
2010-09-10 19:45:28 +02:00
get_options ( Host , Node , JID , SubId , Lang ) - >
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2009-08-25 19:14:30 +02:00
case lists : member ( " subscription-options " , features ( Type ) ) of
true - >
2010-09-10 19:45:28 +02:00
get_options_helper ( JID , Lang , Node , Nidx , SubId , Type ) ;
2009-08-25 19:14:30 +02:00
false - >
2010-10-19 17:08:59 +02:00
{ error , extended_error (
'feature-not-implemented' ,
unsupported , " subscription-options " ) }
2009-08-25 19:14:30 +02:00
end
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
2010-04-08 13:58:47 +02:00
{ result , { _ Node , XForm } } - > { result , XForm } ;
2009-08-25 19:14:30 +02:00
Error - > Error
end .
2011-05-30 14:04:42 +02:00
get_options_helper ( JID , Lang , Node , Nidx , SubId , Type ) - >
2009-08-25 19:14:30 +02:00
Subscriber = try exmpp_jid : parse ( JID ) of
2010-10-19 17:08:59 +02:00
J - > jlib : short_jid ( J )
catch
_ - >
2010-04-29 12:54:35 +02:00
exmpp_jid : make ( " " , " " , " " ) %% TODO, check if use <<>> instead of ""
2009-08-25 19:14:30 +02:00
end ,
{ result , Subs } = node_call ( Type , get_subscriptions ,
2011-05-30 14:04:42 +02:00
[ Nidx , Subscriber ] ) ,
2010-09-10 19:45:28 +02:00
SubIds = lists : foldl ( fun ( { subscribed , SID } , Acc ) - >
2009-08-25 19:14:30 +02:00
[ SID | Acc ] ;
2010-10-19 17:08:59 +02:00
( _ , Acc ) - >
2009-08-25 19:14:30 +02:00
Acc
end , [ ] , Subs ) ,
2010-09-10 19:45:28 +02:00
case { SubId , SubIds } of
2009-08-25 19:14:30 +02:00
{ _ , [ ] } - >
{ error , extended_error ( 'not-acceptable' , " not-subscribed " ) } ;
{ [ ] , [ SID ] } - >
2011-05-30 14:04:42 +02:00
read_sub ( Subscriber , Node , Nidx , SID , Lang ) ;
2009-08-25 19:14:30 +02:00
{ [ ] , _ } - >
{ error , extended_error ( 'not-acceptable' , " subid-required " ) } ;
{ _ , _ } - >
2011-05-30 14:04:42 +02:00
read_sub ( Subscriber , Node , Nidx , SubId , Lang )
2009-08-25 19:14:30 +02:00
end .
2011-05-30 14:04:42 +02:00
read_sub ( Subscriber , Node , Nidx , SubId , Lang ) - >
case pubsub_subscription : get_subscription ( Subscriber , Nidx , SubId ) of
2009-08-25 19:14:30 +02:00
{ result , #pubsub_subscription { options = Options } } - >
2009-09-01 10:19:14 +02:00
{ result , XdataEl } = pubsub_subscription : get_options_xform ( Lang , Options ) ,
OptionsEl = #xmlel { ns = ? NS_PUBSUB , name = 'options' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( Subscriber ) ) ,
? XMLATTR ( < < " subid " > > , SubId ) | nodeAttr ( Node ) ] ,
2009-09-01 10:19:14 +02:00
children = [ XdataEl ] } ,
PubsubEl = #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children = [ OptionsEl ] } ,
2011-03-23 09:36:03 +01:00
{ result , PubsubEl } ;
_ - >
OptionsEl = #xmlel { ns = ? NS_PUBSUB , name = 'options' ,
attrs = [ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( Subscriber ) ) ,
? XMLATTR ( < < " subid " > > , SubId ) | nodeAttr ( Node ) ] } ,
PubsubEl = #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children = [ OptionsEl ] } ,
2009-09-01 10:19:14 +02:00
{ result , PubsubEl }
2009-08-25 19:14:30 +02:00
end .
2010-09-10 19:45:28 +02:00
set_options ( Host , Node , JID , SubId , Configuration ) - >
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2009-08-25 19:14:30 +02:00
case lists : member ( " subscription-options " , features ( Type ) ) of
true - >
2010-09-10 19:45:28 +02:00
set_options_helper ( Configuration , JID , Nidx ,
SubId , Type ) ;
2009-08-25 19:14:30 +02:00
false - >
2010-10-19 17:08:59 +02:00
{ error , extended_error (
'feature-not-implemented' ,
unsupported , " subscription-options " ) }
2009-08-25 19:14:30 +02:00
end
end ,
case transaction ( Host , Node , Action , sync_dirty ) of
{ result , { _ Node , Result } } - > { result , Result } ;
Error - > Error
end .
2011-05-30 14:04:42 +02:00
set_options_helper ( Configuration , JID , Nidx , SubId , Type ) - >
2009-10-12 12:23:51 +02:00
SubOpts = case pubsub_subscription : parse_options_xform ( Configuration ) of
2010-10-19 17:08:59 +02:00
{ result , GoodSubOpts } - > GoodSubOpts ;
_ - > invalid
end ,
2009-08-25 19:14:30 +02:00
Subscriber = try exmpp_jid : parse ( JID ) of
2010-07-22 18:42:18 +02:00
J - > J
2010-10-19 17:08:59 +02:00
catch
2010-04-29 12:54:35 +02:00
_ - > exmpp_jid : make ( " " , " " , " " ) %% TODO, check if use <<>> instead of ""
2009-08-25 19:14:30 +02:00
end ,
{ result , Subs } = node_call ( Type , get_subscriptions ,
2011-05-30 14:04:42 +02:00
[ Nidx , Subscriber ] ) ,
2010-09-10 19:45:28 +02:00
SubIds = lists : foldl ( fun ( { subscribed , SID } , Acc ) - >
2009-08-25 19:14:30 +02:00
[ SID | Acc ] ;
2010-10-19 17:08:59 +02:00
( _ , Acc ) - >
2009-08-25 19:14:30 +02:00
Acc
end , [ ] , Subs ) ,
2010-09-10 19:45:28 +02:00
case { SubId , SubIds } of
2009-08-25 19:14:30 +02:00
{ _ , [ ] } - >
{ error , extended_error ( 'not-acceptable' , " not-subscribed " ) } ;
{ [ ] , [ SID ] } - >
2011-05-30 14:04:42 +02:00
write_sub ( Subscriber , Nidx , SID , SubOpts ) ;
2009-08-25 19:14:30 +02:00
{ [ ] , _ } - >
{ error , extended_error ( 'not-acceptable' , " subid-required " ) } ;
{ _ , _ } - >
2011-05-30 14:04:42 +02:00
write_sub ( Subscriber , Nidx , SubId , SubOpts )
2009-08-25 19:14:30 +02:00
end .
2011-05-30 14:04:42 +02:00
write_sub ( _ Subscriber , _ Nidx , _ SubId , invalid ) - >
2009-10-12 12:23:51 +02:00
{ error , extended_error ( 'bad-request' , " invalid-options " ) } ;
2011-05-30 14:04:42 +02:00
write_sub ( _ Subscriber , _ Nidx , _ SubId , [ ] ) - >
2011-03-23 09:36:03 +01:00
{ result , [ ] } ;
2011-05-30 14:04:42 +02:00
write_sub ( Subscriber , Nidx , SubId , Options ) - >
case pubsub_subscription : set_subscription ( Subscriber , Nidx , SubId , Options ) of
2009-08-25 19:14:30 +02:00
{ result , _ } - >
2011-03-23 09:36:03 +01:00
{ result , [ ] } ;
{ error , _ } - >
{ error , extended_error ( 'not-acceptable' , " invalid-subid " ) }
2009-08-25 19:14:30 +02:00
end .
2003-07-08 22:11:27 +02:00
2009-05-08 02:02:08 +02:00
%% @spec (Host, Node, JID, Plugins) -> {error, Reason} | {result, Response}
2007-12-01 06:16:30 +01:00
%% Host = host()
2009-05-08 02:02:08 +02:00
%% Node = pubsubNode()
2007-12-01 06:16:30 +01:00
%% JID = jid()
%% Plugins = [Plugin::string()]
%% Reason = stanzaError()
%% Response = [pubsubIQResponse()]
%% @doc <p>Return the list of subscriptions as an XMPP response.</p>
2009-05-08 02:02:08 +02:00
get_subscriptions ( Host , Node , JID , Plugins ) when is_list ( Plugins ) - >
2007-12-01 06:16:30 +01:00
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
2008-12-05 16:13:09 +01:00
{ { error , extended_error ( 'feature-not-implemented' , unsupported , " retrieve-subscriptions " ) } , Acc } ;
2007-12-01 06:16:30 +01:00
true - >
2009-08-25 19:14:30 +02:00
Subscriber = exmpp_jid : bare ( JID ) ,
2009-05-01 01:17:38 +02:00
{ result , Subscriptions } = node_action ( Host , Type , get_entity_subscriptions , [ Host , Subscriber ] ) ,
2007-12-01 06:16:30 +01:00
{ Status , [ Subscriptions | Acc ] }
end
end , { ok , [ ] } , Plugins ) ,
case Result of
{ ok , Subscriptions } - >
Entities = lists : flatmap (
2009-05-08 02:02:08 +02:00
fun ( { _ , none } ) - >
2010-10-19 17:08:59 +02:00
[ ] ;
2010-09-10 19:45:28 +02:00
( { #pubsub_node { id = { _ , SubsNode } } , Subscription } ) - >
2010-10-19 17:08:59 +02:00
case Node of
< < > > - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " node " > > , node_to_string ( SubsNode ) ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2010-10-19 17:08:59 +02:00
SubsNode - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2010-10-19 17:08:59 +02:00
_ - >
[ ]
end ;
2009-05-08 02:02:08 +02:00
( { _ , none , _ } ) - >
2010-10-19 17:08:59 +02:00
[ ] ;
2010-09-10 19:45:28 +02:00
( { #pubsub_node { id = { _ , SubsNode } } , Subscription , SubId , SubJID } ) - >
2010-10-19 17:08:59 +02:00
case Node of
< < > > - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( SubJID ) ) ,
? XMLATTR ( < < " subid " > > , SubId ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) | nodeAttr ( SubsNode ) ] } ] ;
2010-10-19 17:08:59 +02:00
SubsNode - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( SubJID ) ) ,
? XMLATTR ( < < " subid " > > , SubId ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2010-10-19 17:08:59 +02:00
_ - >
[ ]
end ;
2010-09-10 19:45:28 +02:00
( { #pubsub_node { id = { _ , SubsNode } } , Subscription , SubJID } ) - >
2010-10-19 17:08:59 +02:00
case Node of
< < > > - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " node " > > , node_to_string ( SubsNode ) ) ,
? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( SubJID ) ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2010-10-19 17:08:59 +02:00
SubsNode - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( SubJID ) ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2010-10-19 17:08:59 +02:00
_ - >
[ ]
end
2007-12-11 17:19:17 +01:00
end , lists : usort ( lists : flatten ( Subscriptions ) ) ) ,
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'subscriptions' , children =
Entities } ] } } ;
2007-12-01 06:16:30 +01:00
{ Error , _ } - >
Error
2009-05-08 02:02:08 +02:00
end .
2007-12-01 06:16:30 +01:00
get_subscriptions ( Host , Node , JID ) - >
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
Features = features ( Type ) ,
RetrieveFeature = lists : member ( " manage-subscriptions " , Features ) ,
{ result , Affiliation } = node_call ( Type , get_affiliation , [ Nidx , JID ] ) ,
if
not RetrieveFeature - >
%% Service does not support manage subscriptions
{ error , extended_error ( 'feature-not-implemented' , unsupported , " manage-subscriptions " ) } ;
Affiliation / = owner - >
%% Entity is not an owner
{ error , 'forbidden' } ;
true - >
node_call ( Type , get_node_subscriptions , [ Nidx ] )
end
end ,
2007-12-01 06:16:30 +01:00
case transaction ( Host , Node , Action , sync_dirty ) of
2010-10-19 17:08:59 +02:00
%% Fix bug when node owner retrieve an empty subscriptions list
% {result, {_, []}} ->
% {error, 'item-not-found'};
2009-05-01 01:17:38 +02:00
{ result , { _ , Subscriptions } } - >
2007-12-01 06:16:30 +01:00
Entities = lists : flatmap (
fun ( { _ , none } ) - > [ ] ;
2009-08-25 21:54:44 +02:00
( { _ , pending , _ } ) - > [ ] ;
2008-12-05 16:13:09 +01:00
( { { AU , AS , AR } , Subscription } ) - >
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( AU , AS , AR ) ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ] } ] ;
2008-12-05 16:13:09 +01:00
( { { AU , AS , AR } , Subscription , SubId } ) - >
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'subscription' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( AU , AS , AR ) ) ,
? XMLATTR ( < < " subscription " > > , subscription_to_string ( Subscription ) ) ,
? XMLATTR ( < < " subid " > > , SubId ) ] } ]
2007-12-01 06:16:30 +01:00
end , Subscriptions ) ,
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'subscriptions' , attrs = nodeAttr ( Node ) , children =
Entities } ] } } ;
2009-05-01 01:17:38 +02:00
Error - >
Error
2005-07-20 05:09:34 +02:00
end .
2003-07-08 22:11:27 +02:00
2007-12-01 06:16:30 +01:00
set_subscriptions ( Host , Node , From , EntitiesEls ) - >
2008-12-05 16:13:09 +01:00
Owner = jlib : short_prepd_bare_jid ( From ) ,
2007-12-01 06:16:30 +01:00
Entities =
2003-07-08 22:11:27 +02:00
lists : foldl (
2007-12-01 06:16:30 +01:00
fun ( El , Acc ) - >
case Acc of
error - >
error ;
2003-07-08 22:11:27 +02:00
_ - >
2007-12-01 06:16:30 +01:00
case El of
2008-12-05 16:13:09 +01:00
#xmlel { name = 'subscription' , attrs = Attrs } - >
JID = try
2010-10-19 17:08:59 +02:00
exmpp_jid : parse (
2010-11-29 20:44:31 +01:00
exmpp_xml : get_attribute_from_list ( Attrs , < < " jid " > > , " " ) )
2010-10-19 17:08:59 +02:00
catch
_ : _ - >
error
end ,
2007-12-01 06:16:30 +01:00
Subscription = string_to_subscription (
2010-11-29 20:44:31 +01:00
exmpp_xml : get_attribute_from_list_as_list ( Attrs , < < " subscription " > > , false ) ) ,
SubId = exmpp_xml : get_attribute_from_list_as_list ( Attrs , < < " subid " > > , false ) ,
2007-12-01 06:16:30 +01:00
if
( JID == error ) or
( Subscription == false ) - >
error ;
true - >
2009-08-25 21:54:44 +02:00
[ { JID , Subscription , SubId } | Acc ]
2007-12-01 06:16:30 +01:00
end
end
2003-07-08 22:11:27 +02:00
end
2007-12-01 06:16:30 +01:00
end , [ ] , EntitiesEls ) ,
case Entities of
error - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' } ;
2003-07-08 22:11:27 +02:00
_ - >
2009-08-25 21:54:44 +02:00
Notify = fun ( JID , Sub , _ SubId ) - >
2010-10-19 17:08:59 +02:00
Stanza = #xmlel { ns = ? NS_JABBER_CLIENT ,
name = 'message' ,
children =
[ #xmlel { ns = ? NS_PUBSUB ,
name = 'pubsub' ,
children =
[ #xmlel { ns = ? NS_PUBSUB ,
name = 'subscription' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " jid " > > , exmpp_jid : to_binary ( JID ) ) ,
? XMLATTR ( < < " subsription " > > , subscription_to_string ( Sub ) ) | nodeAttr ( Node ) ] } ] } ] } ,
2010-10-19 17:08:59 +02:00
ejabberd_router : route ( service_jid ( Host ) , JID , Stanza )
end ,
2010-09-10 19:45:28 +02:00
Action = fun ( #pubsub_node { owners = Owners , type = Type , idx = Nidx } ) - >
2010-10-19 17:08:59 +02:00
case lists : member ( Owner , Owners ) of
true - >
Result = lists : foldl ( fun ( { JID , Subscription , SubId } , Acc ) - >
case node_call ( Type , set_subscriptions , [ Nidx , JID , Subscription , SubId ] ) of
{ error , Err } - > [ { error , Err } | Acc ] ;
_ - > Notify ( JID , Subscription , SubId ) , Acc
end
end , [ ] , Entities ) ,
case Result of
[ ] - > { result , [ ] } ;
_ - > { error , 'not-acceptable' }
end ;
_ - >
{ error , 'forbidden' }
end
end ,
2009-05-01 01:17:38 +02:00
case transaction ( Host , Node , Action , sync_dirty ) of
{ result , { _ , Result } } - > { result , Result } ;
Other - > Other
end
2003-07-08 22:11:27 +02:00
end .
2010-08-04 18:30:22 +02:00
get_roster_info ( _ , _ , { undefined , undefined , _ } , _ ) - >
{ false , false } ;
2008-04-01 12:11:39 +02:00
%% @spec (OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, SubscriberResource}, AllowedGroups)
%% -> {PresenceSubscription, RosterGroup}
2007-12-01 06:16:30 +01:00
get_roster_info ( OwnerUser , OwnerServer , { SubscriberUser , SubscriberServer , _ } , AllowedGroups ) - >
{ Subscription , Groups } =
ejabberd_hooks : run_fold (
2009-11-06 15:34:23 +01:00
roster_get_jid_info , OwnerServer ,
2007-12-01 06:16:30 +01:00
{ none , [ ] } ,
2009-11-06 15:34:23 +01:00
[ OwnerUser , OwnerServer , exmpp_jid : make ( { SubscriberUser , SubscriberServer , undefined } ) ] ) ,
2008-09-16 16:39:57 +02:00
PresenceSubscription = ( Subscription == both ) orelse ( Subscription == from )
2010-10-19 17:08:59 +02:00
orelse ( { OwnerUser , OwnerServer } == { SubscriberUser , SubscriberServer } ) ,
2007-12-01 06:16:30 +01:00
RosterGroup = lists : any ( fun ( Group ) - >
lists : member ( Group , AllowedGroups )
end , Groups ) ,
2010-08-04 18:30:22 +02:00
{ PresenceSubscription , RosterGroup } ;
get_roster_info ( OwnerUser , OwnerServer , JID , AllowedGroups ) - >
get_roster_info ( OwnerUser , OwnerServer , exmpp_jid : to_lower ( JID ) , AllowedGroups ) .
2007-12-01 06:16:30 +01:00
%% @spec (AffiliationStr) -> Affiliation
%% AffiliationStr = string()
%% Affiliation = atom()
%% @doc <p>Convert an affiliation type from string to atom.</p>
string_to_affiliation ( " owner " ) - > owner ;
string_to_affiliation ( " publisher " ) - > publisher ;
2009-01-03 01:58:18 +01:00
string_to_affiliation ( " member " ) - > member ;
2007-12-01 06:16:30 +01:00
string_to_affiliation ( " outcast " ) - > outcast ;
string_to_affiliation ( " none " ) - > none ;
string_to_affiliation ( _ ) - > false .
%% @spec (SubscriptionStr) -> Subscription
%% SubscriptionStr = string()
%% Subscription = atom()
%% @doc <p>Convert a subscription type from string to atom.</p>
string_to_subscription ( " subscribed " ) - > subscribed ;
string_to_subscription ( " pending " ) - > pending ;
string_to_subscription ( " unconfigured " ) - > unconfigured ;
string_to_subscription ( " none " ) - > none ;
string_to_subscription ( _ ) - > false .
%% @spec (Affiliation) -> AffiliationStr
%% Affiliation = atom()
%% AffiliationStr = string()
%% @doc <p>Convert an affiliation type from atom to string.</p>
affiliation_to_string ( owner ) - > " owner " ;
affiliation_to_string ( publisher ) - > " publisher " ;
2009-01-03 01:58:18 +01:00
affiliation_to_string ( member ) - > " member " ;
2007-12-01 06:16:30 +01:00
affiliation_to_string ( outcast ) - > " outcast " ;
affiliation_to_string ( _ ) - > " none " .
%% @spec (Subscription) -> SubscriptionStr
%% Subscription = atom()
%% SubscriptionStr = string()
%% @doc <p>Convert a subscription type from atom to string.</p>
subscription_to_string ( subscribed ) - > " subscribed " ;
subscription_to_string ( pending ) - > " pending " ;
subscription_to_string ( unconfigured ) - > " unconfigured " ;
subscription_to_string ( _ ) - > " none " .
%% @spec (Node) -> NodeStr
%% Node = pubsubNode()
%% NodeStr = string()
%% @doc <p>Convert a node type from pubsubNode to string.</p>
2009-10-21 00:09:43 +02:00
node_to_string ( Node ) - > binary_to_list ( Node ) .
string_to_node ( SNode ) - > list_to_binary ( SNode ) .
2007-12-01 06:16:30 +01:00
2007-12-22 01:04:05 +01:00
%% @spec (Host) -> jid()
2007-12-11 17:19:17 +01:00
%% Host = host()
2007-12-22 01:04:05 +01:00
%% @doc <p>Generate pubsub service JID.</p>
2010-10-19 17:08:59 +02:00
- spec ( service_jid / 1 : :
(
Host : : host ( ) | string ( ) )
- > ServiceJID : : jidContact ( ) | jidComponent ( )
) .
2007-12-22 01:04:05 +01:00
service_jid ( Host ) - >
case Host of
2010-10-19 17:08:59 +02:00
{ U , S , _ } - > exmpp_jid : make ( U , S ) ;
_ - > exmpp_jid : make ( Host )
2007-12-22 01:04:05 +01:00
end .
2009-08-25 21:54:44 +02:00
%% @spec (LJID, NotifyType, Depth, NodeOptions, SubOptions) -> boolean()
2007-12-22 01:04:05 +01:00
%% LJID = jid()
2009-08-25 19:14:30 +02:00
%% 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 ) .
2011-02-08 19:08:38 +01:00
node_to_deliver ( LJID , NodeOptions ) - >
presence_can_deliver ( LJID , get_option ( NodeOptions , presence_based_delivery ) ) .
2009-08-25 19:14:30 +02:00
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 ;
2011-02-08 19:08:38 +01:00
sub_option_can_deliver ( _ , _ , { deliver , false } ) - > false ;
sub_option_can_deliver ( _ , _ , { expire , When } ) - > now ( ) < When ;
sub_option_can_deliver ( _ , _ , _ ) - > true .
2009-08-25 19:14:30 +02:00
presence_can_deliver ( _ , false ) - > true ;
2010-04-29 12:54:35 +02:00
presence_can_deliver ( { User , Server , Resource } , true ) - >
2011-02-08 19:08:38 +01:00
case ejabberd_sm : get_user_sessions ( User , Server ) of
2010-10-19 17:08:59 +02:00
[ ] - > false ;
Sessions - >
lists : foldl ( fun ( _ , true ) - > true ;
( { session , _ , _ , _ , undefined , _ } , _ Acc ) - > false ;
( { session , _ , { _ , _ , R } , _ , _ Priority , _ } , _ Acc ) - >
case Resource of
undefined - > true ;
R - > true ;
_ - > false
end
end , false , Sessions )
2007-12-11 17:19:17 +01:00
end .
2007-12-01 06:16:30 +01:00
2010-05-12 13:46:34 +02:00
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
2010-10-19 17:08:59 +02:00
%% 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 )
2010-05-12 13:46:34 +02:00
end .
get_resource_state ( { U , S , R } , ShowValues , JIDs ) - >
%% Get user session PID
2011-02-21 15:12:31 +01:00
case ejabberd_sm : get_session_pid ( { U , S , R } ) of
2010-10-19 17:08:59 +02:00
%% 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
2010-05-12 13:46:34 +02:00
end .
2009-01-19 12:59:40 +01:00
%% @spec (Payload) -> int()
2008-12-09 01:32:36 +01:00
%% Payload = term()
2009-01-19 12:59:40 +01:00
%% @doc <p>Count occurence of XML elements in payload.</p>
2010-05-03 14:35:49 +02:00
payload_els_ns ( Payload ) - > payload_els_ns ( Payload , 0 , undefined ) .
payload_els_ns ( [ ] , Count , NS ) - > { Count , NS } ;
payload_els_ns ( [ #xmlel { ns = NS } | Tail ] , Count , undefined ) - > payload_els_ns ( Tail , Count + 1 , NS ) ;
payload_els_ns ( [ #xmlel { } | Tail ] , Count , NS ) - > payload_els_ns ( Tail , Count + 1 , NS ) ;
payload_els_ns ( [ _ | Tail ] , Count , NS ) - > payload_els_ns ( Tail , Count , NS ) .
2008-12-09 01:32:36 +01:00
2009-01-03 01:29:36 +01:00
%% @spec (Els) -> stanza()
2011-02-08 19:08:38 +01:00
%% Els = [xmlel()]
2009-01-19 12:59:40 +01:00
%% @doc <p>Build pubsub event stanza</p>
2009-01-03 01:29:36 +01:00
event_stanza ( Els ) - >
2009-09-23 18:32:06 +02:00
event_stanza_withmoreels ( Els , [ ] ) .
2010-05-28 11:32:51 +02:00
event_stanza_with_delay ( Els , ModifNow , { U , S , R } ) - >
2009-09-23 18:32:06 +02:00
DateTime = calendar : now_to_datetime ( ModifNow ) ,
2010-05-28 11:32:51 +02:00
LJID = exmpp_jid : make ( U , S , R ) ,
MoreEls = [ jlib : timestamp_to_xml ( DateTime , utc , LJID , " " ) ] ,
2009-09-23 18:32:06 +02:00
event_stanza_withmoreels ( Els , MoreEls ) .
event_stanza_withmoreels ( Els , MoreEls ) - >
2009-01-03 01:29:36 +01:00
#xmlel { ns = ? NS_JABBER_CLIENT , name = 'message' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'event' , children = Els } | MoreEls ] } .
2009-01-03 01:29:36 +01:00
2011-02-08 19:08:38 +01:00
event_stanza ( Event , EvAttr ) - >
event_stanza ( [ #xmlel { ns = ? NS_PUBSUB_EVENT , name = Event , attrs = EvAttr } ] ) .
event_stanza ( Event , EvAttr , Entries ) - >
event_stanza ( [ #xmlel { ns = ? NS_PUBSUB_EVENT , name = Event , attrs = EvAttr , children =
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = Entry , attrs = EnAttr } | |
{ Entry , EnAttr } < - Entries ] } ] ) .
event_stanza ( Event , EvAttr , Entry , EnAttr , Payload ) - >
event_stanza ( [ #xmlel { ns = ? NS_PUBSUB_EVENT , name = Event , attrs = EvAttr , children =
[ #xmlel { ns = ? NS_PUBSUB_EVENT , name = Entry , attrs = EnAttr , children = Payload } ] } ] ) .
event_stanza ( Event , EvAttr , Entry , EnAttr , Payload , Publisher ) - >
Stanza = event_stanza ( Event , EvAttr , Entry , EnAttr , Payload ) ,
add_extended_headers ( Stanza , extended_headers ( [ jlib : jid_to_string ( Publisher ) ] ) ) .
2007-12-01 06:16:30 +01:00
%%%%%% broadcast functions
2011-02-08 19:08:38 +01:00
broadcast_publish_item ( Host , Node , NodeId , Type , NodeOptions , ItemId , Publisher , Payload , Removed ) - >
PStanza = case get_option ( NodeOptions , deliver_payloads ) of
true - > event_stanza ( 'items' , nodeAttr ( Node ) , 'item' , itemAttr ( ItemId ) , Payload , Publisher ) ;
false - > event_stanza ( 'items' , nodeAttr ( Node ) , 'item' , itemAttr ( ItemId ) , [ ] , Publisher )
end ,
RStanza = event_stanza ( 'items' , nodeAttr ( Node ) , [ { 'retract' , itemAttr ( Rid ) } | | Rid < - Removed ] ) ,
Stanzas = [ { true , PStanza , true } , { get_option ( NodeOptions , notify_retract ) , RStanza , true } ] ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , items , Stanzas ) } .
2009-05-01 01:17:38 +02:00
2009-08-25 21:54:44 +02:00
broadcast_retract_items ( Host , Node , NodeId , Type , NodeOptions , ItemIds ) - >
2011-02-08 19:08:38 +01:00
broadcast_retract_items ( Host , Node , NodeId , Type , NodeOptions , ItemIds , notify_retract ) .
broadcast_retract_items ( _ Host , _ Node , _ NodeId , _ Type , _ NodeOptions , [ ] , _ ) - >
{ result , false } ;
broadcast_retract_items ( Host , Node , NodeId , Type , NodeOptions , ItemIds , false ) - >
broadcast_retract_items ( Host , Node , NodeId , Type , NodeOptions , ItemIds , notify_retract ) ;
broadcast_retract_items ( Host , Node , NodeId , Type , NodeOptions , ItemIds , Notify ) - >
Stanza = event_stanza ( 'items' , nodeAttr ( Node ) , [ { 'retract' , itemAttr ( Rid ) } | | Rid < - ItemIds ] ) ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , items , Notify , Stanza , true ) } .
2007-12-01 06:16:30 +01:00
2009-08-25 21:54:44 +02:00
broadcast_purge_node ( Host , Node , NodeId , Type , NodeOptions ) - >
2011-02-08 19:08:38 +01:00
Stanza = event_stanza ( 'purge' , nodeAttr ( Node ) ) ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , nodes , notify_retract , Stanza , false ) } .
2007-12-01 06:16:30 +01:00
2011-02-08 19:08:38 +01:00
broadcast_removed_node ( Host , Node , NodeId , Type , NodeOptions ) - >
Stanza = event_stanza ( 'delete' , nodeAttr ( Node ) ) ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , nodes , notify_delete , Stanza , false ) } .
2003-07-08 22:11:27 +02:00
2011-02-08 19:08:38 +01:00
broadcast_created_node ( Host , Node , NodeId , Type , NodeOptions ) - >
Stanza = event_stanza ( 'create' , nodeAttr ( Node ) ) ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , nodes , true , Stanza , true ) } .
2010-05-28 13:27:28 +02:00
2009-08-25 21:54:44 +02:00
broadcast_config_notification ( Host , Node , NodeId , Type , NodeOptions , Lang ) - >
2011-02-08 19:08:38 +01:00
Stanza = case get_option ( NodeOptions , deliver_payloads ) of
2009-05-01 01:17:38 +02:00
true - >
2011-02-08 19:08:38 +01:00
event_stanza ( [ #xmlel { ns = ? NS_PUBSUB_EVENT , name = 'configuration' , attrs = nodeAttr ( Node ) , children =
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs = [ ? XMLATTR ( < < " type " > > , < < " form " > > ) ] , children =
get_configure_xfields ( Type , NodeOptions , Lang , [ ] ) } ] } ] ) ;
false - >
event_stanza ( " configuration " , nodeAttr ( Node ) )
end ,
{ result , broadcast ( Host , Node , NodeId , Type , NodeOptions , nodes , notify_config , Stanza , false ) } .
broadcast ( Host , Node , NodeId , Type , NodeOptions , Notify , Stanzas ) - >
Subs = node_subscriptions ( Host , Node , NodeId , Type , NodeOptions , Notify ) ,
Result = [ broadcast ( Host , Node , NodeId , Type , NodeOptions , Subs , Stanza , SHIM ) | |
{ Cond , Stanza , SHIM } < - Stanzas , Cond =:= true ] ,
lists : member ( true , Result ) .
broadcast ( Host , Node , NodeId , Type , NodeOptions , Notify , true , Stanza , SHIM ) - >
Subs = node_subscriptions ( Host , Node , NodeId , Type , NodeOptions , Notify ) ,
broadcast ( Host , Node , NodeId , Type , NodeOptions , Subs , Stanza , SHIM ) ;
broadcast ( _ Host , _ Node , _ NodeId , _ Type , _ NodeOptions , _ Notify , false , _ Stanza , _ SHIM ) - >
false ;
broadcast ( Host , Node , NodeId , Type , NodeOptions , Notify , Condition , Stanza , SHIM ) - >
broadcast ( Host , Node , NodeId , Type , NodeOptions , Notify , get_option ( NodeOptions , Condition ) , Stanza , SHIM ) .
broadcast ( { U , S , R } , Node , NodeId , Type , NodeOptions , Subscriptions , Stanza , SHIM ) - >
broadcast ( S , Node , NodeId , Type , NodeOptions , Subscriptions , Stanza , SHIM )
2011-02-21 15:12:31 +01:00
or case ejabberd_sm : get_session_pid ( { U , S , user_resource ( U , S , R ) } ) of
2011-02-08 19:08:38 +01:00
C2SPid when is_pid ( C2SPid ) - >
%% 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
2011-03-10 12:24:56 +01:00
Event = { pep_message , < < Node / binary , < < " +notify " > > / binary > > } ,
2011-02-08 19:08:38 +01:00
Message = case get_option ( NodeOptions , notification_type , headline ) of
normal - > Stanza ;
MsgType - > add_message_type ( Stanza , atom_to_list ( MsgType ) )
end ,
ejabberd_c2s : broadcast ( C2SPid , Event , jlib : make_jid ( U , S , " " ) , Message ) ,
true ;
2009-05-01 01:17:38 +02:00
_ - >
2011-02-08 19:08:38 +01:00
? DEBUG ( " ~p @ ~p has no session; can't deliver stanza: ~p " , [ U , S , Stanza ] ) ,
false
end ;
broadcast ( _ Host , _ Node , _ NodeId , _ Type , _ NodeOptions , [ ] , _ Stanza , _ SHIM ) - >
false ;
broadcast ( Host , _ Node , _ NodeId , _ Type , NodeOptions , Subscriptions , Stanza , SHIM ) - >
From = service_jid ( Host ) ,
Message = case get_option ( NodeOptions , notification_type , headline ) of
normal - > Stanza ;
MsgType - > add_message_type ( Stanza , atom_to_list ( MsgType ) )
end ,
lists : foreach ( fun ( { LJID , NodeName , SubIds } ) - >
Send = case { SHIM , SubIds } of
{ false , _ } - > Message ;
{ true , [ _ ] } - > add_shim_headers ( Message , collection_shim ( NodeName ) ) ;
{ true , _ } - > add_shim_headers ( Message , lists : append ( collection_shim ( NodeName ) , subid_shim ( SubIds ) ) )
end ,
ejabberd_router : route ( From , jlib : make_jid ( LJID ) , Send )
end , Subscriptions ) ,
true .
2010-10-19 17:08:59 +02:00
2011-02-08 19:08:38 +01:00
node_subscriptions ( Host , Node , NodeId , Type , _ NodeOptions , Notify ) - >
% TODO temporary dirty condition, should be improved using plugin or node options
case Type of
? STDNODE - > node_subscriptions_bare ( Host , Node , NodeId , Type ) ;
? PEPNODE - > node_subscriptions_bare ( Host , Node , NodeId , Type ) ;
_ - > node_subscriptions_full ( Host , Node , Notify )
2009-08-25 19:14:30 +02:00
end .
2011-02-08 19:08:38 +01:00
node_subscriptions_bare ( Host , Node , NodeId , Type ) - >
case node_action ( Host , Type , get_node_subscriptions , [ NodeId ] ) of
{ result , Subs } - >
SubsByJid = lists : foldl (
fun ( { JID , subscribed , SubId } , Acc ) - >
case dict : is_key ( JID , Acc ) of
true - > dict : append ( JID , SubId , Acc ) ;
false - > dict : store ( JID , [ SubId ] , Acc )
2009-08-25 21:54:44 +02:00
end ;
2011-02-08 19:08:38 +01:00
( _ , Acc ) - >
2009-08-25 19:14:30 +02:00
Acc
2011-02-08 19:08:38 +01:00
end , dict : new ( ) , Subs ) ,
[ { J , Node , S } | | { J , S } < - dict : to_list ( SubsByJid ) ] ;
_ - >
[ ]
end .
node_subscriptions_full ( Host , Node , NotifyType ) - >
Action = fun ( ) - >
Collection = tree_call ( Host , get_parentnodes_tree , [ Host , Node , service_jid ( Host ) ] ) ,
{ result , [ { Depth , [ { N , sub_with_options ( N ) } | | N < - Nodes ] } | | { Depth , Nodes } < - Collection ] }
end ,
case transaction ( Action , sync_dirty ) of
{ result , CollSubs } - > subscribed_nodes_by_jid ( NotifyType , CollSubs ) ;
_ - > [ ]
end .
2010-12-02 20:14:54 +01:00
2011-02-09 14:27:40 +01:00
subscribed_nodes_by_jid ( NotifyType , SubsByDepth ) - >
NodesToDeliver = fun ( Depth , Node , Subs , Acc ) - >
NodeName = case Node #pubsub_node.id 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 .
2011-05-30 14:04:42 +02:00
sub_with_options ( #pubsub_node { type = Type , idx = Nidx } ) - >
case node_call ( Type , get_node_subscriptions , [ Nidx ] ) of
2011-02-08 19:08:38 +01:00
{ result , Subs } - >
lists : foldl (
2011-05-30 14:04:42 +02:00
fun ( { JID , subscribed , SubId } , Acc ) - > [ sub_with_options ( JID , Nidx , SubId ) | Acc ] ;
2011-02-08 19:08:38 +01:00
( _ , Acc ) - > Acc
end , [ ] , Subs ) ;
2008-12-09 01:32:36 +01:00
_ - >
2011-02-08 19:08:38 +01:00
[ ]
end .
2011-05-30 14:04:42 +02:00
sub_with_options ( JID , Nidx , SubId ) - >
case pubsub_subscription : read_subscription ( JID , Nidx , SubId ) of
2011-02-08 19:08:38 +01:00
#pubsub_subscription { options = Options } - > { JID , SubId , Options } ;
_ - > { JID , SubId , [ ] }
end .
2009-08-25 21:54:44 +02:00
2009-03-04 00:26:07 +01:00
user_resources ( User , Server ) - >
2010-03-05 15:38:44 +01:00
ejabberd_sm : get_user_resources ( User , Server ) .
2007-12-22 01:04:05 +01:00
2011-02-21 15:12:31 +01:00
user_resource ( User , Server , undefined ) - >
2011-02-08 19:08:38 +01:00
case user_resources ( User , Server ) of
[ R | _ ] - > R ;
_ - > [ ]
end ;
user_resource ( _ , _ , Resource ) - >
Resource .
2007-12-01 06:16:30 +01:00
%%%%%%% Configuration handling
%%<p>There are several reasons why the default node configuration options request might fail:</p>
%%<ul>
2008-04-01 12:11:39 +02:00
%%<li>The service does not support node configuration.</li>
%%<li>The service does not support retrieval of default node configuration.</li>
2007-12-01 06:16:30 +01:00
%%</ul>
2010-11-10 15:14:16 +01:00
get_configure ( Host , ServerHost , Node , #jid { node = User , domain = Server } = From , Lang ) - >
2007-12-01 06:16:30 +01:00
Action =
2010-09-10 19:45:28 +02:00
fun ( #pubsub_node { options = Options , type = Type , idx = Nidx } ) - >
case node_call ( Type , get_affiliation , [ Nidx , From ] ) of
2007-12-01 06:16:30 +01:00
{ result , owner } - >
2010-11-10 15:14:16 +01:00
Groups = case lists : member ( binary_to_list ( Server ) , ? MYHOSTS ) of
true - >
%Roster_Items = ejabberd_hooks:run_fold(roster_get, ServerHost, [], [{User,Server}]),
Roster_Groups = lists : foldl ( fun
( #roster { groups = [ ] } , Acc ) - > Acc ;
( #roster { groups = Item_Groups } , Acc ) - > [ Item_Groups | Acc ]
end , [ ] , ejabberd_hooks : run_fold ( roster_get , ServerHost , [ ] , [ { User , Server } ] ) ) ,
case Roster_Groups of
[ [ [ ] ] ] - > [ ] ;
_ - > lists : usort ( Roster_Groups )
end ;
false - >
[ ]
end ,
2007-12-01 06:16:30 +01:00
{ result ,
2009-08-25 19:14:30 +02:00
#xmlel { ns = ? NS_PUBSUB_OWNER , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'configure' , attrs =
nodeAttr ( Node ) , children =
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs =
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " type " > > , < < " form " > > ) ] , children =
2010-10-19 17:08:59 +02:00
get_configure_xfields ( Type , Options , Lang , Groups )
} ] } ] } } ;
2007-12-01 06:16:30 +01:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'forbidden' }
2007-12-01 06:16:30 +01:00
end
end ,
2009-05-01 01:17:38 +02:00
case transaction ( Host , Node , Action , sync_dirty ) of
{ result , { _ , Result } } - > { result , Result } ;
Other - > Other
end .
2007-12-01 06:16:30 +01:00
2008-10-08 14:02:30 +02:00
get_default ( Host , Node , _ From , Lang ) - >
2008-12-09 01:32:36 +01:00
Type = select_type ( Host , Host , Node ) ,
2008-06-18 18:19:18 +02:00
Options = node_options ( Type ) ,
2009-08-25 19:14:30 +02:00
{ result , #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'pubsub' , children =
2010-10-19 17:08:59 +02:00
[ #xmlel { ns = ? NS_PUBSUB_OWNER , name = 'default' , children =
2010-11-29 20:44:31 +01:00
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'x' , attrs = [ ? XMLATTR ( < < " type " > > , < < " form " > > ) ] , children =
2010-10-19 17:08:59 +02:00
get_configure_xfields ( Type , Options , Lang , [ ] )
} ] } ] } } .
2007-12-01 06:16:30 +01:00
%% Get node option
%% The result depend of the node type plugin system.
2010-10-19 17:08:59 +02:00
- spec ( get_option / 2 : :
(
Options : : [ ] | [ Option : : nodeOption ( ) ] ,
Key : : atom ( ) )
- > Value : : 'false' | term ( )
) .
2007-12-01 06:16:30 +01:00
get_option ( [ ] , _ ) - > false ;
2010-10-19 17:08:59 +02:00
get_option ( Options , Key ) - >
get_option ( Options , Key , false ) .
- spec ( get_option / 3 : :
(
Options : : [ ] | [ Option : : nodeOption ( ) ] ,
Key : : atom ( ) ,
Default : : term ( ) )
- > Value : : term ( )
) .
get_option ( Options , Key , Default ) - >
case lists : keysearch ( Key , 1 , Options ) of
{ value , { _ Key , Value } } - > Value ;
_ - > Default
2007-12-01 06:16:30 +01:00
end .
2003-07-20 22:35:35 +02:00
2007-12-01 06:16:30 +01:00
%% Get default options from the module plugin.
2010-10-19 17:08:59 +02:00
- spec ( node_options / 1 : :
(
Type : : nodeType ( ) )
- > Options : : [ Option : : nodeOption ( ) ]
) .
2007-12-01 06:16:30 +01:00
node_options ( Type ) - >
Module = list_to_atom ( ? PLUGIN_PREFIX ++ Type ) ,
case catch Module : options ( ) of
{ 'EXIT' , { undef , _ } } - >
DefaultModule = list_to_atom ( ? PLUGIN_PREFIX ++ ? STDNODE ) ,
DefaultModule : options ( ) ;
Result - >
Result
2005-07-20 05:09:34 +02:00
end .
2009-08-25 21:54:44 +02:00
%% @spec (Host, Options) -> MaxItems
%% Host = host()
2007-12-01 06:16:30 +01:00
%% 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.
2010-10-19 17:08:59 +02:00
- spec ( max_items / 2 : :
(
Host : : host ( ) ,
Options : : [ Option : : nodeOption ( ) ] )
- > MaxItems : : integer ( ) | 'unlimited'
) .
2009-08-25 21:54:44 +02:00
max_items ( Host , Options ) - >
2010-10-19 17:08:59 +02:00
case get_option ( Options , 'persist_items' ) of
2007-12-01 06:16:30 +01:00
true - >
2010-10-19 17:08:59 +02:00
case get_option ( Options , 'max_items' ) of
false - > 'unlimited' ;
2007-12-01 06:16:30 +01:00
Result when ( Result < 0 ) - > 0 ;
Result - > Result
2005-07-20 05:09:34 +02:00
end ;
2007-12-01 06:16:30 +01:00
false - >
2010-10-19 17:08:59 +02:00
case get_option ( Options , 'send_last_published_item' ) of
'never' - >
2009-08-25 21:54:44 +02:00
0 ;
_ - >
case is_last_item_cache_enabled ( Host ) of
2010-10-19 17:08:59 +02:00
true - > 0 ;
false - > 1
2009-08-25 21:54:44 +02:00
end
2007-12-01 06:16:30 +01:00
end
2005-07-20 05:09:34 +02:00
end .
- define ( BOOL_CONFIG_FIELD ( Label , Var ) ,
? BOOLXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
2007-12-01 06:16:30 +01:00
get_option ( Options , Var ) ) ) .
2005-07-20 05:09:34 +02:00
- define ( STRING_CONFIG_FIELD ( Label , Var ) ,
? STRINGXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
2008-09-16 16:39:57 +02:00
get_option ( Options , Var , " " ) ) ) .
2005-07-20 05:09:34 +02:00
- define ( INTEGER_CONFIG_FIELD ( Label , Var ) ,
? STRINGXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
2007-12-01 06:16:30 +01:00
integer_to_list ( get_option ( Options , Var ) ) ) ) .
2005-07-20 05:09:34 +02:00
- define ( JLIST_CONFIG_FIELD ( Label , Var , Opts ) ,
? LISTXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
2009-06-01 18:52:14 +02:00
exmpp_jid : to_list ( get_option ( Options , Var ) ) ,
[ exmpp_jid : to_list ( O ) | | O < - Opts ] ) ) .
2005-07-20 05:09:34 +02:00
- define ( ALIST_CONFIG_FIELD ( Label , Var , Opts ) ,
? LISTXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
2007-12-01 06:16:30 +01:00
atom_to_list ( get_option ( Options , Var ) ) ,
2005-07-20 05:09:34 +02:00
[ atom_to_list ( O ) | | O < - Opts ] ) ) .
2009-03-20 01:08:38 +01:00
- define ( LISTM_CONFIG_FIELD ( Label , Var , Opts ) ,
? LISTMXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
get_option ( Options , Var ) , Opts ) ) .
2009-08-25 19:14:30 +02:00
- define ( NLIST_CONFIG_FIELD ( Label , Var ) ,
? STRINGMXFIELD ( Label , " pubsub# " ++ atom_to_list ( Var ) ,
[ node_to_string ( N ) | | N < - get_option ( Options , Var , [ ] ) ] ) ) .
get_configure_xfields ( _ Type , Options , Lang , Groups ) - >
2008-12-05 16:13:09 +01:00
[ ? XFIELD ( " hidden " , " " , " FORM_TYPE " , ? NS_PUBSUB_NODE_CONFIG_s ) ,
2005-07-20 05:09:34 +02:00
? BOOL_CONFIG_FIELD ( " Deliver payloads with event notifications " , deliver_payloads ) ,
2007-12-01 06:16:30 +01:00
? BOOL_CONFIG_FIELD ( " Deliver event notifications " , deliver_notifications ) ,
2005-07-20 05:09:34 +02:00
? 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 ) ,
2010-07-30 16:49:31 +02:00
? BOOL_CONFIG_FIELD ( " Notify owners about new subscribers and unsubscribers " , notify_sub ) ,
2005-07-20 05:09:34 +02:00
? BOOL_CONFIG_FIELD ( " Persist items to storage " , persist_items ) ,
2008-07-11 14:48:27 +02:00
? STRING_CONFIG_FIELD ( " A friendly name for the node " , title ) ,
2005-07-20 05:09:34 +02:00
? INTEGER_CONFIG_FIELD ( " Max # of items to persist " , max_items ) ,
? BOOL_CONFIG_FIELD ( " Whether to allow subscriptions " , subscribe ) ,
2007-12-01 06:16:30 +01:00
? ALIST_CONFIG_FIELD ( " Specify the access model " , access_model ,
[ open , authorize , presence , roster , whitelist ] ) ,
%% XXX: change to list-multi, include current roster groups as options
2009-03-20 01:08:38 +01:00
? LISTM_CONFIG_FIELD ( " Roster groups allowed to subscribe " , roster_groups_allowed , Groups ) ,
2005-07-20 05:09:34 +02:00
? ALIST_CONFIG_FIELD ( " Specify the publisher model " , publish_model ,
[ publishers , subscribers , open ] ) ,
2010-10-19 17:08:59 +02:00
? BOOL_CONFIG_FIELD ( " Purge all items when the relevant publisher goes offline " , purge_offline ) ,
2009-09-24 21:47:01 +02:00
? ALIST_CONFIG_FIELD ( " Specify the event message type " , notification_type ,
[ headline , normal ] ) ,
2005-07-20 05:09:34 +02:00
? INTEGER_CONFIG_FIELD ( " Max payload size in bytes " , max_payload_size ) ,
2007-12-01 06:16:30 +01:00
? ALIST_CONFIG_FIELD ( " When to send the last published item " , send_last_published_item ,
[ never , on_sub , on_sub_and_presence ] ) ,
2009-08-25 19:14:30 +02:00
? BOOL_CONFIG_FIELD ( " Only deliver notifications to available users " , presence_based_delivery ) ,
2010-05-12 14:51:38 +02:00
? NLIST_CONFIG_FIELD ( " The collections with which a node is affiliated " , collection ) ,
? STRING_CONFIG_FIELD ( " The type of node data, usually specified by the namespace of the payload (if any) " , type )
2005-07-20 05:09:34 +02:00
] .
2007-12-01 06:16:30 +01:00
%%<p>There are several reasons why the node configuration request might fail:</p>
%%<ul>
2008-04-01 12:11:39 +02:00
%%<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>
2007-12-01 06:16:30 +01:00
%%</ul>
set_configure ( Host , Node , From , Els , Lang ) - >
2008-12-05 16:13:09 +01:00
case exmpp_xml : remove_cdata_from_list ( Els ) of
[ #xmlel { ns = ? NS_DATA_FORMS , name = 'x' } = XEl ] - >
2010-11-29 20:44:31 +01:00
case exmpp_xml : get_attribute_as_list ( XEl , < < " type " > > , undefined ) of
2008-12-05 16:13:09 +01:00
" cancel " - >
2007-12-01 06:16:30 +01:00
{ result , [ ] } ;
2008-12-05 16:13:09 +01:00
" submit " - >
2007-12-01 06:16:30 +01:00
Action =
2010-09-10 19:45:28 +02:00
fun ( #pubsub_node { options = Options , type = Type , idx = Nidx } = N ) - >
case node_call ( Type , get_affiliation , [ Nidx , From ] ) of
2007-12-01 06:16:30 +01:00
{ result , owner } - >
case jlib : parse_xdata_submit ( XEl ) of
invalid - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' } ;
2007-12-01 06:16:30 +01:00
XData - >
OldOpts = case Options of
[ ] - > node_options ( Type ) ;
_ - > Options
end ,
2009-09-23 18:32:06 +02:00
case set_xoption ( Host , XData , OldOpts ) of
2007-12-01 06:16:30 +01:00
NewOpts when is_list ( NewOpts ) - >
2009-08-25 21:54:44 +02:00
case tree_call ( Host , set_node , [ N #pubsub_node { options = NewOpts } ] ) of
ok - > { result , ok } ;
Err - > Err
end ;
2007-12-01 06:16:30 +01:00
Err - >
Err
end
end ;
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'forbidden' }
2007-12-01 06:16:30 +01:00
end
end ,
case transaction ( Host , Node , Action , transaction ) of
2009-05-01 01:17:38 +02:00
{ result , { TNode , ok } } - >
2010-09-10 19:45:28 +02:00
Nidx = TNode #pubsub_node.idx ,
2009-05-01 01:17:38 +02:00
Type = TNode #pubsub_node.type ,
Options = TNode #pubsub_node.options ,
2010-09-10 19:45:28 +02:00
broadcast_config_notification ( Host , Node , Nidx , Type , Options , Lang ) ,
2007-12-01 06:16:30 +01:00
{ result , [ ] } ;
Other - >
Other
2005-07-20 05:09:34 +02:00
end ;
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' }
2005-07-20 05:09:34 +02:00
end ;
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'bad-request' }
2005-07-20 05:09:34 +02:00
end .
add_opt ( Key , Value , Opts ) - >
Opts1 = lists : keydelete ( Key , 1 , Opts ) ,
[ { Key , Value } | Opts1 ] .
- define ( SET_BOOL_XOPT ( Opt , Val ) ,
2007-12-01 06:16:30 +01:00
BoolVal = case Val of
" 0 " - > false ;
" 1 " - > true ;
" false " - > false ;
" true " - > true ;
_ - > error
end ,
case BoolVal of
2008-12-05 16:13:09 +01:00
error - > { error , 'not-acceptable' } ;
2009-09-23 18:32:06 +02:00
_ - > set_xoption ( Host , Opts , add_opt ( Opt , BoolVal , NewOpts ) )
2005-07-20 05:09:34 +02:00
end ) .
- define ( SET_STRING_XOPT ( Opt , Val ) ,
2009-09-23 18:32:06 +02:00
set_xoption ( Host , Opts , add_opt ( Opt , Val , NewOpts ) ) ) .
2005-07-20 05:09:34 +02:00
- define ( SET_INTEGER_XOPT ( Opt , Val , Min , Max ) ,
case catch list_to_integer ( Val ) of
IVal when is_integer ( IVal ) ,
2007-12-01 06:16:30 +01:00
IVal > = Min ,
IVal =< Max - >
2009-09-23 18:32:06 +02:00
set_xoption ( Host , Opts , add_opt ( Opt , IVal , NewOpts ) ) ;
2005-07-20 05:09:34 +02:00
_ - >
2008-12-05 16:13:09 +01:00
{ error , 'not-acceptable' }
2005-07-20 05:09:34 +02:00
end ) .
- define ( SET_ALIST_XOPT ( Opt , Val , Vals ) ,
case lists : member ( Val , [ atom_to_list ( V ) | | V < - Vals ] ) of
2009-09-23 18:32:06 +02:00
true - > set_xoption ( Host , Opts , add_opt ( Opt , list_to_atom ( Val ) , NewOpts ) ) ;
2008-12-05 16:13:09 +01:00
false - > { error , 'not-acceptable' }
2005-07-20 05:09:34 +02:00
end ) .
2007-12-01 06:16:30 +01:00
- define ( SET_LIST_XOPT ( Opt , Val ) ,
2009-09-23 18:32:06 +02:00
set_xoption ( Host , Opts , add_opt ( Opt , Val , NewOpts ) ) ) .
2005-07-20 05:09:34 +02:00
2009-09-23 18:32:06 +02:00
set_xoption ( _ Host , [ ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
NewOpts ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " FORM_TYPE " , _ } | Opts ] , NewOpts ) - >
set_xoption ( Host , Opts , NewOpts ) ;
set_xoption ( Host , [ { " pubsub#roster_groups_allowed " , Value } | Opts ] , NewOpts ) - >
2009-03-20 01:08:38 +01:00
? SET_LIST_XOPT ( roster_groups_allowed , Value ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#deliver_payloads " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( deliver_payloads , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#deliver_notifications " , [ Val ] } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_BOOL_XOPT ( deliver_notifications , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#notify_config " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( notify_config , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#notify_delete " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( notify_delete , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#notify_retract " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( notify_retract , Val ) ;
2010-05-12 12:01:54 +02:00
set_xoption ( Host , [ { " pubsub#notify_sub " , [ Val ] } | Opts ] , NewOpts ) - >
? SET_BOOL_XOPT ( notify_sub , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#persist_items " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( persist_items , Val ) ;
2009-09-23 18:32:06 +02:00
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 ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( subscribe , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#access_model " , [ Val ] } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_ALIST_XOPT ( access_model , Val , [ open , authorize , presence , roster , whitelist ] ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#publish_model " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_ALIST_XOPT ( publish_model , Val , [ publishers , subscribers , open ] ) ;
2009-09-24 21:47:01 +02:00
set_xoption ( Host , [ { " pubsub#notification_type " , [ Val ] } | Opts ] , NewOpts ) - >
? SET_ALIST_XOPT ( notification_type , Val , [ headline , normal ] ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#node_type " , [ Val ] } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_ALIST_XOPT ( node_type , Val , [ leaf , collection ] ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#max_payload_size " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_INTEGER_XOPT ( max_payload_size , Val , 0 , ? MAX_PAYLOAD_SIZE ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#send_last_published_item " , [ Val ] } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_ALIST_XOPT ( send_last_published_item , Val , [ never , on_sub , on_sub_and_presence ] ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#presence_based_delivery " , [ Val ] } | Opts ] , NewOpts ) - >
2005-07-20 05:09:34 +02:00
? SET_BOOL_XOPT ( presence_based_delivery , Val ) ;
2010-03-05 16:09:06 +01:00
set_xoption ( Host , [ { " pubsub#purge_offline " , [ Val ] } | Opts ] , NewOpts ) - >
? SET_BOOL_XOPT ( purge_offline , Val ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#title " , Value } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_STRING_XOPT ( title , Value ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#type " , Value } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_STRING_XOPT ( type , Value ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#body_xslt " , Value } | Opts ] , NewOpts ) - >
2007-12-01 06:16:30 +01:00
? SET_STRING_XOPT ( body_xslt , Value ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#collection " , Value } | Opts ] , NewOpts ) - >
2009-08-25 21:54:44 +02:00
NewValue = [ string_to_node ( V ) | | V < - Value ] ,
? SET_LIST_XOPT ( collection , NewValue ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ { " pubsub#node " , [ Value ] } | Opts ] , NewOpts ) - >
2009-08-25 21:54:44 +02:00
NewValue = string_to_node ( Value ) ,
? SET_LIST_XOPT ( node , NewValue ) ;
2009-09-23 18:32:06 +02:00
set_xoption ( Host , [ _ | Opts ] , NewOpts ) - >
2010-10-19 17:08:59 +02:00
% skip unknown field
2009-09-23 18:32:06 +02:00
set_xoption ( Host , Opts , NewOpts ) .
2010-10-19 17:08:59 +02:00
- spec ( get_max_items_node / 1 : :
(
Host : : host ( ) )
- > MaxItems : : integer ( )
) .
2009-09-23 18:32:06 +02:00
get_max_items_node ( { _ , ServerHost , _ } ) - >
get_max_items_node ( ServerHost ) ;
get_max_items_node ( Host ) - >
2010-10-19 17:08:59 +02:00
case catch ets : lookup ( gen_mod : get_module_proc ( Host , 'config' ) , 'max_items_node' ) of
[ { 'max_items_node' , Integer } ] - > Integer ;
_ - > ? MAXITEMS
2009-09-23 18:32:06 +02:00
end .
2007-12-01 06:16:30 +01:00
2009-05-29 01:21:50 +02:00
%%%% last item cache handling
2010-10-19 17:08:59 +02:00
- spec ( is_last_item_cache_enabled / 1 : :
(
Host : : string ( ) | host ( ) )
- > IsLastItemCacheEnabled : : boolean ( )
) .
2009-05-29 01:21:50 +02:00
2009-08-25 21:54:44 +02:00
is_last_item_cache_enabled ( { _ , ServerHost , _ } ) - >
2010-05-28 13:17:36 +02:00
is_last_item_cache_enabled ( binary_to_list ( ServerHost ) ) ;
2009-08-25 21:54:44 +02:00
is_last_item_cache_enabled ( Host ) - >
2010-10-19 17:08:59 +02:00
case catch ets : lookup ( gen_mod : get_module_proc ( Host , 'config' ) , 'last_item_cache' ) of
[ { 'last_item_cache' , true } ] - > true ;
_ - > false
2009-08-25 21:54:44 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( set_cached_item / 5 : :
(
Host : : string ( ) | host ( ) ,
NodeId : : nodeIdx ( ) ,
ItemId : : itemId ( ) ,
Publisher : : jidEntity ( ) ,
Payload : : payload ( ) )
- > 'ok' | { 'aborted' , Reason : : _ }
) .
2010-05-28 11:32:51 +02:00
set_cached_item ( { _ , ServerHost , _ } , NodeId , ItemId , Publisher , Payload ) - >
2010-05-28 13:17:36 +02:00
set_cached_item ( binary_to_list ( ServerHost ) , NodeId , ItemId , Publisher , Payload ) ;
2010-10-19 17:08:59 +02:00
set_cached_item ( Host , NodeId , ItemId , #jid { node = U , domain = S } = _ Publisher , Payload ) - >
2009-08-25 21:54:44 +02:00
case is_last_item_cache_enabled ( Host ) of
2010-10-19 17:08:59 +02:00
true - >
mnesia : dirty_write ( { pubsub_last_item , NodeId , ItemId , { now ( ) , { U , S , undefined } } , Payload } ) ;
_ - > ok
2009-05-29 01:21:50 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( unset_cached_item / 2 : :
(
Host : : string ( ) | host ( ) ,
NodeId : : nodeIdx ( ) )
- > 'ok' | { 'aborted' , Reason : : _ }
) .
2009-05-29 01:21:50 +02:00
unset_cached_item ( { _ , ServerHost , _ } , NodeId ) - >
2010-05-28 13:17:36 +02:00
unset_cached_item ( binary_to_list ( ServerHost ) , NodeId ) ;
2009-05-29 01:21:50 +02:00
unset_cached_item ( Host , NodeId ) - >
2009-08-25 21:54:44 +02:00
case is_last_item_cache_enabled ( Host ) of
2010-10-19 17:08:59 +02:00
true - > mnesia : dirty_delete ( { pubsub_last_item , NodeId } ) ;
_ - > ok
2009-05-29 01:21:50 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( get_cached_item / 2 : :
(
Host : : string ( ) | host ( ) ,
NodeId : : nodeIdx ( ) )
- > CachedItem : : 'undefined' | pubsubItem ( )
) .
2009-05-29 01:21:50 +02:00
get_cached_item ( { _ , ServerHost , _ } , NodeId ) - >
2010-05-28 13:17:36 +02:00
get_cached_item ( binary_to_list ( ServerHost ) , NodeId ) ;
2009-05-29 01:21:50 +02:00
get_cached_item ( Host , NodeId ) - >
2009-08-25 21:54:44 +02:00
case is_last_item_cache_enabled ( Host ) of
2010-10-19 17:08:59 +02:00
true - >
case mnesia : dirty_read ( { pubsub_last_item , NodeId } ) of %% TODO
[ { pubsub_last_item , NodeId , ItemId , Creation , Payload } ] - >
#pubsub_item { id = { ItemId , NodeId } , payload = Payload ,
creation = Creation , modification = Creation } ;
_ - >
undefined
end ;
2009-05-29 01:21:50 +02:00
_ - >
undefined
end .
2007-12-01 06:16:30 +01:00
%%%% plugin handling
2010-10-19 17:08:59 +02:00
- spec ( host / 1 : :
(
ServerHost : : string ( ) | binary ( ) )
- > Host : : string ( )
) .
2010-01-12 16:14:57 +01:00
host ( ServerHost ) - >
2010-10-19 17:08:59 +02:00
case catch ets : lookup ( gen_mod : get_module_proc ( ServerHost , 'config' ) , 'host' ) of
[ { 'host' , Host } ] - > Host ;
_ - > " pubsub. " ++ ServerHost
2010-01-12 16:14:57 +01:00
end .
2010-10-19 17:08:59 +02:00
- spec ( plugins / 1 : :
(
Host : : binary ( ) | string ( ) )
- > Plugins : : [ Plugin : : nodeType ( ) ]
) .
2009-08-25 19:14:30 +02:00
plugins ( Host ) when is_binary ( Host ) - >
2010-01-12 16:14:57 +01:00
plugins ( binary_to_list ( Host ) ) ;
2009-08-25 19:14:30 +02:00
plugins ( Host ) when is_list ( Host ) - >
2010-10-19 17:08:59 +02:00
case catch ets : lookup ( gen_mod : get_module_proc ( Host , 'config' ) , 'plugins' ) of
[ { 'plugins' , [ ] } ] - > [ ? STDNODE ] ;
[ { 'plugins' , Plugins } ] - > Plugins ;
_ - > [ ? STDNODE ]
end .
- spec ( select_type / 4 : :
(
ServerHost : : binary ( ) | string ( ) ,
Host : : host ( ) ,
NodeId : : nodeId ( ) ,
Type : : nodeType ( ) )
- > SelectedType : : nodeType ( )
) .
select_type ( ServerHost , Host , NodeId , Type ) when is_binary ( ServerHost ) - >
select_type ( binary_to_list ( ServerHost ) , Host , NodeId , Type ) ;
select_type ( ServerHost , Host , NodeId , Type ) - >
2009-05-29 01:21:50 +02:00
SelectedType = case Host of
2010-10-19 17:08:59 +02:00
{ _ User , _ Server , _ Resource } - >
case catch ets : lookup ( gen_mod : get_module_proc ( ServerHost , 'config' ) , 'pep_mapping' ) of
[ { 'pep_mapping' , PepMapping } ] - >
proplists : get_value ( node_to_string ( NodeId ) , PepMapping , ? PEPNODE ) ;
_ - > ? PEPNODE
end ;
_ - > Type
end ,
%ConfiguredTypes = plugins(ServerHost),
case lists : member ( SelectedType , ConfiguredTypes = plugins ( ServerHost ) ) of
true - > SelectedType ;
false - > hd ( ConfiguredTypes )
2008-10-08 14:02:30 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( select_type / 3 : :
(
ServerHost : : binary ( ) | string ( ) ,
Host : : host ( ) ,
NodeId : : nodeId ( ) )
- > SelectedType : : nodeType ( )
) .
select_type ( ServerHost , Host , NodeId ) - >
select_type ( ServerHost , Host , NodeId , hd ( plugins ( ServerHost ) ) ) .
- spec ( features / 0 : : ( ) - > Features : : [ Feature : : string ( ) ] ) .
2008-04-02 11:06:28 +02:00
2007-12-01 06:16:30 +01:00
features ( ) - >
2010-10-19 17:08:59 +02:00
[
% see plugin "access-authorize", % OPTIONAL
" access-open " , % OPTIONAL this relates to access_model option in node_flat
" access-presence " , % OPTIONAL this relates to access_model option in node_pep
%TODO "access-roster", % OPTIONAL
" access-whitelist " , % OPTIONAL
% see plugin "auto-create", % OPTIONAL
% see plugin "auto-subscribe", % RECOMMENDED
" collections " , % RECOMMENDED
" config-node " , % RECOMMENDED
" create-and-configure " , % RECOMMENDED
% see plugin "create-nodes", % RECOMMENDED
% see plugin "delete-items", % RECOMMENDED
% see plugin "delete-nodes", % RECOMMENDED
% see plugin "filtered-notifications", % RECOMMENDED
% see plugin "get-pending", % OPTIONAL
% see plugin "instant-nodes", % RECOMMENDED
" item-ids " , % RECOMMENDED
" last-published " , % RECOMMENDED
%TODO "cache-last-item",
%TODO "leased-subscription", % OPTIONAL
% see plugin "manage-subscriptions", % OPTIONAL
" member-affiliation " , % RECOMMENDED
%TODO "meta-data", % RECOMMENDED
% see plugin "modify-affiliations", % OPTIONAL
% see plugin "multi-collection", % OPTIONAL
% see plugin "multi-subscribe", % OPTIONAL
% see plugin "outcast-affiliation", % RECOMMENDED
% see plugin "persistent-items", % RECOMMENDED
" presence-notifications " , % OPTIONAL
" presence-subscribe " , % RECOMMENDED
% see plugin "publish", % REQUIRED
%TODO "publish-options", % OPTIONAL
" publisher-affiliation " , % RECOMMENDED
% see plugin "purge-nodes", % OPTIONAL
% see plugin "retract-items", % OPTIONAL
% see plugin "retrieve-affiliations", % RECOMMENDED
" retrieve-default " % RECOMMENDED
% 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
] .
- spec ( features / 1 : :
(
Type : : nodeType ( ) )
- > Features : : [ Feature : : string ( ) ]
) .
2007-12-01 06:16:30 +01:00
features ( Type ) - >
Module = list_to_atom ( ? PLUGIN_PREFIX ++ Type ) ,
features ( ) ++ case catch Module : features ( ) of
{ 'EXIT' , { undef , _ } } - > [ ] ;
Result - > Result
end .
2010-10-19 17:08:59 +02:00
- spec ( features / 2 : :
(
Host : : string ( ) | binary ( ) ,
NodeId : : nodeId ( ) )
- > Features : : [ Feature : : string ( ) ]
) .
features ( Host , < < > > = _ NodeId ) - >
2008-04-02 11:06:28 +02:00
lists : usort ( lists : foldl ( fun ( Plugin , Acc ) - >
2010-10-19 17:08:59 +02:00
Acc ++ features ( Plugin )
end , [ ] , plugins ( Host ) ) ) ;
features ( Host , NodeId ) - >
2009-05-01 01:17:38 +02:00
Action = fun ( #pubsub_node { type = Type } ) - > { result , features ( Type ) } end ,
2010-10-19 17:08:59 +02:00
case transaction ( Host , NodeId , Action , sync_dirty ) of
{ result , Features } - > lists : usort ( features ( ) ++ Features ) ;
_ - > features ( )
2009-05-01 01:17:38 +02:00
end .
2007-12-01 06:16:30 +01:00
%% @doc <p>node tree plugin call.</p>
2010-10-19 17:08:59 +02:00
- spec ( tree_call / 3 : :
(
Host : : string ( ) | host ( ) ,
Function : : atom ( ) ,
Args : : [ term ( ) ] )
- > any ( ) %% TODO
) .
2007-12-01 06:16:30 +01:00
tree_call ( { _ User , Server , _ Resource } , Function , Args ) - >
tree_call ( Server , Function , Args ) ;
tree_call ( Host , Function , Args ) - >
2009-05-01 01:17:38 +02:00
? DEBUG ( " tree_call ~p ~p ~p " , [ Host , Function , Args ] ) ,
2010-10-19 17:08:59 +02:00
Module = case catch ets : lookup ( gen_mod : get_module_proc ( Host , 'config' ) , 'nodetree' ) of
[ { 'nodetree' , NodeTree } ] - > NodeTree ;
_ - > list_to_atom ( ? TREE_PREFIX ++ ? STDTREE )
end ,
2007-12-01 06:16:30 +01:00
catch apply ( Module , Function , Args ) .
2010-10-19 17:08:59 +02:00
- spec ( tree_action / 3 : :
(
Host : : string ( ) | host ( ) ,
Function : : atom ( ) ,
Args : : [ term ( ) ] )
- > any ( ) %% TODO
) .
2007-12-01 06:16:30 +01:00
tree_action ( Host , Function , Args ) - >
2009-05-26 23:50:13 +02:00
? DEBUG ( " tree_action ~p ~p ~p " , [ Host , Function , Args ] ) ,
2010-10-19 17:08:59 +02:00
%Fun = fun() -> tree_call(Host, Function, Args) end,
catch mnesia : sync_dirty (
fun ( ) - > tree_call ( Host , Function , Args ) end ) .
2007-12-01 06:16:30 +01:00
%% @doc <p>node plugin call.</p>
2010-10-19 17:08:59 +02:00
- spec ( node_call / 3 : :
(
Type : : nodeType ( ) ,
Function : : atom ( ) ,
Args : : [ term ( ) ] )
- > { 'result' , Result : : _ } | { 'error' , Error : : _ } %% TODO
) .
2007-12-01 06:16:30 +01:00
node_call ( Type , Function , Args ) - >
2009-05-01 01:17:38 +02:00
? DEBUG ( " node_call ~p ~p ~p " , [ Type , Function , Args ] ) ,
2007-12-01 06:16:30 +01:00
Module = list_to_atom ( ? PLUGIN_PREFIX ++ Type ) ,
2010-11-08 11:18:33 +01:00
case apply ( Module , Function , Args ) of
2007-12-01 06:16:30 +01:00
{ 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
2005-07-20 05:09:34 +02:00
end .
2010-10-19 17:08:59 +02:00
- spec ( node_action / 4 : :
(
Host : : string ( ) | host ( ) ,
Type : : nodeType ( ) ,
Function : : atom ( ) ,
Args : : [ term ( ) ] )
- > any ( ) %% TODO
) .
2009-05-26 23:50:13 +02:00
node_action ( Host , Type , Function , Args ) - >
? DEBUG ( " node_action ~p ~p ~p ~p " , [ Host , Type , Function , Args ] ) ,
2007-12-01 06:16:30 +01:00
transaction ( fun ( ) - >
node_call ( Type , Function , Args )
end , sync_dirty ) .
%% @doc <p>plugin transaction handling.</p>
2010-10-19 17:08:59 +02:00
- spec ( transaction / 4 : :
(
Host : : string ( ) | host ( ) ,
NodeId : : nodeId ( ) ,
Action : : fun ( ) ,
Trans : : atom ( ) )
- > { 'result' , { Node : : pubsubNode ( ) , Result : : _ } } | { 'error' , Error : : _ }
) .
transaction ( Host , NodeId , Action , Trans ) - >
2007-12-01 06:16:30 +01:00
transaction ( fun ( ) - >
2010-10-19 17:08:59 +02:00
case tree_call ( Host , get_node , [ Host , NodeId ] ) of
#pubsub_node { } = Node - >
case Action ( Node ) of
{ result , Result } - > { result , { Node , Result } } ;
{ atomic , { result , Result } } - > { result , { Node , Result } } ;
2009-05-01 01:17:38 +02:00
Other - > Other
end ;
Error - >
Error
2007-12-01 06:16:30 +01:00
end
end , Trans ) .
2010-10-19 17:08:59 +02:00
- spec ( transaction / 3 : :
(
Host : : string ( ) | host ( ) ,
Action : : fun ( ) ,
Trans : : atom ( ) )
- > { 'result' , Nodes : : [ ] | [ Node : : pubsubNode ( ) ] }
) .
2010-08-04 18:30:22 +02:00
transaction ( Host , Action , Trans ) - >
transaction ( fun ( ) - >
{ result , lists : foldl ( Action , [ ] , tree_call ( Host , get_nodes , [ Host ] ) ) }
end , Trans ) .
2010-10-19 17:08:59 +02:00
- spec ( transaction / 2 : :
(
Fun : : fun ( ) ,
Trans : : atom ( ) )
- > { 'result' , Result : : _ } | { 'error' , Error : : _ }
) .
2007-12-01 06:16:30 +01:00
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 } ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'internal-server-error' } ;
2007-12-01 06:16:30 +01:00
{ 'EXIT' , Reason } - >
? ERROR_MSG ( " transaction return internal error: ~p ~n " , [ { 'EXIT' , Reason } ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'internal-server-error' } ;
2007-12-01 06:16:30 +01:00
Other - >
? ERROR_MSG ( " transaction return internal error: ~p ~n " , [ Other ] ) ,
2008-12-05 16:13:09 +01:00
{ error , 'internal-server-error' }
2005-04-17 20:08:34 +02:00
end .
2007-12-01 06:16:30 +01:00
%%%% helpers
%% Add pubsub-specific error element
extended_error ( Error , Ext ) - >
2008-12-05 16:13:09 +01:00
extended_error ( Error , Ext , [ ] ) .
2007-12-01 06:16:30 +01:00
extended_error ( Error , unsupported , Feature ) - >
2008-12-05 16:13:09 +01:00
extended_error ( Error , unsupported ,
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " feature " > > , Feature ) ] ) ;
2008-12-05 16:13:09 +01:00
extended_error ( Error , Ext , ExtAttrs ) - >
2010-10-19 17:08:59 +02:00
%Pubsub_Err = #xmlel{ns = ?NS_PUBSUB_ERRORS, name = Ext, attrs = ExtAttrs},
2008-12-05 16:13:09 +01:00
exmpp_xml : append_child ( exmpp_stanza : error ( ? NS_JABBER_CLIENT , Error ) ,
2010-10-19 17:08:59 +02:00
#xmlel { ns = ? NS_PUBSUB_ERRORS , name = Ext , attrs = ExtAttrs } ) .
2007-12-01 06:16:30 +01:00
%% Give a uniq identifier
2010-10-19 17:08:59 +02:00
- spec ( uniqid / 0 : : ( ) - > Id : : binary ( ) ) .
2007-12-01 06:16:30 +01:00
uniqid ( ) - >
{ T1 , T2 , T3 } = now ( ) ,
2010-10-19 17:08:59 +02:00
list_to_binary ( lists : flatten ( io_lib : fwrite ( " ~.16B ~.16B ~.16B " , [ T1 , T2 , T3 ] ) ) ) .
2008-02-06 19:04:23 +01:00
2010-10-19 17:08:59 +02:00
% node attributes
2009-10-21 00:09:43 +02:00
nodeAttr ( Node ) when is_list ( Node ) - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " node " > > , Node ) ] ;
2009-05-08 03:28:17 +02:00
nodeAttr ( Node ) - >
2010-11-29 20:44:31 +01:00
[ ? XMLATTR ( < < " node " > > , node_to_string ( Node ) ) ] .
2009-04-10 15:21:37 +02:00
2010-10-19 17:08:59 +02:00
% item attributes
2009-04-10 15:21:37 +02:00
itemAttr ( [ ] ) - > [ ] ;
2010-11-29 20:44:31 +01:00
itemAttr ( ItemId ) - > [ ? XMLATTR ( < < " id " > > , ItemId ) ] .
2009-04-10 15:21:37 +02:00
2010-10-19 17:08:59 +02:00
% build item elements from item list
- spec ( itemsEls / 1 : :
(
PubsubItems : : [ PubsubItem : : pubsubItem ( ) ] )
2010-11-10 21:27:53 +01:00
- > Items : : [ Item : : #xmlel { } ]
2010-10-19 17:08:59 +02:00
) .
itemsEls ( PubsubItems ) - >
[ #xmlel { ns = ? NS_PUBSUB , name = 'item' , attrs = itemAttr ( ItemId ) , children = Payload }
| | #pubsub_item { id = { ItemId , _ } , payload = Payload } < - PubsubItems ] .
2009-08-25 19:14:30 +02:00
2009-09-24 21:47:01 +02:00
add_message_type ( #xmlel { name = 'message' } = El , Type ) - > exmpp_stanza : set_type ( El , Type ) ;
add_message_type ( El , _ Type ) - > El .
2010-01-12 13:34:02 +01:00
%% 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)".
2010-03-05 18:33:12 +01:00
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 { children = Els } = Stanza , HeaderName , HeaderNS , HeaderEls ) - >
HeaderEl = #xmlel { name = HeaderName , ns = HeaderNS , children = HeaderEls } ,
Stanza #xmlel { children = lists : append ( Els , [ HeaderEl ] ) } .
2009-08-25 19:14:30 +02:00
2010-01-12 13:34:02 +01:00
%% 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 { ns = ? NS_PUBSUB , name = 'header' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " name " > > , < < " Collection " > > ) ] ,
2010-01-12 13:34:02 +01:00
children = [ ? XMLCDATA ( node_to_string ( Node ) ) ] } ] .
2010-09-10 19:45:28 +02:00
subid_shim ( SubIds ) - >
2010-01-12 13:34:02 +01:00
[ #xmlel { ns = ? NS_PUBSUB , name = 'header' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " name " > > , < < " SubId " > > ) ] ,
2010-09-10 19:45:28 +02:00
children = [ ? XMLCDATA ( SubId ) ] }
2010-10-19 17:08:59 +02:00
| | SubId < - SubIds ] .
2010-03-05 15:38:44 +01:00
2010-03-05 18:33:12 +01:00
2010-09-10 19:45:28 +02:00
extended_headers ( JIDs ) - >
2010-03-05 18:33:12 +01:00
[ #xmlel { ns = ? NS_ADDRESS , name = 'address' ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " type " > > , < < " replyto " > > ) , ? XMLATTR ( < < " jid " > > , JID ) ] }
2010-10-19 17:08:59 +02:00
| | JID < - JIDs ] .
2010-03-05 18:33:12 +01:00
2010-03-05 16:09:06 +01:00
on_user_offline ( _ , JID , _ ) - >
{ User , Server , Resource } = jlib : short_prepd_jid ( JID ) ,
2011-02-08 19:08:38 +01:00
case user_resources ( User , Server ) of
2010-03-05 16:09:06 +01:00
[ ] - > purge_offline ( { User , Server , Resource } ) ;
_ - > true
end .
purge_offline ( { User , Server , _ } = LJID ) - >
2010-03-22 20:27:13 +01:00
JID = exmpp_jid : make ( User , Server ) ,
2010-03-05 16:09:06 +01:00
Host = host ( Server ) ,
Plugins = plugins ( Host ) ,
Result = lists : foldl (
2010-10-19 17:08:59 +02:00
fun ( Type , { Status , Acc } ) - >
case lists : member ( " retrieve-affiliations " , features ( Type ) ) of
false - >
{ { error , extended_error ( '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 ) ,
2010-03-05 16:09:06 +01:00
case Result of
{ ok , Affiliations } - >
lists : foreach (
2010-10-19 17:08:59 +02:00
fun ( { #pubsub_node { id = { _ , NodeId } , options = Options , type = Type } , Affiliation } )
when Affiliation == 'owner' orelse Affiliation == 'publisher' - >
Action = fun ( #pubsub_node { type = NType , idx = Nidx } ) - >
node_call ( NType , get_items , [ Nidx , 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 { id = { ItemId , _ } , modification = { _ , Modification } } ) - >
2010-03-05 16:09:06 +01:00
case Modification of
{ User , Server , _ } - >
delete_item ( Host , NodeId , LJID , ItemId , ForceNotify ) ;
_ - >
true
end ;
2010-10-19 17:08:59 +02:00
( _ ) - >
2010-03-05 16:09:06 +01:00
true
2010-10-19 17:08:59 +02:00
end , Items ) ;
_ - >
true
end ;
Error - >
Error
end ;
( _ ) - >
true
end , lists : usort ( lists : flatten ( Affiliations ) ) ) ;
2010-03-05 16:09:06 +01:00
{ Error , _ } - >
? DEBUG ( " on_user_offline ~p " , [ Error ] )
end .
2010-05-12 12:01:54 +02:00
notify_owners ( false , _ , _ , _ , _ , _ ) - > true ;
notify_owners ( true , JID , Host , Node , Owners , State ) - >
Message = #xmlel { name = 'message' , ns = ? NS_JABBER_CLIENT ,
2010-10-19 17:08:59 +02:00
children = [ #xmlel { name = 'pubsub' , ns = ? NS_PUBSUB ,
children = [ #xmlel { name = 'subscription' , ns = ? NS_PUBSUB ,
2010-11-29 20:44:31 +01:00
attrs = [ ? XMLATTR ( < < " node " > > , Node ) ,
? XMLATTR ( < < " jid " > > , exmpp_jid : prep_to_binary ( exmpp_jid : make ( JID ) ) ) ,
? XMLATTR ( < < " subscription " > > , State ) ] } ] } ] } ,
2010-05-12 12:01:54 +02:00
lists : foreach (
fun ( Owner ) - >
2010-10-19 17:08:59 +02:00
ejabberd_router : route ( exmpp_jid : make ( Host ) , exmpp_jid : make ( Owner ) , Message )
2010-05-12 12:01:54 +02:00
end , Owners ) .