25
1
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:
Alexey Shchepin 2007-12-01 05:16:30 +00:00
parent e4cf286aa2
commit c3c782d882
23 changed files with 5233 additions and 1346 deletions

View File

@ -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

View File

@ -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;

View File

@ -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),

View File

@ -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.

View File

@ -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;

View File

@ -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
View 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}.

View File

@ -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)

View File

@ -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

View 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.

View 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

View 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).

View 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).

View 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