mirror of
https://github.com/processone/ejabberd.git
synced 2024-10-13 15:16:49 +02:00
* src/mod_caps.erl: CAPS support (thanks to Magnus Henoch)
* src/ejabberd_local.erl: Support for IQ responses * src/jlib.erl: Added iq_query_or_response_info/1 function * src/jlib.hrl: Added NS_PUBSUB_ERRORS and NS_CAPS * src/mod_pubsub/Makefile.in: New pubsub+pep implementation (thanks to Christophe Romain and Magnus Henoch) * src/ejabberd_sm.erl: Added get_session_pid/3 function * src/ejabberd_c2s.erl: Added get_subscribed_and_online/1 function SVN Revision: 1004
This commit is contained in:
parent
e4cf286aa2
commit
c3c782d882
48
ChangeLog
48
ChangeLog
@ -1,32 +1,44 @@
|
||||
2007-12-01 Alexey Shchepin <alexey@process-one.net>
|
||||
|
||||
* src/mod_caps.erl: CAPS support (thanks to Magnus Henoch)
|
||||
* src/ejabberd_local.erl: Support for IQ responses
|
||||
* src/jlib.erl: Added iq_query_or_response_info/1 function
|
||||
* src/jlib.hrl: Added NS_PUBSUB_ERRORS and NS_CAPS
|
||||
|
||||
* src/mod_pubsub/Makefile.in: New pubsub+pep implementation
|
||||
(thanks to Christophe Romain and Magnus Henoch)
|
||||
* src/ejabberd_sm.erl: Added get_session_pid/3 function
|
||||
* src/ejabberd_c2s.erl: Added get_subscribed_and_online/1 function
|
||||
|
||||
2007-11-30 Mickael Remond <mremond@process-one.net>
|
||||
|
||||
* src/odbc_queries.erl: Added a default define value so that we can
|
||||
recompile the file manually with a simple erlc command.
|
||||
* src/odbc_queries.erl: Added a default define value so that we
|
||||
can recompile the file manually with a simple erlc command.
|
||||
|
||||
2007-11-29 Badlop <badlop@process-one.net>
|
||||
|
||||
* src/mod_vcard.erl: Add type of x:data field to search
|
||||
results (thanks to Robin Redeker) (EJAB-327)
|
||||
* src/mod_vcard_ldap.erl:
|
||||
* src/mod_vcard_odbc.erl:
|
||||
* src/mod_vcard.erl: Add type of x:data field to search results
|
||||
(thanks to Robin Redeker) (EJAB-327)
|
||||
* src/mod_vcard_ldap.erl: Likewise
|
||||
* src/mod_vcard_odbc.erl: Likewise
|
||||
|
||||
* src/aclocal.m4: Fix autoconf caching for SSL libraries (thanks
|
||||
to Michael Shields) (EJAB-439)
|
||||
|
||||
* src/configure.ac: Don't hardcode gcc and gcc options in
|
||||
Makefiles (thanks to Etan Reisner) (EJAB-436)
|
||||
* src/Makefile.in:
|
||||
* src/ejabberd_zlib/Makefile.in:
|
||||
* src/eldap/Makefile.in:
|
||||
* src/mod_irc/Makefile.in:
|
||||
* src/mod_muc/Makefile.in:
|
||||
* src/mod_proxy65/Makefile.in:
|
||||
* src/mod_pubsub/Makefile.in:
|
||||
* src/odbc/Makefile.in:
|
||||
* src/pam/Makefile.in:
|
||||
* src/stringprep/Makefile.in:
|
||||
* src/tls/Makefile.in:
|
||||
* src/web/Makefile.in:
|
||||
* src/Makefile.in: Likewise
|
||||
* src/ejabberd_zlib/Makefile.in: Likewise
|
||||
* src/eldap/Makefile.in: Likewise
|
||||
* src/mod_irc/Makefile.in: Likewise
|
||||
* src/mod_muc/Makefile.in: Likewise
|
||||
* src/mod_proxy65/Makefile.in: Likewise
|
||||
* src/mod_pubsub/Makefile.in: Likewise
|
||||
* src/odbc/Makefile.in: Likewise
|
||||
* src/pam/Makefile.in: Likewise
|
||||
* src/stringprep/Makefile.in: Likewise
|
||||
* src/tls/Makefile.in: Likewise
|
||||
* src/web/Makefile.in: Likewise
|
||||
|
||||
* src/mod_muc/mod_muc_room.erl: Hide the option 'Make room
|
||||
moderated' because it isn't implemented, and set the default value
|
||||
|
@ -18,7 +18,8 @@
|
||||
send_text/2,
|
||||
send_element/2,
|
||||
socket_type/0,
|
||||
get_presence/1]).
|
||||
get_presence/1,
|
||||
get_subscribed_and_online/1]).
|
||||
|
||||
%% gen_fsm callbacks
|
||||
-export([init/1,
|
||||
@ -39,6 +40,7 @@
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(SETS, gb_sets).
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(state, {socket,
|
||||
sockmod,
|
||||
@ -60,6 +62,7 @@
|
||||
pres_f = ?SETS:new(),
|
||||
pres_a = ?SETS:new(),
|
||||
pres_i = ?SETS:new(),
|
||||
pres_available = ?DICT:new(),
|
||||
pres_last, pres_pri,
|
||||
pres_timestamp,
|
||||
pres_invis = false,
|
||||
@ -173,6 +176,12 @@ init([{SockMod, Socket}, Opts]) ->
|
||||
shaper = Shaper,
|
||||
ip = IP}, ?C2S_OPEN_TIMEOUT}.
|
||||
|
||||
%% Return list of all available resources of contacts,
|
||||
%% in form [{JID, Caps}].
|
||||
get_subscribed_and_online(FsmRef) ->
|
||||
gen_fsm:sync_send_all_state_event(
|
||||
FsmRef, get_subscribed_and_online, 1000).
|
||||
|
||||
|
||||
%%----------------------------------------------------------------------
|
||||
%% Func: StateName/2
|
||||
@ -572,7 +581,7 @@ wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||
case xml:get_subtag(El, "method") of
|
||||
false ->
|
||||
send_element(StateData,
|
||||
{xmlelement, "failure",
|
||||
{xmlelement, "failure",
|
||||
[{"xmlns", ?NS_COMPRESS}],
|
||||
[{xmlelement, "setup-failed", [], []}]}),
|
||||
fsm_next_state(wait_for_feature_request, StateData);
|
||||
@ -964,6 +973,17 @@ handle_sync_event({get_presence}, _From, StateName, StateData) ->
|
||||
Reply = {User, Resource, Show, Status},
|
||||
fsm_reply(Reply, StateName, StateData);
|
||||
|
||||
handle_sync_event(get_subscribed_and_online, _From, StateName, StateData) ->
|
||||
Subscribed = StateData#state.pres_f,
|
||||
Online = StateData#state.pres_available,
|
||||
Pred = fun(User, _Caps) ->
|
||||
?SETS:is_element(jlib:jid_remove_resource(User),
|
||||
Subscribed) orelse
|
||||
?SETS:is_element(User, Subscribed)
|
||||
end,
|
||||
SubscribedAndOnline = ?DICT:filter(Pred, Online),
|
||||
{reply, ?DICT:to_list(SubscribedAndOnline), StateName, StateData};
|
||||
|
||||
handle_sync_event(_Event, _From, StateName, StateData) ->
|
||||
Reply = ok,
|
||||
fsm_reply(Reply, StateName, StateData).
|
||||
@ -1054,32 +1074,42 @@ handle_info({route, From, To, Packet}, StateName, StateData) ->
|
||||
allow ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
LBFrom = jlib:jid_remove_resource(LFrom),
|
||||
%% Note contact availability
|
||||
Caps = mod_caps:read_caps(Els),
|
||||
mod_caps:note_caps(StateData#state.server, From, Caps),
|
||||
NewAvailable = case xml:get_attr_s("type", Attrs) of
|
||||
"unavailable" ->
|
||||
?DICT:erase(LFrom, StateData#state.pres_available);
|
||||
_ ->
|
||||
?DICT:store(LFrom, Caps, StateData#state.pres_available)
|
||||
end,
|
||||
NewStateData = StateData#state{pres_available = NewAvailable},
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_a) orelse
|
||||
LFrom, NewStateData#state.pres_a) orelse
|
||||
?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_a) of
|
||||
LBFrom, NewStateData#state.pres_a) of
|
||||
true ->
|
||||
{true, Attrs, StateData};
|
||||
{true, Attrs, NewStateData};
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LFrom, StateData#state.pres_f) of
|
||||
LFrom, NewStateData#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LFrom,
|
||||
StateData#state.pres_a),
|
||||
NewStateData#state.pres_a),
|
||||
{true, Attrs,
|
||||
StateData#state{pres_a = A}};
|
||||
NewStateData#state{pres_a = A}};
|
||||
false ->
|
||||
case ?SETS:is_element(
|
||||
LBFrom, StateData#state.pres_f) of
|
||||
LBFrom, NewStateData#state.pres_f) of
|
||||
true ->
|
||||
A = ?SETS:add_element(
|
||||
LBFrom,
|
||||
StateData#state.pres_a),
|
||||
NewStateData#state.pres_a),
|
||||
{true, Attrs,
|
||||
StateData#state{pres_a = A}};
|
||||
NewStateData#state{pres_a = A}};
|
||||
false ->
|
||||
{true, Attrs, StateData}
|
||||
{true, Attrs, NewStateData}
|
||||
end
|
||||
end
|
||||
end;
|
||||
|
@ -18,6 +18,7 @@
|
||||
-export([route/3,
|
||||
register_iq_handler/4,
|
||||
register_iq_handler/5,
|
||||
register_iq_response_handler/4,
|
||||
unregister_iq_handler/2,
|
||||
refresh_iq_handlers/0,
|
||||
bounce_resource_packet/3
|
||||
@ -32,6 +33,8 @@
|
||||
|
||||
-record(state, {}).
|
||||
|
||||
-record(iq_response, {id, module, function}).
|
||||
|
||||
-define(IQTABLE, local_iqtable).
|
||||
|
||||
%%====================================================================
|
||||
@ -68,13 +71,38 @@ process_iq(From, To, Packet) ->
|
||||
ejabberd_router:route(To, From, Err)
|
||||
end;
|
||||
reply ->
|
||||
ok;
|
||||
process_iq_reply(From, To, Packet);
|
||||
_ ->
|
||||
Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
|
||||
ejabberd_router:route(To, From, Err),
|
||||
ok
|
||||
end.
|
||||
|
||||
process_iq_reply(From, To, Packet) ->
|
||||
IQ = jlib:iq_query_or_response_info(Packet),
|
||||
#iq{id = ID} = IQ,
|
||||
case catch mnesia:dirty_read(iq_response, ID) of
|
||||
[] ->
|
||||
ok;
|
||||
_ ->
|
||||
F = fun() ->
|
||||
case mnesia:read({iq_response, ID}) of
|
||||
[] ->
|
||||
nothing;
|
||||
[#iq_response{module = Module,
|
||||
function = Function}] ->
|
||||
mnesia:delete({iq_response, ID}),
|
||||
{Module, Function}
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {Module, Function}} ->
|
||||
Module:Function(From, To, IQ);
|
||||
_ ->
|
||||
ok
|
||||
end
|
||||
end.
|
||||
|
||||
route(From, To, Packet) ->
|
||||
case catch do_route(From, To, Packet) of
|
||||
{'EXIT', Reason} ->
|
||||
@ -84,6 +112,9 @@ route(From, To, Packet) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
register_iq_response_handler(Host, ID, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_response_handler, Host, ID, Module, Fun}.
|
||||
|
||||
register_iq_handler(Host, XMLNS, Module, Fun) ->
|
||||
ejabberd_local ! {register_iq_handler, Host, XMLNS, Module, Fun}.
|
||||
|
||||
@ -120,6 +151,9 @@ init([]) ->
|
||||
?MODULE, bounce_resource_packet, 100)
|
||||
end, ?MYHOSTS),
|
||||
catch ets:new(?IQTABLE, [named_table, public]),
|
||||
mnesia:create_table(iq_response,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, iq_response)}]),
|
||||
{ok, #state{}}.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
@ -159,6 +193,9 @@ handle_info({route, From, To, Packet}, State) ->
|
||||
ok
|
||||
end,
|
||||
{noreply, State};
|
||||
handle_info({register_iq_response_handler, _Host, ID, Module, Function}, State) ->
|
||||
mnesia:dirty_write(#iq_response{id = ID, module = Module, function = Function}),
|
||||
{noreply, State};
|
||||
handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) ->
|
||||
ets:insert(?IQTABLE, {{XMLNS, Host}, Module, Function}),
|
||||
catch mod_disco:register_feature(Host, XMLNS),
|
||||
|
@ -28,6 +28,7 @@
|
||||
register_iq_handler/5,
|
||||
unregister_iq_handler/2,
|
||||
ctl_process/2,
|
||||
get_session_pid/3,
|
||||
get_user_ip/3
|
||||
]).
|
||||
|
||||
@ -74,7 +75,7 @@ open_session(SID, User, Server, Resource, IP) ->
|
||||
close_session(SID, User, Server, Resource) ->
|
||||
F = fun() ->
|
||||
mnesia:delete({session, SID})
|
||||
end,
|
||||
end,
|
||||
mnesia:sync_dirty(F),
|
||||
JID = jlib:make_jid(User, Server, Resource),
|
||||
ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver,
|
||||
@ -139,6 +140,15 @@ close_session_unset_presence(SID, User, Server, Resource, Status) ->
|
||||
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
|
||||
[User, Server, Resource, Status]).
|
||||
|
||||
get_session_pid(User, Server, Resource) ->
|
||||
LUser = jlib:nodeprep(User),
|
||||
LServer = jlib:nameprep(Server),
|
||||
LResource = jlib:resourceprep(Resource),
|
||||
USR = {LUser, LServer, LResource},
|
||||
case catch mnesia:dirty_index_read(session, USR, #session.usr) of
|
||||
[#session{sid = {_, Pid}}] -> Pid;
|
||||
_ -> none
|
||||
end.
|
||||
|
||||
dirty_get_sessions_list() ->
|
||||
mnesia:dirty_select(
|
||||
@ -315,7 +325,7 @@ clean_table_from_bad_node(Node) ->
|
||||
lists:foreach(fun(E) ->
|
||||
mnesia:delete({session, E#session.sid})
|
||||
end, Es)
|
||||
end,
|
||||
end,
|
||||
mnesia:sync_dirty(F).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
@ -368,7 +378,6 @@ do_route(From, To, Packet) ->
|
||||
{true, false}
|
||||
end,
|
||||
if Pass ->
|
||||
LFrom = jlib:jid_tolower(From),
|
||||
PResources = get_user_present_resources(
|
||||
LUser, LServer),
|
||||
lists:foreach(
|
||||
@ -377,7 +386,9 @@ do_route(From, To, Packet) ->
|
||||
From,
|
||||
jlib:jid_replace_resource(To, R),
|
||||
Packet)
|
||||
end, PResources);
|
||||
end, PResources),
|
||||
ejabberd_hooks:run(incoming_presence_hook, LServer,
|
||||
[From, To, Packet]);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
@ -649,4 +660,3 @@ update_tables() ->
|
||||
false ->
|
||||
ok
|
||||
end.
|
||||
|
||||
|
72
src/jlib.erl
72
src/jlib.erl
@ -32,6 +32,7 @@
|
||||
jid_replace_resource/2,
|
||||
get_iq_namespace/1,
|
||||
iq_query_info/1,
|
||||
iq_query_or_response_info/1,
|
||||
is_iq_request_type/1,
|
||||
iq_to_xml/1,
|
||||
parse_xdata_submit/1,
|
||||
@ -331,39 +332,66 @@ get_iq_namespace({xmlelement, Name, _Attrs, Els}) when Name == "iq" ->
|
||||
get_iq_namespace(_) ->
|
||||
"".
|
||||
|
||||
iq_query_info({xmlelement, Name, Attrs, Els}) when Name == "iq" ->
|
||||
iq_query_info(El) ->
|
||||
iq_info_internal(El, request).
|
||||
|
||||
iq_query_or_response_info(El) ->
|
||||
iq_info_internal(El, any).
|
||||
|
||||
iq_info_internal({xmlelement, Name, Attrs, Els}, Filter) when Name == "iq" ->
|
||||
%% Filter is either request or any. If it is request, any replies
|
||||
%% are converted to the atom reply.
|
||||
ID = xml:get_attr_s("id", Attrs),
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||
Type1 = case Type of
|
||||
"set" -> set;
|
||||
"get" -> get;
|
||||
"result" -> reply;
|
||||
"error" -> reply;
|
||||
_ -> invalid
|
||||
{Type1, Class} = case Type of
|
||||
"set" -> {set, request};
|
||||
"get" -> {get, request};
|
||||
"result" -> {result, reply};
|
||||
"error" -> {error, reply};
|
||||
_ -> {invalid, invalid}
|
||||
end,
|
||||
if
|
||||
(Type1 /= invalid) and (Type1 /= reply) ->
|
||||
case xml:remove_cdata(Els) of
|
||||
[{xmlelement, Name2, Attrs2, Els2}] ->
|
||||
XMLNS = xml:get_attr_s("xmlns", Attrs2),
|
||||
if
|
||||
XMLNS /= "" ->
|
||||
Type1 == invalid ->
|
||||
invalid;
|
||||
Class == request; Filter == any ->
|
||||
%% The iq record is a bit strange. The sub_el field is an
|
||||
%% XML tuple for requests, but a list of XML tuples for
|
||||
%% responses.
|
||||
FilteredEls = xml:remove_cdata(Els),
|
||||
{XMLNS, SubEl} =
|
||||
case {Class, FilteredEls} of
|
||||
{request, [{xmlelement, _Name2, Attrs2, _Els2}]} ->
|
||||
{xml:get_attr_s("xmlns", Attrs2),
|
||||
hd(FilteredEls)};
|
||||
{reply, _} ->
|
||||
%% Find the namespace of the first non-error
|
||||
%% element, if there is one.
|
||||
NonErrorEls = [El ||
|
||||
{xmlelement, SubName, _, _} = El
|
||||
<- FilteredEls,
|
||||
SubName /= "error"],
|
||||
{case NonErrorEls of
|
||||
[NonErrorEl] -> xml:get_tag_attr_s("xmlns", NonErrorEl);
|
||||
_ -> invalid
|
||||
end,
|
||||
FilteredEls};
|
||||
_ ->
|
||||
{invalid, invalid}
|
||||
end,
|
||||
if XMLNS == "", Class == request ->
|
||||
invalid;
|
||||
true ->
|
||||
#iq{id = ID,
|
||||
type = Type1,
|
||||
xmlns = XMLNS,
|
||||
lang = Lang,
|
||||
sub_el = {xmlelement, Name2, Attrs2, Els2}};
|
||||
true ->
|
||||
invalid
|
||||
sub_el = SubEl}
|
||||
end;
|
||||
_ ->
|
||||
invalid
|
||||
end;
|
||||
true ->
|
||||
Type1
|
||||
Class == reply, Filter /= any ->
|
||||
reply
|
||||
end;
|
||||
iq_query_info(_) ->
|
||||
iq_info_internal(_, _) ->
|
||||
not_iq.
|
||||
|
||||
is_iq_request_type(set) -> true;
|
||||
|
@ -33,6 +33,7 @@
|
||||
-define(NS_PUBSUB_EVENT, "http://jabber.org/protocol/pubsub#event").
|
||||
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
|
||||
-define(NS_PUBSUB_NMI, "http://jabber.org/protocol/pubsub#node-meta-info").
|
||||
-define(NS_PUBSUB_ERRORS,"http://jabber.org/protocol/pubsub#errors").
|
||||
-define(NS_COMMANDS, "http://jabber.org/protocol/commands").
|
||||
-define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams").
|
||||
-define(NS_ADMIN, "http://jabber.org/protocol/admin").
|
||||
@ -55,6 +56,8 @@
|
||||
|
||||
-define(NS_COMPRESS, "http://jabber.org/protocol/compress").
|
||||
|
||||
-define(NS_CAPS, "http://jabber.org/protocol/caps").
|
||||
|
||||
% TODO: remove "code" attribute (currently it used for backward-compatibility)
|
||||
-define(STANZA_ERROR(Code, Type, Condition),
|
||||
{xmlelement, "error",
|
||||
|
259
src/mod_caps.erl
Normal file
259
src/mod_caps.erl
Normal file
@ -0,0 +1,259 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_caps.erl
|
||||
%%% Author : Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%% Purpose : Request and cache Entity Capabilities (XEP-0115)
|
||||
%%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se>
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_caps).
|
||||
-author('henoch@dtek.chalmers.se').
|
||||
|
||||
-behaviour(gen_server).
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-export([read_caps/1,
|
||||
note_caps/3,
|
||||
get_features/2,
|
||||
handle_disco_response/3]).
|
||||
|
||||
%% gen_mod callbacks
|
||||
-export([start/2, start_link/2,
|
||||
stop/1]).
|
||||
|
||||
%% gen_server callbacks
|
||||
-export([init/1,
|
||||
handle_info/2,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
terminate/2,
|
||||
code_change/3
|
||||
]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(PROCNAME, ejabberd_mod_caps).
|
||||
-define(DICT, dict).
|
||||
|
||||
-record(caps, {node, version, exts}).
|
||||
-record(caps_features, {node_pair, features}).
|
||||
-record(state, {host,
|
||||
disco_requests = ?DICT:new(),
|
||||
feature_queries = []}).
|
||||
|
||||
%% read_caps takes a list of XML elements (the child elements of a
|
||||
%% <presence/> stanza) and returns an opaque value representing the
|
||||
%% Entity Capabilities contained therein, or the atom nothing if no
|
||||
%% capabilities are advertised.
|
||||
read_caps([{xmlelement, "c", Attrs, _Els} | Tail]) ->
|
||||
case xml:get_attr_s("xmlns", Attrs) of
|
||||
?NS_CAPS ->
|
||||
Node = xml:get_attr_s("node", Attrs),
|
||||
Version = xml:get_attr_s("ver", Attrs),
|
||||
Exts = string:tokens(xml:get_attr_s("ext", Attrs), " "),
|
||||
#caps{node = Node, version = Version, exts = Exts};
|
||||
_ ->
|
||||
read_caps(Tail)
|
||||
end;
|
||||
read_caps([_ | Tail]) ->
|
||||
read_caps(Tail);
|
||||
read_caps([]) ->
|
||||
nothing.
|
||||
|
||||
%% note_caps should be called to make the module request disco
|
||||
%% information. Host is the host that asks, From is the full JID that
|
||||
%% sent the caps packet, and Caps is what read_caps returned.
|
||||
note_caps(Host, From, Caps) ->
|
||||
case Caps of
|
||||
nothing -> ok;
|
||||
_ ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:cast(Proc, {note_caps, From, Caps})
|
||||
end.
|
||||
|
||||
%% get_features returns a list of features implied by the given caps
|
||||
%% record (as extracted by read_caps). It may block, and may signal a
|
||||
%% timeout error.
|
||||
get_features(Host, Caps) ->
|
||||
case Caps of
|
||||
nothing -> [];
|
||||
#caps{} ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:call(Proc, {get_features, Caps})
|
||||
end.
|
||||
|
||||
start_link(Host, Opts) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []).
|
||||
|
||||
start(Host, Opts) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
ChildSpec =
|
||||
{Proc,
|
||||
{?MODULE, start_link, [Host, Opts]},
|
||||
transient,
|
||||
1000,
|
||||
worker,
|
||||
[?MODULE]},
|
||||
supervisor:start_child(ejabberd_sup, ChildSpec).
|
||||
|
||||
stop(Host) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:call(Proc, stop),
|
||||
supervisor:stop_child(ejabberd_sup, Proc).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
%%====================================================================
|
||||
|
||||
init([Host, _Opts]) ->
|
||||
mnesia:create_table(caps_features,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, caps_features)}]),
|
||||
mnesia:add_table_copy(caps_features, node(), ram_copies),
|
||||
{ok, #state{host = Host}}.
|
||||
|
||||
maybe_get_features(#caps{node = Node, version = Version, exts = Exts}) ->
|
||||
SubNodes = [Version | Exts],
|
||||
F = fun() ->
|
||||
%% Make sure that we have all nodes we need to know.
|
||||
%% If a single one is missing, we wait for more disco
|
||||
%% responses.
|
||||
lists:foldl(fun(SubNode, Acc) ->
|
||||
case Acc of
|
||||
fail -> fail;
|
||||
_ ->
|
||||
case mnesia:read({caps_features, {Node, SubNode}}) of
|
||||
[] -> fail;
|
||||
[#caps_features{features = Features}] -> Features ++ Acc
|
||||
end
|
||||
end
|
||||
end, [], SubNodes)
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, fail} ->
|
||||
wait;
|
||||
{atomic, Features} ->
|
||||
{ok, Features}
|
||||
end.
|
||||
|
||||
timestamp() ->
|
||||
{MegaSecs, Secs, _MicroSecs} = now(),
|
||||
MegaSecs * 1000000 + Secs.
|
||||
|
||||
handle_call({get_features, Caps}, From, State) ->
|
||||
case maybe_get_features(Caps) of
|
||||
{ok, Features} ->
|
||||
{reply, Features, State};
|
||||
wait ->
|
||||
Timeout = timestamp() + 10,
|
||||
FeatureQueries = State#state.feature_queries,
|
||||
NewFeatureQueries = [{From, Caps, Timeout} | FeatureQueries],
|
||||
NewState = State#state{feature_queries = NewFeatureQueries},
|
||||
{noreply, NewState}
|
||||
end;
|
||||
|
||||
handle_call(stop, _From, State) ->
|
||||
{stop, normal, ok, State}.
|
||||
|
||||
handle_cast({note_caps, From,
|
||||
#caps{node = Node, version = Version, exts = Exts}},
|
||||
#state{host = Host, disco_requests = Requests} = State) ->
|
||||
%% XXX: this leads to race conditions where ejabberd will send
|
||||
%% lots of caps disco requests.
|
||||
SubNodes = [Version | Exts],
|
||||
%% Now, find which of these are not already in the database.
|
||||
Fun = fun() ->
|
||||
lists:foldl(fun(SubNode, Acc) ->
|
||||
case mnesia:read({caps_features, {Node, SubNode}}) of
|
||||
[] ->
|
||||
[SubNode | Acc];
|
||||
_ ->
|
||||
Acc
|
||||
end
|
||||
end, [], SubNodes)
|
||||
end,
|
||||
case mnesia:transaction(Fun) of
|
||||
{atomic, Missing} ->
|
||||
%% For each unknown caps "subnode", we send a disco
|
||||
%% request.
|
||||
NewRequests =
|
||||
lists:foldl(
|
||||
fun(SubNode, Dict) ->
|
||||
ID = randoms:get_string(),
|
||||
Stanza =
|
||||
{xmlelement, "iq",
|
||||
[{"type", "get"},
|
||||
{"id", ID}],
|
||||
[{xmlelement, "query",
|
||||
[{"xmlns", ?NS_DISCO_INFO},
|
||||
{"node", Node ++ "#" ++ SubNode}],
|
||||
[]}]},
|
||||
ejabberd_local:register_iq_response_handler
|
||||
(Host, ID, ?MODULE, handle_disco_response),
|
||||
ejabberd_router:route(jlib:make_jid("", Host, ""), From, Stanza),
|
||||
?DICT:store(ID, {Node, SubNode}, Dict)
|
||||
end, Requests, Missing),
|
||||
{noreply, State#state{disco_requests = NewRequests}};
|
||||
Error ->
|
||||
?ERROR_MSG("Transaction failed: ~p", [Error]),
|
||||
{noreply, State}
|
||||
end;
|
||||
handle_cast({disco_response, From, _To,
|
||||
#iq{type = Type, id = ID,
|
||||
sub_el = SubEls}},
|
||||
#state{disco_requests = Requests} = State) ->
|
||||
case {Type, SubEls} of
|
||||
{result, [{xmlelement, "query", Attrs, Els}]} ->
|
||||
case ?DICT:find(ID, Requests) of
|
||||
{ok, {Node, SubNode}} ->
|
||||
Features =
|
||||
lists:flatmap(fun({xmlelement, "feature", FAttrs, _}) ->
|
||||
[xml:get_attr_s("var", FAttrs)];
|
||||
(_) ->
|
||||
[]
|
||||
end, Els),
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
mnesia:write(#caps_features{node_pair = {Node, SubNode},
|
||||
features = Features})
|
||||
end),
|
||||
gen_server:cast(self(), visit_feature_queries);
|
||||
error ->
|
||||
?ERROR_MSG("ID '~s' matches no query", [ID])
|
||||
end;
|
||||
{result, _} ->
|
||||
?ERROR_MSG("Invalid IQ contents from ~s: ~p", [jlib:jid_to_string(From), SubEls]);
|
||||
_ ->
|
||||
%% Can't do anything about errors
|
||||
ok
|
||||
end,
|
||||
NewRequests = ?DICT:erase(ID, Requests),
|
||||
{noreply, State#state{disco_requests = NewRequests}};
|
||||
handle_cast(visit_feature_queries, #state{feature_queries = FeatureQueries} = State) ->
|
||||
Timestamp = timestamp(),
|
||||
NewFeatureQueries =
|
||||
lists:foldl(fun({From, Caps, Timeout}, Acc) ->
|
||||
case maybe_get_features(Caps) of
|
||||
wait when Timeout < Timestamp -> [{From, Caps, Timeout} | Acc];
|
||||
wait -> Acc;
|
||||
{ok, Features} ->
|
||||
gen_server:reply(From, Features),
|
||||
Acc
|
||||
end
|
||||
end, [], FeatureQueries),
|
||||
{noreply, State#state{feature_queries = NewFeatureQueries}}.
|
||||
|
||||
handle_disco_response(From, To, IQ) ->
|
||||
#jid{lserver = Host} = To,
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:cast(Proc, {disco_response, From, To, IQ}).
|
||||
|
||||
handle_info(_Info, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVsn, State, _Extra) ->
|
||||
{ok, State}.
|
@ -15,11 +15,18 @@ OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
# make debug=true to compile Erlang module with debug informations.
|
||||
ifdef debug
|
||||
EFLAGS+=+debug_info
|
||||
EFLAGS+=+debug_info
|
||||
endif
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)/mod_pubsub.beam
|
||||
$(OUTDIR)/gen_pubsub_node.beam \
|
||||
$(OUTDIR)/gen_pubsub_nodetree.beam \
|
||||
$(OUTDIR)/nodetree_default.beam \
|
||||
$(OUTDIR)/nodetree_virtual.beam \
|
||||
$(OUTDIR)/mod_pubsub.beam \
|
||||
$(OUTDIR)/mod_pubsub_old.beam \
|
||||
$(OUTDIR)/node_default.beam \
|
||||
$(OUTDIR)/node_pep.beam
|
||||
|
||||
all: $(OBJS)
|
||||
|
||||
|
@ -5,12 +5,38 @@ OUTDIR = ..
|
||||
EFLAGS = -I .. -pz ..
|
||||
|
||||
OBJS = \
|
||||
$(OUTDIR)\mod_pubsub.beam
|
||||
$(OUTDIR)/gen_pubsub_node.beam \
|
||||
$(OUTDIR)/gen_pubsub_nodetree.beam \
|
||||
$(OUTDIR)/nodetree_default.beam \
|
||||
$(OUTDIR)/nodetree_virtual.beam \
|
||||
$(OUTDIR)/mod_pubsub.beam \
|
||||
$(OUTDIR)/mod_pubsub_old.beam \
|
||||
$(OUTDIR)/node_default.beam \
|
||||
$(OUTDIR)/node_pep.beam
|
||||
|
||||
ALL : $(OBJS)
|
||||
|
||||
CLEAN :
|
||||
-@erase $(OBJS)
|
||||
|
||||
$(OUTDIR)\gen_pubsub_node.beam : gen_pubsub_node.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) gen_pubsub_node.erl
|
||||
|
||||
$(OUTDIR)\gen_pubsub_nodetree.beam : gen_pubsub_nodetree.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) gen_pubsub_nodetree.erl
|
||||
|
||||
$(OUTDIR)\mod_pubsub.beam : mod_pubsub.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) mod_pubsub.erl
|
||||
|
||||
$(OUTDIR)\nodetree_default.beam : nodetree_default.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_default.erl
|
||||
|
||||
$(OUTDIR)\nodetree_virtual.beam : nodetree_virtual.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) nodetree_virtual.erl
|
||||
|
||||
$(OUTDIR)\node_default.beam : node_default.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) node_default.erl
|
||||
|
||||
$(OUTDIR)\node_pep.beam : node_pep.erl
|
||||
erlc -W $(EFLAGS) -o $(OUTDIR) node_pep.erl
|
||||
|
||||
|
55
src/mod_pubsub/gen_pubsub_node.erl
Normal file
55
src/mod_pubsub/gen_pubsub_node.erl
Normal file
@ -0,0 +1,55 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2007, Process-one.
|
||||
%%%
|
||||
%%% @copyright 2006 Process-one
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @private
|
||||
%%% @doc <p>The module <strong>{@module}</strong> defines the PubSub node
|
||||
%%% plugin behaviour. This behaviour is used to check that a PubSub plugin
|
||||
%%% respects the current ejabberd PubSub plugin API.</p>
|
||||
|
||||
-module(gen_pubsub_node).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
%% @spec (Query::atom()) -> Callbacks | atom()
|
||||
%% Callbacks = [{Function,Arity}]
|
||||
%% Function = atom()
|
||||
%% Arity = integer()
|
||||
%% @doc Behaviour definition
|
||||
behaviour_info(callbacks) ->
|
||||
[{init, 3},
|
||||
{terminate, 2},
|
||||
{options, 0},
|
||||
{features, 0},
|
||||
{create_node_permission, 6},
|
||||
{create_node, 3},
|
||||
{delete_node, 2},
|
||||
{purge_node, 3},
|
||||
{subscribe_node, 8},
|
||||
{unsubscribe_node, 5},
|
||||
{publish_item, 7},
|
||||
{delete_item, 4},
|
||||
{remove_extra_items, 4},
|
||||
{get_node_affiliations, 2},
|
||||
{get_entity_affiliations, 2},
|
||||
{get_affiliation, 3},
|
||||
{set_affiliation, 4},
|
||||
{get_node_subscriptions, 2},
|
||||
{get_entity_subscriptions, 2},
|
||||
{get_subscription, 3},
|
||||
{set_subscription, 4},
|
||||
{get_states, 2},
|
||||
{get_state, 3},
|
||||
{set_state, 1},
|
||||
{get_items, 2},
|
||||
{get_item, 3},
|
||||
{set_item, 1}
|
||||
];
|
||||
behaviour_info(_Other) ->
|
||||
undefined.
|
40
src/mod_pubsub/gen_pubsub_nodetree.erl
Normal file
40
src/mod_pubsub/gen_pubsub_nodetree.erl
Normal file
@ -0,0 +1,40 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2006, Process-one.
|
||||
%%%
|
||||
%%% $Id: gen_pubsub_nodetree.erl 100 2007-11-15 13:04:44Z mremond $
|
||||
%%%
|
||||
%%% @copyright 2006 Process-one
|
||||
%%% @author Christophe Romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
%%% @private
|
||||
%%% @doc <p>The module <strong>{@module}</strong> defines the PubSub node
|
||||
%%% tree plugin behaviour. This behaviour is used to check that a PubSub
|
||||
%%% node tree plugin respects the current ejabberd PubSub plugin API.</p>
|
||||
|
||||
-module(gen_pubsub_nodetree).
|
||||
|
||||
-export([behaviour_info/1]).
|
||||
|
||||
%% @spec (Query::atom()) -> Callbacks | atom()
|
||||
%% Callbacks = [{Function,Arity}]
|
||||
%% Function = atom()
|
||||
%% Arity = integer()
|
||||
%% @doc Behaviour definition
|
||||
behaviour_info(callbacks) ->
|
||||
[{init, 3},
|
||||
{terminate, 2},
|
||||
{options, 0},
|
||||
{set_node, 1},
|
||||
{get_node, 2},
|
||||
{get_nodes, 1},
|
||||
{get_subnodes, 2},
|
||||
{get_subnodes_tree, 2},
|
||||
{create_node, 5},
|
||||
{delete_node, 2}
|
||||
];
|
||||
behaviour_info(_Other) ->
|
||||
undefined.
|
File diff suppressed because it is too large
Load Diff
163
src/mod_pubsub/node.template
Normal file
163
src/mod_pubsub/node.template
Normal file
@ -0,0 +1,163 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2007, Process-one.
|
||||
%%%
|
||||
%%% @copyright 2007 Process-one
|
||||
%%% @author Christophe romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
-module(__TO_BE_DEFINED__).
|
||||
-author(__TO_BE_DEFINED__).
|
||||
|
||||
-include("pubsub.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-behaviour(gen_pubsub_node).
|
||||
|
||||
%% Note on function definition
|
||||
%% included is all defined plugin function
|
||||
%% it's possible not to define some function at all
|
||||
%% in that case, warning will be generated at compilation
|
||||
%% and function call will fail,
|
||||
%% then mod_pubsub will call function from node_default
|
||||
%% (this makes code cleaner, but execution a little bit longer)
|
||||
|
||||
%% API definition
|
||||
-export([init/3, terminate/2,
|
||||
options/0, features/0,
|
||||
create_node_permission/6,
|
||||
create_node/3,
|
||||
delete_node/2,
|
||||
purge_node/3,
|
||||
subscribe_node/8,
|
||||
unsubscribe_node/5,
|
||||
publish_item/7,
|
||||
delete_item/4,
|
||||
remove_extra_items/4,
|
||||
get_entity_affiliations/2,
|
||||
get_node_affiliations/2,
|
||||
get_affiliation/3,
|
||||
set_affiliation/4,
|
||||
get_entity_subscriptions/2,
|
||||
get_node_subscriptions/2,
|
||||
get_subscription/3,
|
||||
set_subscription/4,
|
||||
get_states/2,
|
||||
get_state/3,
|
||||
set_state/1,
|
||||
get_items/2,
|
||||
get_item/3,
|
||||
set_item/1
|
||||
]).
|
||||
|
||||
|
||||
init(Host, ServerHost, Opts) ->
|
||||
node_default:init(Host, ServerHost, Opts).
|
||||
|
||||
terminate(Host, ServerHost) ->
|
||||
node_default:terminate(Host, ServerHost).
|
||||
|
||||
options() ->
|
||||
[{node_type, __TO_BE_DEFINED__},
|
||||
{deliver_payloads, true},
|
||||
{notify_config, false},
|
||||
{notify_delete, false},
|
||||
{notify_retract, true},
|
||||
{persist_items, true},
|
||||
{max_items, ?MAXITEMS div 2},
|
||||
{subscribe, true},
|
||||
{access_model, open},
|
||||
{access_roster_groups, []},
|
||||
{publish_model, publishers},
|
||||
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
||||
{send_last_published_item, never},
|
||||
{deliver_notifications, true},
|
||||
{presence_based_delivery, false}].
|
||||
|
||||
features() ->
|
||||
["create-nodes",
|
||||
"delete-nodes",
|
||||
"instant-nodes",
|
||||
"item-ids",
|
||||
"outcast-affiliation",
|
||||
"persistent-items",
|
||||
"publish",
|
||||
"purge-nodes",
|
||||
"retract-items",
|
||||
"retrieve-affiliations",
|
||||
"retrieve-items",
|
||||
"retrieve-subscriptions",
|
||||
"subscribe",
|
||||
"subscription-notifications"
|
||||
].
|
||||
|
||||
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
|
||||
node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
|
||||
|
||||
create_node(Host, Node, Owner) ->
|
||||
node_default:create_node(Host, Node, Owner).
|
||||
|
||||
delete_node(Host, Removed) ->
|
||||
node_default:delete_node(Host, Removed).
|
||||
|
||||
subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) ->
|
||||
node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup).
|
||||
|
||||
unsubscribe_node(Host, Node, Sender, Subscriber, SubID) ->
|
||||
node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID).
|
||||
|
||||
publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) ->
|
||||
node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload).
|
||||
|
||||
remove_extra_items(Host, Node, MaxItems, ItemIds) ->
|
||||
node_default:remove_extra_items(Host, Node, MaxItems, ItemIds).
|
||||
|
||||
delete_item(Host, Node, JID, ItemId) ->
|
||||
node_default:delete_item(Host, Node, JID, ItemId).
|
||||
|
||||
purge_node(Host, Node, Owner) ->
|
||||
node_default:purge_node(Host, Node, Owner).
|
||||
|
||||
get_entity_affiliations(Host, Owner) ->
|
||||
node_default:get_entity_affiliations(Host, Owner).
|
||||
|
||||
get_node_affiliations(Host, Node) ->
|
||||
node_default:get_node_affiliations(Host, Node).
|
||||
|
||||
get_affiliation(Host, Node, Owner) ->
|
||||
node_default:get_affiliation(Host, Node, Owner).
|
||||
|
||||
set_affiliation(Host, Node, Owner, Affiliation) ->
|
||||
node_default:set_affiliation(Host, Node, Owner, Affiliation).
|
||||
|
||||
get_entity_subscriptions(Host, Owner) ->
|
||||
node_default:get_entity_subscriptions(Host, Owner).
|
||||
|
||||
get_node_subscriptions(Host, Node) ->
|
||||
node_default:get_node_subscriptions(Host, Node).
|
||||
|
||||
get_subscription(Host, Node, Owner) ->
|
||||
node_default:get_subscription(Host, Node, Owner).
|
||||
|
||||
set_subscription(Host, Node, Owner, Subscription) ->
|
||||
node_default:set_subscription(Host, Node, Owner, Subscription).
|
||||
|
||||
get_states(Host, Node) ->
|
||||
node_default:get_states(Host, Node).
|
||||
|
||||
get_state(Host, Node, JID) ->
|
||||
node_default:get_state(Host, Node, JID).
|
||||
|
||||
set_state(State) ->
|
||||
node_default:set_state(State).
|
||||
|
||||
get_items(Host, Node) ->
|
||||
node_default:get_items(Host, Node).
|
||||
|
||||
get_item(Host, Node, ItemId) ->
|
||||
node_default:get_items(Host, Node, ItemId).
|
||||
|
||||
set_item(Item) ->
|
||||
node_default:set_item(Item).
|
163
src/mod_pubsub/node_buddy.erl
Normal file
163
src/mod_pubsub/node_buddy.erl
Normal file
@ -0,0 +1,163 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2007, Process-one.
|
||||
%%%
|
||||
%%% @copyright 2007 Process-one
|
||||
%%% @author Christophe romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
-module(node_buddy).
|
||||
-author('christophe.romain@process-one.net').
|
||||
|
||||
-include("pubsub.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-behaviour(gen_pubsub_node).
|
||||
|
||||
%% Note on function definition
|
||||
%% included is all defined plugin function
|
||||
%% it's possible not to define some function at all
|
||||
%% in that case, warning will be generated at compilation
|
||||
%% and function call will fail,
|
||||
%% then mod_pubsub will call function from node_default
|
||||
%% (this makes code cleaner, but execution a little bit longer)
|
||||
|
||||
%% API definition
|
||||
-export([init/3, terminate/2,
|
||||
options/0, features/0,
|
||||
create_node_permission/6,
|
||||
create_node/3,
|
||||
delete_node/2,
|
||||
purge_node/3,
|
||||
subscribe_node/8,
|
||||
unsubscribe_node/5,
|
||||
publish_item/7,
|
||||
delete_item/4,
|
||||
remove_extra_items/4,
|
||||
get_entity_affiliations/2,
|
||||
get_node_affiliations/2,
|
||||
get_affiliation/3,
|
||||
set_affiliation/4,
|
||||
get_entity_subscriptions/2,
|
||||
get_node_subscriptions/2,
|
||||
get_subscription/3,
|
||||
set_subscription/4,
|
||||
get_states/2,
|
||||
get_state/3,
|
||||
set_state/1,
|
||||
get_items/2,
|
||||
get_item/3,
|
||||
set_item/1
|
||||
]).
|
||||
|
||||
|
||||
init(Host, ServerHost, Opts) ->
|
||||
node_default:init(Host, ServerHost, Opts).
|
||||
|
||||
terminate(Host, ServerHost) ->
|
||||
node_default:terminate(Host, ServerHost).
|
||||
|
||||
options() ->
|
||||
[{node_type, buddy},
|
||||
{deliver_payloads, true},
|
||||
{notify_config, false},
|
||||
{notify_delete, false},
|
||||
{notify_retract, true},
|
||||
{persist_items, true},
|
||||
{max_items, ?MAXITEMS div 2},
|
||||
{subscribe, true},
|
||||
{access_model, presence},
|
||||
{access_roster_groups, []},
|
||||
{publish_model, publishers},
|
||||
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
||||
{send_last_published_item, never},
|
||||
{deliver_notifications, true},
|
||||
{presence_based_delivery, false}].
|
||||
|
||||
features() ->
|
||||
["create-nodes",
|
||||
"delete-nodes",
|
||||
"instant-nodes",
|
||||
"item-ids",
|
||||
"outcast-affiliation",
|
||||
"persistent-items",
|
||||
"publish",
|
||||
"purge-nodes",
|
||||
"retract-items",
|
||||
"retrieve-affiliations",
|
||||
"retrieve-items",
|
||||
"retrieve-subscriptions",
|
||||
"subscribe",
|
||||
"subscription-notifications"
|
||||
].
|
||||
|
||||
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
|
||||
node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
|
||||
|
||||
create_node(Host, Node, Owner) ->
|
||||
node_default:create_node(Host, Node, Owner).
|
||||
|
||||
delete_node(Host, Removed) ->
|
||||
node_default:delete_node(Host, Removed).
|
||||
|
||||
subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) ->
|
||||
node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup).
|
||||
|
||||
unsubscribe_node(Host, Node, Sender, Subscriber, SubID) ->
|
||||
node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID).
|
||||
|
||||
publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) ->
|
||||
node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload).
|
||||
|
||||
remove_extra_items(Host, Node, MaxItems, ItemIds) ->
|
||||
node_default:remove_extra_items(Host, Node, MaxItems, ItemIds).
|
||||
|
||||
delete_item(Host, Node, JID, ItemId) ->
|
||||
node_default:delete_item(Host, Node, JID, ItemId).
|
||||
|
||||
purge_node(Host, Node, Owner) ->
|
||||
node_default:purge_node(Host, Node, Owner).
|
||||
|
||||
get_entity_affiliations(Host, Owner) ->
|
||||
node_default:get_entity_affiliations(Host, Owner).
|
||||
|
||||
get_node_affiliations(Host, Node) ->
|
||||
node_default:get_node_affiliations(Host, Node).
|
||||
|
||||
get_affiliation(Host, Node, Owner) ->
|
||||
node_default:get_affiliation(Host, Node, Owner).
|
||||
|
||||
set_affiliation(Host, Node, Owner, Affiliation) ->
|
||||
node_default:set_affiliation(Host, Node, Owner, Affiliation).
|
||||
|
||||
get_entity_subscriptions(Host, Owner) ->
|
||||
node_default:get_entity_subscriptions(Host, Owner).
|
||||
|
||||
get_node_subscriptions(Host, Node) ->
|
||||
node_default:get_node_subscriptions(Host, Node).
|
||||
|
||||
get_subscription(Host, Node, Owner) ->
|
||||
node_default:get_subscription(Host, Node, Owner).
|
||||
|
||||
set_subscription(Host, Node, Owner, Subscription) ->
|
||||
node_default:set_subscription(Host, Node, Owner, Subscription).
|
||||
|
||||
get_states(Host, Node) ->
|
||||
node_default:get_states(Host, Node).
|
||||
|
||||
get_state(Host, Node, JID) ->
|
||||
node_default:get_state(Host, Node, JID).
|
||||
|
||||
set_state(State) ->
|
||||
node_default:set_state(State).
|
||||
|
||||
get_items(Host, Node) ->
|
||||
node_default:get_items(Host, Node).
|
||||
|
||||
get_item(Host, Node, ItemId) ->
|
||||
node_default:get_items(Host, Node, ItemId).
|
||||
|
||||
set_item(Item) ->
|
||||
node_default:set_item(Item).
|
163
src/mod_pubsub/node_club.erl
Normal file
163
src/mod_pubsub/node_club.erl
Normal file
@ -0,0 +1,163 @@
|
||||
%%% ====================================================================
|
||||
%%% This software is copyright 2007, Process-one.
|
||||
%%%
|
||||
%%% @copyright 2007 Process-one
|
||||
%%% @author Christophe romain <christophe.romain@process-one.net>
|
||||
%%% [http://www.process-one.net/]
|
||||
%%% @version {@vsn}, {@date} {@time}
|
||||
%%% @end
|
||||
%%% ====================================================================
|
||||
|
||||
-module(node_club).
|
||||
-author('christophe.romain@process-one.net').
|
||||
|
||||
-include("pubsub.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-behaviour(gen_pubsub_node).
|
||||
|
||||
%% Note on function definition
|
||||
%% included is all defined plugin function
|
||||
%% it's possible not to define some function at all
|
||||
%% in that case, warning will be generated at compilation
|
||||
%% and function call will fail,
|
||||
%% then mod_pubsub will call function from node_default
|
||||
%% (this makes code cleaner, but execution a little bit longer)
|
||||
|
||||
%% API definition
|
||||
-export([init/3, terminate/2,
|
||||
options/0, features/0,
|
||||
create_node_permission/6,
|
||||
create_node/3,
|
||||
delete_node/2,
|
||||
purge_node/3,
|
||||
subscribe_node/8,
|
||||
unsubscribe_node/5,
|
||||
publish_item/7,
|
||||
delete_item/4,
|
||||
remove_extra_items/4,
|
||||
get_entity_affiliations/2,
|
||||
get_node_affiliations/2,
|
||||
get_affiliation/3,
|
||||
set_affiliation/4,
|
||||
get_entity_subscriptions/2,
|
||||
get_node_subscriptions/2,
|
||||
get_subscription/3,
|
||||
set_subscription/4,
|
||||
get_states/2,
|
||||
get_state/3,
|
||||
set_state/1,
|
||||
get_items/2,
|
||||
get_item/3,
|
||||
set_item/1
|
||||
]).
|
||||
|
||||
|
||||
init(Host, ServerHost, Opts) ->
|
||||
node_default:init(Host, ServerHost, Opts).
|
||||
|
||||
terminate(Host, ServerHost) ->
|
||||
node_default:terminate(Host, ServerHost).
|
||||
|
||||
options() ->
|
||||
[{node_type, club},
|
||||
{deliver_payloads, true},
|
||||
{notify_config, false},
|
||||
{notify_delete, false},
|
||||
{notify_retract, true},
|
||||
{persist_items, true},
|
||||
{max_items, ?MAXITEMS div 2},
|
||||
{subscribe, true},
|
||||
{access_model, authorize},
|
||||
{access_roster_groups, []},
|
||||
{publish_model, publishers},
|
||||
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
||||
{send_last_published_item, never},
|
||||
{deliver_notifications, true},
|
||||
{presence_based_delivery, false}].
|
||||
|
||||
features() ->
|
||||
["create-nodes",
|
||||
"delete-nodes",
|
||||
"instant-nodes",
|
||||
"item-ids",
|
||||
"outcast-affiliation",
|
||||
"persistent-items",
|
||||
"publish",
|
||||
"purge-nodes",
|
||||
"retract-items",
|
||||
"retrieve-affiliations",
|
||||
"retrieve-items",
|
||||
"retrieve-subscriptions",
|
||||
"subscribe",
|
||||
"subscription-notifications"
|
||||
].
|
||||
|
||||
create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access) ->
|
||||
node_default:create_node_permission(Host, ServerHost, Node, ParentNode, Owner, Access).
|
||||
|
||||
create_node(Host, Node, Owner) ->
|
||||
node_default:create_node(Host, Node, Owner).
|
||||
|
||||
delete_node(Host, Removed) ->
|
||||
node_default:delete_node(Host, Removed).
|
||||
|
||||
subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup) ->
|
||||
node_default:subscribe_node(Host, Node, Sender, Subscriber, AccessModel, SendLast, PresenceSubscription, RosterGroup).
|
||||
|
||||
unsubscribe_node(Host, Node, Sender, Subscriber, SubID) ->
|
||||
node_default:unsubscribe_node(Host, Node, Sender, Subscriber, SubID).
|
||||
|
||||
publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload) ->
|
||||
node_default:publish_item(Host, Node, Publisher, Model, MaxItems, ItemId, Payload).
|
||||
|
||||
remove_extra_items(Host, Node, MaxItems, ItemIds) ->
|
||||
node_default:remove_extra_items(Host, Node, MaxItems, ItemIds).
|
||||
|
||||
delete_item(Host, Node, JID, ItemId) ->
|
||||
node_default:delete_item(Host, Node, JID, ItemId).
|
||||
|
||||
purge_node(Host, Node, Owner) ->
|
||||
node_default:purge_node(Host, Node, Owner).
|
||||
|
||||
get_entity_affiliations(Host, Owner) ->
|
||||
node_default:get_entity_affiliations(Host, Owner).
|
||||
|
||||
get_node_affiliations(Host, Node) ->
|
||||
node_default:get_node_affiliations(Host, Node).
|
||||
|
||||
get_affiliation(Host, Node, Owner) ->
|
||||
node_default:get_affiliation(Host, Node, Owner).
|
||||
|
||||
set_affiliation(Host, Node, Owner, Affiliation) ->
|
||||
node_default:set_affiliation(Host, Node, Owner, Affiliation).
|
||||
|
||||
get_entity_subscriptions(Host, Owner) ->
|
||||
node_default:get_entity_subscriptions(Host, Owner).
|
||||
|
||||
get_node_subscriptions(Host, Node) ->
|
||||
node_default:get_node_subscriptions(Host, Node).
|
||||
|
||||
get_subscription(Host, Node, Owner) ->
|
||||
node_default:get_subscription(Host, Node, Owner).
|
||||
|
||||
set_subscription(Host, Node, Owner, Subscription) ->
|
||||
node_default:set_subscription(Host, Node, Owner, Subscription).
|
||||
|
||||
get_states(Host, Node) ->
|
||||
node_default:get_states(Host, Node).
|
||||
|
||||
get_state(Host, Node, JID) ->
|
||||
node_default:get_state(Host, Node, JID).
|
||||
|
||||
set_state(State) ->
|
||||
node_default:set_state(State).
|
||||
|
||||
get_items(Host, Node) ->
|
||||
node_default:get_items(Host, Node).
|
||||
|
||||
get_item(Host, Node, ItemId) ->
|
||||
node_default:get_items(Host, Node, ItemId).
|
||||
|
||||
set_item(Item) ->
|
||||
node_default:set_item(Item).
|
712
712