2003-07-08 22:11:27 +02:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% File : mod_pubsub.erl
|
|
|
|
%%% Author : Alexey Shchepin <alexey@sevcom.net>
|
|
|
|
%%% Purpose : Pub/sub support (JEP-0060)
|
|
|
|
%%% Created : 4 Jul 2003 by Alexey Shchepin <alexey@sevcom.net>
|
|
|
|
%%% Id : $Id$
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
-module(mod_pubsub).
|
|
|
|
-author('alexey@sevcom.net').
|
|
|
|
-vsn('$Revision$ ').
|
|
|
|
|
|
|
|
-behaviour(gen_mod).
|
|
|
|
|
|
|
|
-export([start/1,
|
2003-07-20 22:35:35 +02:00
|
|
|
init/3,
|
|
|
|
loop/2,
|
2003-07-08 22:11:27 +02:00
|
|
|
stop/0,
|
2003-07-20 22:35:35 +02:00
|
|
|
system_continue/3,
|
2003-07-21 22:01:22 +02:00
|
|
|
system_terminate/4,
|
2003-07-20 22:35:35 +02:00
|
|
|
system_code_change/4]).
|
2003-07-08 22:11:27 +02:00
|
|
|
|
|
|
|
-include("ejabberd.hrl").
|
|
|
|
-include("jlib.hrl").
|
|
|
|
|
|
|
|
-define(DICT, dict).
|
|
|
|
-define(MAXITEMS, 10).
|
|
|
|
|
2005-04-17 20:08:34 +02:00
|
|
|
-record(pubsub_node, {host_node, host_parent, info}).
|
2003-07-08 22:11:27 +02:00
|
|
|
-record(nodeinfo, {items = [],
|
2003-07-09 20:58:43 +02:00
|
|
|
options = [],
|
2003-07-08 22:11:27 +02:00
|
|
|
entities = ?DICT:new()
|
|
|
|
}).
|
|
|
|
-record(entity, {affiliation = none,
|
|
|
|
subscription = none}).
|
|
|
|
-record(item, {id, publisher, payload}).
|
|
|
|
|
|
|
|
|
|
|
|
start(Opts) ->
|
|
|
|
mnesia:create_table(pubsub_node,
|
|
|
|
[{disc_only_copies, [node()]},
|
|
|
|
{attributes, record_info(fields, pubsub_node)}]),
|
2005-04-17 20:08:34 +02:00
|
|
|
Hosts = gen_mod:get_hosts(Opts, "pubsub."),
|
|
|
|
Host = hd(Hosts),
|
|
|
|
update_table(Host),
|
|
|
|
mnesia:add_table_index(pubsub_node, host_parent),
|
|
|
|
ServedHosts = gen_mod:get_opt(served_hosts, Opts, []),
|
2003-07-20 22:35:35 +02:00
|
|
|
register(ejabberd_mod_pubsub,
|
2005-04-17 20:08:34 +02:00
|
|
|
proc_lib:spawn_link(?MODULE, init, [Hosts, ServedHosts, self()])).
|
2003-07-08 22:11:27 +02:00
|
|
|
|
|
|
|
|
2003-10-07 22:31:44 +02:00
|
|
|
-define(MYJID, #jid{user = "", server = Host, resource = "",
|
|
|
|
luser = "", lserver = Host, lresource = ""}).
|
2003-07-08 22:11:27 +02:00
|
|
|
|
2005-04-17 20:08:34 +02:00
|
|
|
init(Hosts, ServedHosts, Parent) ->
|
|
|
|
ejabberd_router:register_routes(Hosts),
|
|
|
|
lists:foreach(
|
|
|
|
fun(Host) ->
|
|
|
|
create_new_node(Host, ["pubsub"], ?MYJID),
|
|
|
|
create_new_node(Host, ["pubsub", "nodes"], ?MYJID),
|
|
|
|
create_new_node(Host, ["home"], ?MYJID),
|
|
|
|
create_new_node(Host, ["home", find_my_host(Host)], ?MYJID),
|
|
|
|
lists:foreach(fun(H) ->
|
|
|
|
create_new_node(Host, ["home", H], ?MYJID)
|
|
|
|
end, ServedHosts)
|
|
|
|
end, Hosts),
|
|
|
|
loop(Hosts, Parent).
|
|
|
|
|
|
|
|
loop(Hosts, Parent) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
receive
|
|
|
|
{route, From, To, Packet} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case catch do_route(To#jid.lserver, From, To, Packet) of
|
2003-07-08 22:11:27 +02:00
|
|
|
{'EXIT', Reason} ->
|
|
|
|
?ERROR_MSG("~p", [Reason]);
|
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end,
|
2005-04-17 20:08:34 +02:00
|
|
|
loop(Hosts, Parent);
|
2003-07-08 22:11:27 +02:00
|
|
|
{room_destroyed, Room} ->
|
|
|
|
ets:delete(muc_online_room, Room),
|
2005-04-17 20:08:34 +02:00
|
|
|
loop(Hosts, Parent);
|
2003-07-08 22:11:27 +02:00
|
|
|
stop ->
|
2005-05-16 01:03:25 +02:00
|
|
|
ejabberd_router:unregister_routes(Hosts),
|
2003-07-08 22:11:27 +02:00
|
|
|
ok;
|
2003-07-09 20:58:43 +02:00
|
|
|
reload ->
|
2005-04-17 20:08:34 +02:00
|
|
|
?MODULE:loop(Hosts, Parent);
|
2003-07-20 22:35:35 +02:00
|
|
|
{system, From, Request} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
sys:handle_system_msg(Request, From, Parent, ?MODULE, [], Hosts);
|
2003-07-08 22:11:27 +02:00
|
|
|
_ ->
|
2005-04-17 20:08:34 +02:00
|
|
|
loop(Hosts, Parent)
|
2003-07-08 22:11:27 +02:00
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
do_route(Host, From, To, Packet) ->
|
|
|
|
{xmlelement, Name, Attrs, Els} = Packet,
|
|
|
|
case To of
|
2003-10-07 22:31:44 +02:00
|
|
|
#jid{luser = "", lresource = ""} ->
|
2003-07-08 22:11:27 +02:00
|
|
|
case Name of
|
|
|
|
"iq" ->
|
|
|
|
case jlib:iq_query_info(Packet) of
|
2003-12-17 21:13:21 +01:00
|
|
|
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
|
|
|
sub_el = SubEl} = IQ ->
|
2003-07-08 22:11:27 +02:00
|
|
|
{xmlelement, _, QAttrs, _} = SubEl,
|
|
|
|
Node = xml:get_attr_s("node", QAttrs),
|
2003-12-17 21:13:21 +01:00
|
|
|
Res = IQ#iq{type = result,
|
|
|
|
sub_el = [{xmlelement, "query",
|
|
|
|
QAttrs,
|
|
|
|
iq_disco_info(Node)}]},
|
2003-07-08 22:11:27 +02:00
|
|
|
ejabberd_router:route(To,
|
|
|
|
From,
|
|
|
|
jlib:iq_to_xml(Res));
|
2003-12-17 21:13:21 +01:00
|
|
|
#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS,
|
|
|
|
sub_el = SubEl} = IQ ->
|
2003-07-08 22:11:27 +02:00
|
|
|
{xmlelement, _, QAttrs, _} = SubEl,
|
|
|
|
Node = xml:get_attr_s("node", QAttrs),
|
|
|
|
Res =
|
|
|
|
case iq_disco_items(Host, From, Node) of
|
|
|
|
{result, IQRes} ->
|
|
|
|
jlib:iq_to_xml(
|
2003-12-17 21:13:21 +01:00
|
|
|
IQ#iq{type = result,
|
|
|
|
sub_el = [{xmlelement, "query",
|
|
|
|
QAttrs,
|
|
|
|
IQRes}]});
|
2003-07-08 22:11:27 +02:00
|
|
|
{error, Error} ->
|
|
|
|
jlib:make_error_reply(
|
|
|
|
Packet, Error)
|
|
|
|
end,
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(To, From, Res);
|
2004-02-26 23:00:04 +01:00
|
|
|
%#iq{type = get, xmlns = ?NS_REGISTER = XMLNS,
|
|
|
|
% lang = Lang, sub_el = SubEl} = IQ ->
|
|
|
|
% Res = IQ#iq{type = result,
|
|
|
|
% sub_el = [{xmlelement, "query",
|
|
|
|
% [{"xmlns", XMLNS}],
|
|
|
|
% iq_get_register_info(
|
|
|
|
% From, Lang)}]},
|
2003-07-08 22:11:27 +02:00
|
|
|
% ejabberd_router:route(To,
|
|
|
|
% From,
|
|
|
|
% jlib:iq_to_xml(Res));
|
2004-02-26 23:00:04 +01:00
|
|
|
%#iq{type = set, xmlns = ?NS_REGISTER = XMLNS,
|
|
|
|
% sub_el = SubEl} = IQ ->
|
2003-07-08 22:11:27 +02:00
|
|
|
% case process_iq_register_set(From, SubEl) of
|
|
|
|
% {result, IQRes} ->
|
2004-02-26 23:00:04 +01:00
|
|
|
% Res = IQ#iq{type = result,
|
|
|
|
% sub_el = [{xmlelement, "query",
|
|
|
|
% [{"xmlns", XMLNS}],
|
|
|
|
% IQRes}]},
|
2003-07-08 22:11:27 +02:00
|
|
|
% ejabberd_router:route(
|
|
|
|
% To, From, jlib:iq_to_xml(Res));
|
|
|
|
% {error, Error} ->
|
|
|
|
% Err = jlib:make_error_reply(
|
|
|
|
% Packet, Error),
|
|
|
|
% ejabberd_router:route(
|
|
|
|
% To, From, Err)
|
|
|
|
% end;
|
2003-12-17 21:13:21 +01:00
|
|
|
#iq{type = Type, xmlns = ?NS_PUBSUB = XMLNS,
|
|
|
|
sub_el = SubEl} = IQ ->
|
2003-07-08 22:11:27 +02:00
|
|
|
Res =
|
|
|
|
case iq_pubsub(Host, From, Type, SubEl) of
|
|
|
|
{result, IQRes} ->
|
|
|
|
jlib:iq_to_xml(
|
2003-12-17 21:13:21 +01:00
|
|
|
IQ#iq{type = result,
|
|
|
|
sub_el = IQRes});
|
2003-07-08 22:11:27 +02:00
|
|
|
{error, Error} ->
|
|
|
|
jlib:make_error_reply(
|
|
|
|
Packet, Error)
|
|
|
|
end,
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(To, From, Res);
|
2003-12-17 21:13:21 +01:00
|
|
|
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
2004-02-26 23:00:04 +01:00
|
|
|
lang = Lang, sub_el = SubEl} = IQ ->
|
2003-12-17 21:13:21 +01:00
|
|
|
Res = IQ#iq{type = result,
|
2004-03-02 22:16:55 +01:00
|
|
|
sub_el = [{xmlelement, "vCard",
|
2003-12-17 21:13:21 +01:00
|
|
|
[{"xmlns", XMLNS}],
|
|
|
|
iq_get_vcard(Lang)}]},
|
2003-07-08 22:11:27 +02:00
|
|
|
ejabberd_router:route(To,
|
|
|
|
From,
|
|
|
|
jlib:iq_to_xml(Res));
|
2003-12-17 21:13:21 +01:00
|
|
|
#iq{} ->
|
2003-07-08 22:11:27 +02:00
|
|
|
Err = jlib:make_error_reply(
|
|
|
|
Packet,
|
|
|
|
?ERR_FEATURE_NOT_IMPLEMENTED),
|
2003-12-17 21:13:21 +01:00
|
|
|
ejabberd_router:route(To, From, Err);
|
|
|
|
_ ->
|
|
|
|
ok
|
2003-07-08 22:11:27 +02:00
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
ok
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
case xml:get_attr_s("type", Attrs) of
|
|
|
|
"error" ->
|
|
|
|
ok;
|
|
|
|
"result" ->
|
|
|
|
ok;
|
|
|
|
_ ->
|
|
|
|
Err = jlib:make_error_reply(
|
|
|
|
Packet, ?ERR_ITEM_NOT_FOUND),
|
|
|
|
ejabberd_router:route(To, From, Err)
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
stop() ->
|
|
|
|
ejabberd_mod_pubsub ! stop,
|
|
|
|
ok.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
node_to_string(Node) ->
|
|
|
|
string:strip(lists:flatten(lists:map(fun(S) -> [S, "/"] end, Node)),
|
|
|
|
right, $/).
|
|
|
|
|
|
|
|
|
|
|
|
iq_disco_info(SNode) ->
|
|
|
|
Node = string:tokens(SNode, "/"),
|
|
|
|
case Node of
|
|
|
|
[] ->
|
|
|
|
[{xmlelement, "identity",
|
2004-02-10 21:50:41 +01:00
|
|
|
[{"category", "pubsub"},
|
|
|
|
{"type", "generic"},
|
2003-07-08 22:11:27 +02:00
|
|
|
{"name", "ejabberd/mod_pubsub"}], []},
|
|
|
|
%{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
|
|
|
{xmlelement, "feature", [{"var", ?NS_PUBSUB}], []},
|
|
|
|
{xmlelement, "feature", [{"var", ?NS_PUBSUB_EVENT}], []},
|
|
|
|
{xmlelement, "feature", [{"var", ?NS_PUBSUB_OWNER}], []},
|
|
|
|
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}];
|
|
|
|
_ ->
|
|
|
|
% TODO
|
|
|
|
[]
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
iq_disco_items(Host, From, SNode) ->
|
|
|
|
Node = string:tokens(SNode, "/"),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
SubNodes = mnesia:index_read(pubsub_node,
|
2005-04-17 20:08:34 +02:00
|
|
|
{Host, Node},
|
|
|
|
#pubsub_node.host_parent),
|
2003-07-08 22:11:27 +02:00
|
|
|
SubItems =
|
2005-04-17 20:08:34 +02:00
|
|
|
lists:map(fun(#pubsub_node{host_node = {_, N}}) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
SN = node_to_string(N),
|
|
|
|
{xmlelement, "item",
|
|
|
|
[{"jid", Host},
|
|
|
|
{"node", SN},
|
|
|
|
{"name", lists:last(N)}], []}
|
|
|
|
end, SubNodes),
|
|
|
|
SN = node_to_string(Node),
|
|
|
|
Items =
|
|
|
|
lists:map(fun(#item{id = Name}) ->
|
|
|
|
{xmlelement, "item",
|
|
|
|
[{"jid", Host},
|
|
|
|
{"node", SN ++ "!" ++ Name},
|
|
|
|
{"name", Name}], []}
|
|
|
|
end, Info#nodeinfo.items),
|
|
|
|
SubItems ++ Items;
|
|
|
|
[] ->
|
|
|
|
case Node of
|
|
|
|
[] ->
|
|
|
|
SubNodes = mnesia:index_read(
|
|
|
|
pubsub_node,
|
2005-04-17 20:08:34 +02:00
|
|
|
{Host, Node},
|
|
|
|
#pubsub_node.host_parent),
|
2003-07-08 22:11:27 +02:00
|
|
|
lists:map(
|
2005-04-17 20:08:34 +02:00
|
|
|
fun(#pubsub_node{host_node = {_, N}}) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
SN = node_to_string(N),
|
|
|
|
{xmlelement, "item",
|
|
|
|
[{"jid", Host},
|
|
|
|
{"node", SN},
|
|
|
|
{"name", lists:last(N)}],
|
|
|
|
[]}
|
|
|
|
end, SubNodes) ;
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, Res} ->
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
% TODO
|
|
|
|
%-define(XFIELD(Type, Label, Var, Val),
|
|
|
|
% {xmlelement, "field", [{"type", Type},
|
|
|
|
% {"label", translate:translate(Lang, Label)},
|
|
|
|
% {"var", Var}],
|
|
|
|
% [{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
|
|
|
%
|
|
|
|
%iq_get_register_info(From, Lang) ->
|
|
|
|
% {LUser, LServer, _} = jlib:jid_tolower(From),
|
|
|
|
% LUS = {LUser, LServer},
|
|
|
|
% Nick = case catch mnesia:dirty_read(muc_registered, LUS) of
|
|
|
|
% {'EXIT', Reason} ->
|
|
|
|
% "";
|
|
|
|
% [] ->
|
|
|
|
% "";
|
|
|
|
% [#muc_registered{nick = N}] ->
|
|
|
|
% N
|
|
|
|
% end,
|
|
|
|
% [{xmlelement, "instructions", [],
|
|
|
|
% [{xmlcdata, translate:translate(
|
|
|
|
% Lang, "You need a x:data capable client to register.")}]},
|
|
|
|
% {xmlelement, "x",
|
|
|
|
% [{"xmlns", ?NS_XDATA}],
|
|
|
|
% [{xmlelement, "title", [],
|
|
|
|
% [{xmlcdata,
|
|
|
|
% translate:translate(
|
|
|
|
% Lang, "Nick Registration")}]},
|
|
|
|
% {xmlelement, "instructions", [],
|
|
|
|
% [{xmlcdata,
|
|
|
|
% translate:translate(
|
|
|
|
% Lang, "Enter nick you want to register.")}]},
|
|
|
|
% ?XFIELD("text-single", "Nick", "nick", Nick)]}].
|
|
|
|
%
|
|
|
|
%iq_set_register_info(From, XData) ->
|
|
|
|
% {LUser, LServer, _} = jlib:jid_tolower(From),
|
|
|
|
% LUS = {LUser, LServer},
|
|
|
|
% case lists:keysearch("nick", 1, XData) of
|
|
|
|
% false ->
|
|
|
|
% {error, ?ERR_BAD_REQUEST};
|
|
|
|
% {value, {_, [Nick]}} ->
|
|
|
|
% F = fun() ->
|
|
|
|
% case Nick of
|
|
|
|
% "" ->
|
|
|
|
% mnesia:delete({muc_registered, LUS}),
|
|
|
|
% ok;
|
|
|
|
% _ ->
|
|
|
|
% Allow = case mnesia:index_read(
|
|
|
|
% muc_registered,
|
|
|
|
% Nick,
|
|
|
|
% #muc_registered.nick) of
|
|
|
|
% [] ->
|
|
|
|
% true;
|
|
|
|
% [#muc_registered{user = U}] ->
|
|
|
|
% U == LUS
|
|
|
|
% end,
|
|
|
|
% if
|
|
|
|
% Allow ->
|
|
|
|
% mnesia:write(
|
|
|
|
% #muc_registered{user = LUS,
|
|
|
|
% nick = Nick}),
|
|
|
|
% ok;
|
|
|
|
% true ->
|
|
|
|
% false
|
|
|
|
% end
|
|
|
|
% end
|
|
|
|
% end,
|
|
|
|
% case mnesia:transaction(F) of
|
|
|
|
% {atomic, ok} ->
|
|
|
|
% {result, []};
|
|
|
|
% {atomic, false} ->
|
|
|
|
% {error, ?ERR_NOT_ALLOWED};
|
|
|
|
% _ ->
|
|
|
|
% {error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
% end
|
|
|
|
% end.
|
|
|
|
%
|
|
|
|
%process_iq_register_set(From, SubEl) ->
|
|
|
|
% {xmlelement, Name, Attrs, Els} = SubEl,
|
|
|
|
% case xml:remove_cdata(Els) of
|
|
|
|
% [{xmlelement, "x", Attrs1, Els1} = XEl] ->
|
|
|
|
% case {xml:get_tag_attr_s("xmlns", XEl),
|
|
|
|
% xml:get_tag_attr_s("type", XEl)} of
|
|
|
|
% {?NS_XDATA, "cancel"} ->
|
|
|
|
% {result, []};
|
|
|
|
% {?NS_XDATA, "submit"} ->
|
|
|
|
% XData = jlib:parse_xdata_submit(XEl),
|
|
|
|
% case XData of
|
|
|
|
% invalid ->
|
|
|
|
% {error, ?ERR_BAD_REQUEST};
|
|
|
|
% _ ->
|
|
|
|
% iq_set_register_info(From, XData)
|
|
|
|
% end;
|
|
|
|
% _ ->
|
|
|
|
% {error, ?ERR_BAD_REQUEST}
|
|
|
|
% end;
|
|
|
|
% _ ->
|
|
|
|
% {error, ?ERR_BAD_REQUEST}
|
|
|
|
% end.
|
|
|
|
|
|
|
|
iq_get_vcard(Lang) ->
|
|
|
|
[{xmlelement, "FN", [],
|
|
|
|
[{xmlcdata, "ejabberd/mod_pubsub"}]},
|
|
|
|
{xmlelement, "URL", [],
|
|
|
|
[{xmlcdata,
|
|
|
|
"http://ejabberd.jabberstudio.org/"}]},
|
|
|
|
{xmlelement, "DESC", [],
|
2004-02-26 23:00:04 +01:00
|
|
|
[{xmlcdata, translate:translate(
|
|
|
|
Lang,
|
|
|
|
"ejabberd pub/sub module\n"
|
2005-01-14 00:04:47 +01:00
|
|
|
"Copyright (c) 2003-2005 Alexey Shchepin")}]}].
|
2003-07-08 22:11:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
iq_pubsub(Host, From, Type, SubEl) ->
|
|
|
|
{xmlelement, _, _, SubEls} = SubEl,
|
|
|
|
case xml:remove_cdata(SubEls) of
|
|
|
|
[{xmlelement, Name, Attrs, Els}] ->
|
|
|
|
SNode = xml:get_attr_s("node", Attrs),
|
|
|
|
Node = string:tokens(SNode, "/"),
|
|
|
|
case {Type, Name} of
|
|
|
|
{set, "create"} ->
|
2003-07-09 20:58:43 +02:00
|
|
|
create_new_node(Host, Node, From);
|
2003-07-08 22:11:27 +02:00
|
|
|
{set, "publish"} ->
|
|
|
|
case xml:remove_cdata(Els) of
|
|
|
|
[{xmlelement, "item", ItemAttrs, Payload}] ->
|
|
|
|
ItemID = xml:get_attr_s("id", ItemAttrs),
|
|
|
|
publish_item(Host, From, Node, ItemID, Payload);
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_BAD_REQUEST}
|
|
|
|
end;
|
|
|
|
{set, "retract"} ->
|
|
|
|
case xml:remove_cdata(Els) of
|
|
|
|
[{xmlelement, "item", ItemAttrs, _}] ->
|
|
|
|
ItemID = xml:get_attr_s("id", ItemAttrs),
|
|
|
|
delete_item(Host, From, Node, ItemID);
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_BAD_REQUEST}
|
|
|
|
end;
|
|
|
|
{set, "subscribe"} ->
|
|
|
|
JID = xml:get_attr_s("jid", Attrs),
|
|
|
|
subscribe_node(Host, From, JID, Node);
|
|
|
|
{set, "unsubscribe"} ->
|
|
|
|
JID = xml:get_attr_s("jid", Attrs),
|
|
|
|
unsubscribe_node(Host, From, JID, Node);
|
|
|
|
{get, "items"} ->
|
|
|
|
MaxItems = xml:get_attr_s("max_items", Attrs),
|
|
|
|
get_items(Host, From, Node, MaxItems);
|
|
|
|
{set, "delete"} ->
|
|
|
|
delete_node(Host, From, Node);
|
|
|
|
{set, "purge"} ->
|
|
|
|
purge_node(Host, From, Node);
|
|
|
|
{get, "entities"} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
get_entities(Host, From, Node);
|
2003-07-08 22:11:27 +02:00
|
|
|
{set, "entities"} ->
|
2005-04-17 20:08:34 +02:00
|
|
|
set_entities(Host, From, Node, xml:remove_cdata(Els));
|
2003-07-09 20:58:43 +02:00
|
|
|
%{get, "configure"} ->
|
|
|
|
% get_node_config(From, Node);
|
2003-07-08 22:11:27 +02:00
|
|
|
_ ->
|
|
|
|
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_BAD_REQUEST}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2003-07-09 20:58:43 +02:00
|
|
|
-define(XFIELD(Type, Label, Var, Val),
|
|
|
|
{xmlelement, "field", [{"type", Type},
|
|
|
|
{"label", translate:translate(Lang, Label)},
|
|
|
|
{"var", Var}],
|
|
|
|
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
2003-07-08 22:11:27 +02:00
|
|
|
|
2003-07-09 20:58:43 +02:00
|
|
|
|
2004-09-17 21:52:59 +02:00
|
|
|
%% Create new pubsub nodes
|
|
|
|
%% This function is used during init to create the first bootstrap nodes
|
2003-07-09 20:58:43 +02:00
|
|
|
create_new_node(Host, Node, Owner) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
case Node of
|
|
|
|
[] ->
|
2003-07-20 22:35:35 +02:00
|
|
|
{LOU, LOS, _} = jlib:jid_tolower(Owner),
|
2003-07-21 22:01:22 +02:00
|
|
|
HomeNode = ["home", LOS, LOU],
|
2003-07-27 21:56:16 +02:00
|
|
|
create_new_node(Host, HomeNode, Owner),
|
|
|
|
NewNode = ["home", LOS, LOU, randoms:get_string()],
|
|
|
|
create_new_node(Host, NewNode, Owner);
|
2003-07-08 22:11:27 +02:00
|
|
|
_ ->
|
|
|
|
LOwner = jlib:jid_tolower(jlib:jid_remove_resource(Owner)),
|
|
|
|
Parent = lists:sublist(Node, length(Node) - 1),
|
|
|
|
F = fun() ->
|
|
|
|
ParentExists = (Parent == []) orelse
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Parent}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[_] ->
|
|
|
|
true;
|
|
|
|
[] ->
|
|
|
|
false
|
|
|
|
end,
|
|
|
|
case ParentExists of
|
|
|
|
false ->
|
|
|
|
{error, ?ERR_CONFLICT};
|
|
|
|
_ ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[_] ->
|
|
|
|
{error, ?ERR_CONFLICT};
|
|
|
|
[] ->
|
|
|
|
Entities =
|
|
|
|
?DICT:store(
|
|
|
|
LOwner,
|
|
|
|
#entity{affiliation = owner,
|
|
|
|
subscription = none},
|
|
|
|
?DICT:new()),
|
|
|
|
mnesia:write(
|
2005-04-17 20:08:34 +02:00
|
|
|
#pubsub_node{host_node = {Host, Node},
|
|
|
|
host_parent = {Host, Parent},
|
2003-07-08 22:11:27 +02:00
|
|
|
info = #nodeinfo{
|
|
|
|
entities = Entities}}),
|
|
|
|
ok
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end,
|
2003-07-09 20:58:43 +02:00
|
|
|
case check_create_permission(Host, Node, Owner) of
|
2003-07-08 22:11:27 +02:00
|
|
|
true ->
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, ok} ->
|
2003-07-09 20:58:43 +02:00
|
|
|
Lang = "",
|
|
|
|
broadcast_publish_item(
|
2003-07-20 22:35:35 +02:00
|
|
|
Host, ["pubsub", "nodes"], node_to_string(Node),
|
2003-07-09 20:58:43 +02:00
|
|
|
[{xmlelement, "x",
|
2003-07-20 22:35:35 +02:00
|
|
|
[{"xmlns", ?NS_XDATA},
|
2003-07-09 20:58:43 +02:00
|
|
|
{"type", "result"}],
|
|
|
|
[?XFIELD("hidden", "", "FORM_TYPE",
|
|
|
|
?NS_PUBSUB_NMI),
|
|
|
|
?XFIELD("jid-single", "Node Creator",
|
|
|
|
"creator",
|
|
|
|
jlib:jid_to_string(LOwner))]}]),
|
2003-07-20 22:35:35 +02:00
|
|
|
{result,
|
|
|
|
[{xmlelement, "pubsub",
|
|
|
|
[{"xmlns", ?NS_PUBSUB}],
|
|
|
|
[{xmlelement, "create",
|
|
|
|
[{"node", node_to_string(Node)}], []}]}]};
|
2003-07-08 22:11:27 +02:00
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
publish_item(Host, JID, Node, ItemID, Payload) ->
|
|
|
|
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
Affiliation = get_affiliation(Info, Publisher),
|
|
|
|
if
|
|
|
|
(Affiliation == owner) or
|
|
|
|
(Affiliation == publisher) ->
|
|
|
|
NewInfo =
|
|
|
|
insert_item(Info, ItemID,
|
|
|
|
Publisher, Payload),
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, []};
|
|
|
|
true ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, Res}} ->
|
|
|
|
broadcast_publish_item(Host, Node, ItemID, Payload),
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
delete_item(Host, JID, Node, ItemID) ->
|
|
|
|
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
case check_item_publisher(Info, ItemID, Publisher)
|
|
|
|
orelse
|
|
|
|
(get_affiliation(Info, Publisher) == owner) of
|
|
|
|
true ->
|
|
|
|
NewInfo =
|
|
|
|
remove_item(Info, ItemID),
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, []};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, Res}} ->
|
|
|
|
broadcast_retract_item(Host, Node, ItemID),
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
subscribe_node(Host, From, JID, Node) ->
|
|
|
|
Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)),
|
|
|
|
SubscriberJID =
|
|
|
|
case jlib:string_to_jid(JID) of
|
|
|
|
error ->
|
|
|
|
{"", "", ""};
|
|
|
|
J ->
|
|
|
|
J
|
|
|
|
end,
|
|
|
|
Subscriber = jlib:jid_tolower(SubscriberJID),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
Affiliation = get_affiliation(Info, Subscriber),
|
|
|
|
if
|
|
|
|
Affiliation /= outcast ->
|
|
|
|
NewInfo =
|
|
|
|
add_subscriber(Info, Subscriber),
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, []};
|
|
|
|
true ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
if
|
|
|
|
Sender == Subscriber ->
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, Res}} ->
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end;
|
|
|
|
true ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
unsubscribe_node(Host, From, JID, Node) ->
|
|
|
|
Sender = jlib:jid_tolower(jlib:jid_remove_resource(From)),
|
|
|
|
SubscriberJID =
|
|
|
|
case jlib:string_to_jid(JID) of
|
|
|
|
error ->
|
|
|
|
{"", "", ""};
|
|
|
|
J ->
|
|
|
|
J
|
|
|
|
end,
|
|
|
|
Subscriber = jlib:jid_tolower(SubscriberJID),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
Subscription = get_subscription(Info, Subscriber),
|
|
|
|
if
|
|
|
|
Subscription /= none ->
|
|
|
|
NewInfo =
|
|
|
|
remove_subscriber(Info, Subscriber),
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, []};
|
|
|
|
true ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
if
|
|
|
|
Sender == Subscriber ->
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, Res}} ->
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end;
|
|
|
|
true ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
get_items(Host, JID, Node, SMaxItems) ->
|
|
|
|
MaxItems =
|
|
|
|
if
|
|
|
|
SMaxItems == "" ->
|
|
|
|
?MAXITEMS;
|
|
|
|
true ->
|
|
|
|
case catch list_to_integer(SMaxItems) of
|
|
|
|
{'EXIT', _} ->
|
|
|
|
{error, ?ERR_BAD_REQUEST};
|
|
|
|
Val ->
|
|
|
|
Val
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case MaxItems of
|
|
|
|
{error, _} = Error ->
|
|
|
|
Error;
|
|
|
|
_ ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
Items = lists:sublist(Info#nodeinfo.items, MaxItems),
|
|
|
|
ItemsEls =
|
|
|
|
lists:map(
|
|
|
|
fun(#item{id = ItemID,
|
|
|
|
payload = Payload}) ->
|
|
|
|
ItemAttrs = case ItemID of
|
|
|
|
"" -> [];
|
|
|
|
_ -> [{"id", ItemID}]
|
|
|
|
end,
|
|
|
|
{xmlelement, "item", ItemAttrs, Payload}
|
|
|
|
end, Items),
|
|
|
|
{result, [{xmlelement, "pubsub",
|
|
|
|
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
|
|
[{xmlelement, "items",
|
|
|
|
[{"node", node_to_string(Node)}],
|
|
|
|
ItemsEls}]}]};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
delete_node(Host, JID, Node) ->
|
|
|
|
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
case get_affiliation(Info, Owner) of
|
|
|
|
owner ->
|
2005-04-17 20:08:34 +02:00
|
|
|
% TODO: don't iterate over entire table
|
2003-07-08 22:11:27 +02:00
|
|
|
Removed =
|
|
|
|
mnesia:foldl(
|
2005-04-17 20:08:34 +02:00
|
|
|
fun(#pubsub_node{host_node = {_, N},
|
2003-07-08 22:11:27 +02:00
|
|
|
info = #nodeinfo{
|
|
|
|
entities = Entities
|
|
|
|
}}, Acc) ->
|
|
|
|
case lists:prefix(Node, N) of
|
|
|
|
true ->
|
|
|
|
[{N, Entities} | Acc];
|
|
|
|
_ ->
|
|
|
|
Acc
|
|
|
|
end
|
|
|
|
end, [], pubsub_node),
|
|
|
|
lists:foreach(
|
|
|
|
fun({N, _}) ->
|
2005-04-17 20:08:34 +02:00
|
|
|
mnesia:delete({pubsub_node, {Host, N}})
|
2003-07-08 22:11:27 +02:00
|
|
|
end, Removed),
|
|
|
|
{removed, Removed};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {removed, Removed}} ->
|
|
|
|
broadcast_removed_node(Host, Removed),
|
2003-07-20 22:35:35 +02:00
|
|
|
Lang = "",
|
|
|
|
broadcast_retract_item(
|
|
|
|
Host, ["pubsub", "nodes"], node_to_string(Node)),
|
2003-07-08 22:11:27 +02:00
|
|
|
{result, []};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
purge_node(Host, JID, Node) ->
|
|
|
|
Owner = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
case get_affiliation(Info, Owner) of
|
|
|
|
owner ->
|
|
|
|
NewInfo = Info#nodeinfo{items = []},
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, Info#nodeinfo.items, []};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, Items, Res}} ->
|
|
|
|
lists:foreach(
|
|
|
|
fun(#item{id = ItemID}) ->
|
|
|
|
broadcast_retract_item(Host, Node, ItemID)
|
|
|
|
end, Items),
|
|
|
|
{result, Res};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2005-04-17 20:08:34 +02:00
|
|
|
get_entities(Host, OJID, Node) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
2005-04-17 20:08:34 +02:00
|
|
|
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
case get_affiliation(Info, Owner) of
|
|
|
|
owner ->
|
|
|
|
Entities = Info#nodeinfo.entities,
|
|
|
|
EntitiesEls =
|
|
|
|
?DICT:fold(
|
|
|
|
fun(JID,
|
|
|
|
#entity{affiliation = Affiliation,
|
|
|
|
subscription = Subscription},
|
|
|
|
Acc) ->
|
|
|
|
[{xmlelement, "entity",
|
|
|
|
[{"jid", jlib:jid_to_string(JID)},
|
|
|
|
{"affiliation",
|
|
|
|
affiliation_to_string(Affiliation)},
|
|
|
|
{"subscription",
|
|
|
|
subscription_to_string(Subscription)}],
|
|
|
|
[]} | Acc]
|
|
|
|
end, [], Entities),
|
|
|
|
{result, [{xmlelement, "pubsub",
|
|
|
|
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
|
|
[{xmlelement, "entities",
|
|
|
|
[{"node", node_to_string(Node)}],
|
|
|
|
EntitiesEls}]}]};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2005-04-17 20:08:34 +02:00
|
|
|
set_entities(Host, OJID, Node, EntitiesEls) ->
|
2003-07-08 22:11:27 +02:00
|
|
|
Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
|
|
|
Entities =
|
|
|
|
lists:foldl(
|
|
|
|
fun(El, Acc) ->
|
|
|
|
case Acc of
|
|
|
|
error ->
|
|
|
|
error;
|
|
|
|
_ ->
|
|
|
|
case El of
|
|
|
|
{xmlelement, "entity", Attrs, _} ->
|
|
|
|
JID = jlib:string_to_jid(
|
|
|
|
xml:get_attr_s("jid", Attrs)),
|
|
|
|
Affiliation =
|
|
|
|
case xml:get_attr_s("affiliation",
|
|
|
|
Attrs) of
|
|
|
|
"owner" -> owner;
|
|
|
|
"publisher" -> publisher;
|
|
|
|
"outcast" -> outcast;
|
|
|
|
"none" -> none;
|
|
|
|
_ -> false
|
|
|
|
end,
|
|
|
|
Subscription =
|
|
|
|
case xml:get_attr_s("subscription",
|
|
|
|
Attrs) of
|
|
|
|
"subscribed" -> subscribed;
|
|
|
|
"pending" -> pending;
|
|
|
|
"unconfigured" -> unconfigured;
|
|
|
|
"none" -> none;
|
|
|
|
_ -> false
|
|
|
|
end,
|
|
|
|
if
|
|
|
|
(JID == error) or
|
|
|
|
(Affiliation == false) or
|
|
|
|
(Subscription == false) ->
|
|
|
|
error;
|
|
|
|
true ->
|
2004-09-17 21:52:59 +02:00
|
|
|
[{jlib:jid_tolower(JID),
|
2003-07-08 22:11:27 +02:00
|
|
|
#entity{
|
|
|
|
affiliation = Affiliation,
|
|
|
|
subscription = Subscription}} |
|
|
|
|
Acc]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end, [], EntitiesEls),
|
|
|
|
case Entities of
|
|
|
|
error ->
|
|
|
|
{error, ?ERR_BAD_REQUEST};
|
|
|
|
_ ->
|
|
|
|
F = fun() ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case mnesia:read({pubsub_node, {Host, Node}}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info} = N] ->
|
|
|
|
case get_affiliation(Info, Owner) of
|
|
|
|
owner ->
|
|
|
|
NewInfo =
|
|
|
|
set_info_entities(Info, Entities),
|
|
|
|
mnesia:write(
|
|
|
|
N#pubsub_node{info = NewInfo}),
|
|
|
|
{result, []};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_NOT_ALLOWED}
|
|
|
|
end;
|
|
|
|
[] ->
|
|
|
|
{error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
case mnesia:transaction(F) of
|
|
|
|
{atomic, {error, _} = Error} ->
|
|
|
|
Error;
|
|
|
|
{atomic, {result, _}} ->
|
|
|
|
{result, []};
|
|
|
|
_ ->
|
|
|
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2003-07-09 20:58:43 +02:00
|
|
|
%get_node_config(OJID, Node) ->
|
|
|
|
% Owner = jlib:jid_tolower(jlib:jid_remove_resource(OJID)),
|
|
|
|
% case catch mnesia:dirty_read(pubsub_node, Node) of
|
|
|
|
% [#pubsub_node{info = Info}] ->
|
|
|
|
% case get_affiliation(Info, Owner) of
|
|
|
|
% owner ->
|
|
|
|
% Entities = Info#nodeinfo.entities,
|
|
|
|
% EntitiesEls =
|
|
|
|
% ?DICT:fold(
|
|
|
|
% fun(JID,
|
|
|
|
% #entity{affiliation = Affiliation,
|
|
|
|
% subscription = Subscription},
|
|
|
|
% Acc) ->
|
|
|
|
% [{xmlelement, "entity",
|
|
|
|
% [{"jid", jlib:jid_to_string(JID)},
|
|
|
|
% {"affiliation",
|
|
|
|
% affiliation_to_string(Affiliation)},
|
|
|
|
% {"subscription",
|
|
|
|
% subscription_to_string(Subscription)}],
|
|
|
|
% []} | Acc]
|
|
|
|
% end, [], Entities),
|
|
|
|
% {result, [{xmlelement, "pubsub",
|
|
|
|
% [{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
|
|
% [{xmlelement, "entities",
|
|
|
|
% [{"node", node_to_string(Node)}],
|
|
|
|
% EntitiesEls}]}]};
|
|
|
|
% _ ->
|
|
|
|
% {error, ?ERR_NOT_ALLOWED}
|
|
|
|
% end;
|
|
|
|
% _ ->
|
|
|
|
% {error, ?ERR_ITEM_NOT_FOUND}
|
|
|
|
% end.
|
|
|
|
|
|
|
|
|
2003-07-08 22:11:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get_affiliation(#nodeinfo{entities = Entities}, JID) ->
|
|
|
|
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
case ?DICT:find(LJID, Entities) of
|
|
|
|
{ok, #entity{affiliation = Affiliation}} ->
|
|
|
|
Affiliation;
|
|
|
|
_ ->
|
|
|
|
none
|
|
|
|
end.
|
|
|
|
|
|
|
|
get_subscription(#nodeinfo{entities = Entities}, JID) ->
|
|
|
|
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
|
|
|
case ?DICT:find(LJID, Entities) of
|
|
|
|
{ok, #entity{subscription = Subscription}} ->
|
|
|
|
Subscription;
|
|
|
|
_ ->
|
|
|
|
none
|
|
|
|
end.
|
|
|
|
|
|
|
|
affiliation_to_string(Affiliation) ->
|
|
|
|
case Affiliation of
|
|
|
|
owner -> "owner";
|
|
|
|
publisher -> "publisher";
|
|
|
|
outcast -> "outcast";
|
|
|
|
_ -> "none"
|
|
|
|
end.
|
|
|
|
|
|
|
|
subscription_to_string(Subscription) ->
|
|
|
|
case Subscription of
|
|
|
|
subscribed -> "subscribed";
|
|
|
|
pending -> "pending";
|
|
|
|
unconfigured -> "unconfigured";
|
|
|
|
_ -> "none"
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2003-07-09 20:58:43 +02:00
|
|
|
check_create_permission(Host, Node, Owner) ->
|
|
|
|
if
|
2004-09-17 21:52:59 +02:00
|
|
|
Owner#jid.lserver == Host ->
|
2003-07-08 22:11:27 +02:00
|
|
|
true;
|
2003-07-09 20:58:43 +02:00
|
|
|
true ->
|
2003-10-07 22:31:44 +02:00
|
|
|
#jid{luser = User, lserver = Server} = Owner,
|
2003-07-09 20:58:43 +02:00
|
|
|
case Node of
|
|
|
|
["home", Server, User | _] ->
|
|
|
|
true;
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end
|
2003-07-08 22:11:27 +02:00
|
|
|
end.
|
|
|
|
|
|
|
|
insert_item(Info, ItemID, Publisher, Payload) ->
|
|
|
|
Items = Info#nodeinfo.items,
|
|
|
|
Items1 = lists:filter(fun(I) ->
|
|
|
|
I#item.id /= ItemID
|
|
|
|
end, Items),
|
|
|
|
Items2 = [#item{id = ItemID, publisher = Publisher, payload = Payload} |
|
|
|
|
Items1],
|
|
|
|
Items3 = lists:sublist(Items2, ?MAXITEMS),
|
|
|
|
Info#nodeinfo{items = Items3}.
|
|
|
|
|
|
|
|
remove_item(Info, ItemID) ->
|
|
|
|
Items = Info#nodeinfo.items,
|
|
|
|
Items1 = lists:filter(fun(I) ->
|
|
|
|
I#item.id /= ItemID
|
|
|
|
end, Items),
|
|
|
|
Info#nodeinfo{items = Items1}.
|
|
|
|
|
|
|
|
check_item_publisher(Info, ItemID, Publisher) ->
|
|
|
|
Items = Info#nodeinfo.items,
|
|
|
|
case lists:keysearch(ItemID, #item.id, Items) of
|
|
|
|
{value, #item{publisher = Publisher}} ->
|
|
|
|
true;
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end.
|
|
|
|
|
|
|
|
add_subscriber(Info, Subscriber) ->
|
|
|
|
Entities = Info#nodeinfo.entities,
|
|
|
|
case ?DICT:find(Subscriber, Entities) of
|
|
|
|
{ok, Entity} ->
|
|
|
|
Info#nodeinfo{
|
|
|
|
entities = ?DICT:store(Subscriber,
|
|
|
|
Entity#entity{subscription = subscribed},
|
|
|
|
Entities)};
|
|
|
|
_ ->
|
|
|
|
Info#nodeinfo{
|
|
|
|
entities = ?DICT:store(Subscriber,
|
|
|
|
#entity{subscription = subscribed},
|
|
|
|
Entities)}
|
|
|
|
end.
|
|
|
|
|
|
|
|
remove_subscriber(Info, Subscriber) ->
|
|
|
|
Entities = Info#nodeinfo.entities,
|
|
|
|
case ?DICT:find(Subscriber, Entities) of
|
|
|
|
{ok, #entity{affiliation = none}} ->
|
|
|
|
Info#nodeinfo{
|
|
|
|
entities = ?DICT:erase(Subscriber, Entities)};
|
|
|
|
{ok, Entity} ->
|
|
|
|
Info#nodeinfo{
|
|
|
|
entities = ?DICT:store(Subscriber,
|
|
|
|
Entity#entity{subscription = none},
|
|
|
|
Entities)};
|
|
|
|
_ ->
|
|
|
|
Info
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
set_info_entities(Info, Entities) ->
|
|
|
|
NewEntities =
|
|
|
|
lists:foldl(
|
|
|
|
fun({JID, Ent}, Es) ->
|
|
|
|
case Ent of
|
|
|
|
#entity{affiliation = none, subscription = none} ->
|
|
|
|
?DICT:erase(JID, Es);
|
|
|
|
_ ->
|
|
|
|
?DICT:store(JID, Ent, Es)
|
|
|
|
end
|
|
|
|
end, Info#nodeinfo.entities, Entities),
|
|
|
|
Info#nodeinfo{entities = NewEntities}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
?DICT:fold(
|
|
|
|
fun(JID, #entity{subscription = Subscription}, _) ->
|
|
|
|
if
|
|
|
|
(Subscription /= none) and
|
|
|
|
(Subscription /= pending) ->
|
|
|
|
ItemAttrs = case ItemID of
|
|
|
|
"" -> [];
|
|
|
|
_ -> [{"id", ItemID}]
|
|
|
|
end,
|
|
|
|
Stanza =
|
|
|
|
{xmlelement, "message", [],
|
|
|
|
[{xmlelement, "x",
|
|
|
|
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
|
|
[{xmlelement, "items",
|
|
|
|
[{"node", node_to_string(Node)}],
|
|
|
|
[{xmlelement, "item",
|
|
|
|
ItemAttrs,
|
|
|
|
Payload}]}]}]},
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
?MYJID, jlib:make_jid(JID), Stanza);
|
2003-07-08 22:11:27 +02:00
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end
|
|
|
|
end, ok, Info#nodeinfo.entities);
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
broadcast_retract_item(Host, Node, ItemID) ->
|
2005-04-17 20:08:34 +02:00
|
|
|
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
2003-07-08 22:11:27 +02:00
|
|
|
[#pubsub_node{info = Info}] ->
|
|
|
|
?DICT:fold(
|
|
|
|
fun(JID, #entity{subscription = Subscription}, _) ->
|
|
|
|
if
|
|
|
|
(Subscription /= none) and
|
|
|
|
(Subscription /= pending) ->
|
|
|
|
ItemAttrs = case ItemID of
|
|
|
|
"" -> [];
|
|
|
|
_ -> [{"id", ItemID}]
|
|
|
|
end,
|
|
|
|
Stanza =
|
|
|
|
{xmlelement, "message", [],
|
|
|
|
[{xmlelement, "x",
|
|
|
|
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
2003-07-20 22:35:35 +02:00
|
|
|
[{xmlelement, "items",
|
2003-07-08 22:11:27 +02:00
|
|
|
[{"node", node_to_string(Node)}],
|
2003-07-20 22:35:35 +02:00
|
|
|
[{xmlelement, "retract",
|
|
|
|
ItemAttrs, []}]}]}]},
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
?MYJID, jlib:make_jid(JID), Stanza);
|
2003-07-08 22:11:27 +02:00
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end
|
|
|
|
end, ok, Info#nodeinfo.entities);
|
|
|
|
_ ->
|
|
|
|
false
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
broadcast_removed_node(Host, Removed) ->
|
|
|
|
lists:foreach(
|
|
|
|
fun({Node, Entities}) ->
|
|
|
|
?DICT:fold(
|
|
|
|
fun(JID, #entity{subscription = Subscription}, _) ->
|
|
|
|
if
|
|
|
|
(Subscription /= none) and
|
|
|
|
(Subscription /= pending) ->
|
|
|
|
Stanza =
|
|
|
|
{xmlelement, "message", [],
|
|
|
|
[{xmlelement, "x",
|
|
|
|
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
|
|
|
[{xmlelement, "delete",
|
|
|
|
[{"node", node_to_string(Node)}],
|
|
|
|
[]}]}]},
|
2003-10-07 22:31:44 +02:00
|
|
|
ejabberd_router:route(
|
|
|
|
?MYJID, jlib:make_jid(JID), Stanza);
|
2003-07-08 22:11:27 +02:00
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end
|
|
|
|
end, ok, Entities)
|
|
|
|
end, Removed).
|
|
|
|
|
2003-07-20 22:35:35 +02:00
|
|
|
|
|
|
|
|
|
|
|
system_continue(Parent, _, State) ->
|
|
|
|
loop(State, Parent).
|
|
|
|
|
|
|
|
system_terminate(Reason, Parent, _, State) ->
|
|
|
|
exit(Reason).
|
|
|
|
|
|
|
|
system_code_change(State, _Mod, Ver, _Extra) ->
|
|
|
|
{ok, State}.
|
2005-04-17 20:08:34 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
find_my_host(LServer) ->
|
|
|
|
Parts = string:tokens(LServer, "."),
|
|
|
|
find_my_host(Parts, ?MYHOSTS).
|
|
|
|
|
|
|
|
find_my_host([], _Hosts) ->
|
|
|
|
?MYNAME;
|
|
|
|
find_my_host([_ | Tail] = Parts, Hosts) ->
|
|
|
|
Domain = parts_to_string(Parts),
|
|
|
|
case lists:member(Domain, Hosts) of
|
|
|
|
true ->
|
|
|
|
Domain;
|
|
|
|
false ->
|
|
|
|
find_my_host(Tail, Hosts)
|
|
|
|
end.
|
|
|
|
|
|
|
|
parts_to_string(Parts) ->
|
|
|
|
string:strip(lists:flatten(lists:map(fun(S) -> [S, $.] end, Parts)),
|
|
|
|
right, $.).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
update_table(Host) ->
|
|
|
|
Fields = record_info(fields, pubsub_node),
|
|
|
|
case mnesia:table_info(pubsub_node, attributes) of
|
|
|
|
Fields ->
|
|
|
|
ok;
|
|
|
|
[node, parent, info] ->
|
|
|
|
?INFO_MSG("Converting pubsub_node table from "
|
|
|
|
"{node, parent, info} format", []),
|
|
|
|
{atomic, ok} = mnesia:create_table(
|
|
|
|
mod_pubsub_tmp_table,
|
|
|
|
[{disc_only_copies, [node()]},
|
|
|
|
{type, bag},
|
|
|
|
{local_content, true},
|
|
|
|
{record_name, pubsub_node},
|
|
|
|
{attributes, record_info(fields, pubsub_node)}]),
|
|
|
|
mnesia:del_table_index(pubsub_node, parent),
|
|
|
|
mnesia:transform_table(pubsub_node, ignore, Fields),
|
|
|
|
F1 = fun() ->
|
|
|
|
mnesia:write_lock_table(mod_pubsub_tmp_table),
|
|
|
|
mnesia:foldl(
|
|
|
|
fun(#pubsub_node{host_node = N,
|
|
|
|
host_parent = P} = R, _) ->
|
|
|
|
mnesia:dirty_write(
|
|
|
|
mod_pubsub_tmp_table,
|
|
|
|
R#pubsub_node{host_node = {Host, N},
|
|
|
|
host_parent = {Host, P}})
|
|
|
|
end, ok, pubsub_node)
|
|
|
|
end,
|
|
|
|
mnesia:transaction(F1),
|
|
|
|
mnesia:clear_table(pubsub_node),
|
|
|
|
F2 = fun() ->
|
|
|
|
mnesia:write_lock_table(pubsub_node),
|
|
|
|
mnesia:foldl(
|
|
|
|
fun(R, _) ->
|
|
|
|
mnesia:dirty_write(R)
|
|
|
|
end, ok, mod_pubsub_tmp_table)
|
|
|
|
end,
|
|
|
|
mnesia:transaction(F2),
|
|
|
|
mnesia:delete_table(mod_pubsub_tmp_table);
|
|
|
|
_ ->
|
|
|
|
?INFO_MSG("Recreating pubsub_node table", []),
|
|
|
|
mnesia:transform_table(pubsub_node, ignore, Fields)
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
|
|
|
|