mirror of
https://github.com/processone/ejabberd.git
synced 2024-06-16 22:05:29 +02:00
* src/mod_pubsub/mod_pubsub.erl: Updated to J-EAI version
SVN Revision: 378
This commit is contained in:
parent
2d09cf8a39
commit
d520f35819
|
@ -1,3 +1,7 @@
|
|||
2005-07-20 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/mod_pubsub/mod_pubsub.erl: Updated to J-EAI version
|
||||
|
||||
2005-07-15 Alexey Shchepin <alexey@sevcom.net>
|
||||
|
||||
* src/acl.erl: Slightly changed "access" option processing
|
||||
|
|
|
@ -20,11 +20,20 @@
|
|||
system_terminate/4,
|
||||
system_code_change/4]).
|
||||
|
||||
-export([delete_item/3,
|
||||
set_entities/4,
|
||||
delete_node/2,
|
||||
create_new_node/2,
|
||||
subscribe_node/3,
|
||||
get_node_config/4,
|
||||
set_node_config/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-define(DICT, dict).
|
||||
-define(MAXITEMS, 10).
|
||||
-define(MAXITEMS, 20).
|
||||
-define(MAX_PAYLOAD_SIZE, 100000).
|
||||
|
||||
-record(pubsub_node, {host_node, host_parent, info}).
|
||||
-record(nodeinfo, {items = [],
|
||||
|
@ -62,6 +71,7 @@ init(Host, ServerHost, ServedHosts, Parent) ->
|
|||
lists:foreach(fun(H) ->
|
||||
create_new_node(Host, ["home", H], ?MYJID)
|
||||
end, ServedHosts),
|
||||
ets:new(gen_mod:get_module_proc(Host, pubsub_presence), [set, named_table]),
|
||||
loop(Host, Parent).
|
||||
|
||||
loop(Host, Parent) ->
|
||||
|
@ -88,6 +98,33 @@ loop(Host, Parent) ->
|
|||
loop(Host, Parent)
|
||||
end.
|
||||
|
||||
%%% API functions
|
||||
|
||||
delete_item(From, Node, ItemID) ->
|
||||
delete_item(get_host(), From, Node, ItemID).
|
||||
|
||||
delete_node(From, Node) ->
|
||||
delete_node(get_host(), From, Node).
|
||||
|
||||
create_new_node(Node, From) ->
|
||||
create_new_node(get_host(), Node, From).
|
||||
|
||||
subscribe_node(From, JID, Node) ->
|
||||
subscribe_node(get_host(), From, JID, Node).
|
||||
|
||||
set_node_config(From, Node, Els, Lang) ->
|
||||
set_node_config(get_host(), From, Node, Els, Lang).
|
||||
|
||||
get_host() ->
|
||||
ejabberd_mod_pubsub ! {get_host, self()},
|
||||
receive
|
||||
{pubsub_host, Host} ->
|
||||
Host
|
||||
after 5000 ->
|
||||
timeout
|
||||
end.
|
||||
|
||||
%%% Internal functions
|
||||
|
||||
do_route(Host, From, To, Packet) ->
|
||||
{xmlelement, Name, Attrs, Els} = Packet,
|
||||
|
@ -124,32 +161,6 @@ do_route(Host, From, To, Packet) ->
|
|||
Packet, Error)
|
||||
end,
|
||||
ejabberd_router:route(To, From, Res);
|
||||
%#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)}]},
|
||||
% ejabberd_router:route(To,
|
||||
% From,
|
||||
% jlib:iq_to_xml(Res));
|
||||
%#iq{type = set, xmlns = ?NS_REGISTER = XMLNS,
|
||||
% sub_el = SubEl} = IQ ->
|
||||
% case process_iq_register_set(From, SubEl) of
|
||||
% {result, IQRes} ->
|
||||
% Res = IQ#iq{type = result,
|
||||
% sub_el = [{xmlelement, "query",
|
||||
% [{"xmlns", XMLNS}],
|
||||
% IQRes}]},
|
||||
% 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;
|
||||
#iq{type = Type, xmlns = ?NS_PUBSUB = XMLNS,
|
||||
sub_el = SubEl} = IQ ->
|
||||
Res =
|
||||
|
@ -163,6 +174,20 @@ do_route(Host, From, To, Packet) ->
|
|||
Packet, Error)
|
||||
end,
|
||||
ejabberd_router:route(To, From, Res);
|
||||
#iq{type = Type, xmlns = ?NS_PUBSUB_OWNER = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ ->
|
||||
Res =
|
||||
case iq_pubsub_owner(
|
||||
Host, From, Type, Lang, SubEl) of
|
||||
{result, IQRes} ->
|
||||
jlib:iq_to_xml(
|
||||
IQ#iq{type = result,
|
||||
sub_el = IQRes});
|
||||
{error, Error} ->
|
||||
jlib:make_error_reply(
|
||||
Packet, Error)
|
||||
end,
|
||||
ejabberd_router:route(To, From, Res);
|
||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||
lang = Lang, sub_el = SubEl} = IQ ->
|
||||
Res = IQ#iq{type = result,
|
||||
|
@ -180,6 +205,19 @@ do_route(Host, From, To, Packet) ->
|
|||
_ ->
|
||||
ok
|
||||
end;
|
||||
"presence" ->
|
||||
Type = xml:get_attr_s("type", Attrs),
|
||||
if
|
||||
(Type == "unavailable") or (Type == "error") ->
|
||||
ets:delete(
|
||||
gen_mod:get_module_proc(Host, pubsub_presence),
|
||||
{From#jid.luser, From#jid.lserver});
|
||||
true ->
|
||||
ets:insert(
|
||||
gen_mod:get_module_proc(Host, pubsub_presence),
|
||||
{{From#jid.luser, From#jid.lserver}, []})
|
||||
end,
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
|
@ -219,7 +257,6 @@ iq_disco_info(SNode) ->
|
|||
[{"category", "pubsub"},
|
||||
{"type", "generic"},
|
||||
{"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}], []},
|
||||
|
@ -286,104 +323,6 @@ iq_disco_items(Host, From, SNode) ->
|
|||
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", [],
|
||||
|
@ -440,8 +379,8 @@ iq_pubsub(Host, From, Type, SubEl) ->
|
|||
get_entities(Host, From, Node);
|
||||
{set, "entities"} ->
|
||||
set_entities(Host, From, Node, xml:remove_cdata(Els));
|
||||
%{get, "configure"} ->
|
||||
% get_node_config(From, Node);
|
||||
{get, "affiliations"} ->
|
||||
get_affiliations(Host, From);
|
||||
_ ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
||||
end;
|
||||
|
@ -456,6 +395,31 @@ iq_pubsub(Host, From, Type, SubEl) ->
|
|||
{"var", Var}],
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
-define(BOOLXFIELD(Label, Var, Val),
|
||||
?XFIELD("boolean", Label, Var,
|
||||
case Val of
|
||||
true -> "1";
|
||||
_ -> "0"
|
||||
end)).
|
||||
|
||||
-define(STRINGXFIELD(Label, Var, Val),
|
||||
?XFIELD("text-single", Label, Var, Val)).
|
||||
|
||||
-define(XFIELDOPT(Type, Label, Var, Val, Opts),
|
||||
{xmlelement, "field", [{"type", Type},
|
||||
{"label", translate:translate(Lang, Label)},
|
||||
{"var", Var}],
|
||||
lists:map(fun(Opt) ->
|
||||
{xmlelement, "option", [],
|
||||
[{xmlelement, "value", [],
|
||||
[{xmlcdata, Opt}]}]}
|
||||
end, Opts) ++
|
||||
[{xmlelement, "value", [], [{xmlcdata, Val}]}]}).
|
||||
|
||||
-define(LISTXFIELD(Label, Var, Val, Opts),
|
||||
?XFIELDOPT("list-single", Label, Var, Val, Opts)).
|
||||
|
||||
|
||||
|
||||
%% Create new pubsub nodes
|
||||
%% This function is used during init to create the first bootstrap nodes
|
||||
|
@ -533,14 +497,25 @@ create_new_node(Host, Node, Owner) ->
|
|||
|
||||
|
||||
publish_item(Host, JID, Node, ItemID, Payload) ->
|
||||
ejabberd_hooks:run(pubsub_publish_item, Host,
|
||||
[JID, ?MYJID, Node, ItemID, Payload]),
|
||||
Publisher = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Publisher),
|
||||
Subscription = get_subscription(Info, Publisher),
|
||||
MaxSize = get_node_option(Info, max_payload_size),
|
||||
Model = get_node_option(Info, publish_model),
|
||||
Size = size(term_to_binary(Payload)),
|
||||
if
|
||||
(Affiliation == owner) or
|
||||
(Affiliation == publisher) ->
|
||||
((Model == open) or
|
||||
((Model == publishers) and
|
||||
((Affiliation == owner) or
|
||||
(Affiliation == publisher))) or
|
||||
((Model == subscribers) and
|
||||
(Subscription == subscribed))) and
|
||||
(Size =< MaxSize) ->
|
||||
NewInfo =
|
||||
insert_item(Info, ItemID,
|
||||
Publisher, Payload),
|
||||
|
@ -612,13 +587,13 @@ subscribe_node(Host, From, JID, Node) ->
|
|||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
Affiliation = get_affiliation(Info, Subscriber),
|
||||
AllowSubscriptions = get_node_option(Info, subscribe),
|
||||
if
|
||||
Affiliation /= outcast ->
|
||||
NewInfo =
|
||||
add_subscriber(Info, Subscriber),
|
||||
mnesia:write(
|
||||
N#pubsub_node{info = NewInfo}),
|
||||
{result, []};
|
||||
AllowSubscriptions and
|
||||
(Affiliation /= outcast) ->
|
||||
NewInfo = add_subscriber(Info, Subscriber),
|
||||
mnesia:write(N#pubsub_node{info = NewInfo}),
|
||||
{result, [], Info};
|
||||
true ->
|
||||
{error, ?ERR_NOT_ALLOWED}
|
||||
end;
|
||||
|
@ -631,7 +606,33 @@ subscribe_node(Host, From, JID, Node) ->
|
|||
case mnesia:transaction(F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {result, Res}} ->
|
||||
{atomic, {result, Res, Info}} ->
|
||||
case get_node_option(Info, send_item_subscribe) of
|
||||
true ->
|
||||
ItemsEls =
|
||||
lists:map(
|
||||
fun(#item{id = ItemID,
|
||||
payload = Payload}) ->
|
||||
ItemAttrs = case ItemID of
|
||||
"" -> [];
|
||||
_ -> [{"id", ItemID}]
|
||||
end,
|
||||
{xmlelement, "item",
|
||||
ItemAttrs, Payload}
|
||||
end, Info#nodeinfo.items),
|
||||
Stanza =
|
||||
{xmlelement, "message",
|
||||
[],
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
ItemsEls}]}]},
|
||||
ejabberd_router:route(
|
||||
?MYJID, jlib:make_jid(Subscriber), Stanza);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
{result, Res};
|
||||
_ ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
|
@ -715,7 +716,7 @@ get_items(Host, JID, Node, SMaxItems) ->
|
|||
{xmlelement, "item", ItemAttrs, Payload}
|
||||
end, Items),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB}],
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
ItemsEls}]}]};
|
||||
|
@ -736,12 +737,10 @@ delete_node(Host, JID, Node) ->
|
|||
Removed =
|
||||
mnesia:foldl(
|
||||
fun(#pubsub_node{host_node = {_, N},
|
||||
info = #nodeinfo{
|
||||
entities = Entities
|
||||
}}, Acc) ->
|
||||
info = NInfo}, Acc) ->
|
||||
case lists:prefix(Node, N) of
|
||||
true ->
|
||||
[{N, Entities} | Acc];
|
||||
[{N, NInfo} | Acc];
|
||||
_ ->
|
||||
Acc
|
||||
end
|
||||
|
@ -826,7 +825,7 @@ get_entities(Host, OJID, Node) ->
|
|||
[]} | Acc]
|
||||
end, [], Entities),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB}],
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "entities",
|
||||
[{"node", node_to_string(Node)}],
|
||||
EntitiesEls}]}]};
|
||||
|
@ -916,41 +915,44 @@ set_entities(Host, OJID, Node, EntitiesEls) ->
|
|||
end.
|
||||
|
||||
|
||||
%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.
|
||||
|
||||
|
||||
|
||||
get_affiliations(Host, JID) ->
|
||||
LJID = jlib:jid_tolower(jlib:jid_remove_resource(JID)),
|
||||
case catch mnesia:dirty_select(
|
||||
pubsub_node,
|
||||
[{#pubsub_node{_ = '_'},
|
||||
[],
|
||||
['$_']}]) of
|
||||
{'EXIT', _} ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR};
|
||||
Nodes ->
|
||||
Entities =
|
||||
lists:flatmap(
|
||||
fun(#pubsub_node{host_node = {H, Node}, info = Info})
|
||||
when H == Host ->
|
||||
Affiliation = get_affiliation(Info, LJID),
|
||||
Subscription = get_subscription(Info, LJID),
|
||||
if
|
||||
(Affiliation /= none) or
|
||||
(Subscription /= none) ->
|
||||
[{xmlelement, "entity",
|
||||
[{"node", node_to_string(Node)},
|
||||
{"jid", jlib:jid_to_string(JID)},
|
||||
{"affiliation",
|
||||
affiliation_to_string(Affiliation)},
|
||||
{"subscription",
|
||||
subscription_to_string(Subscription)}],
|
||||
[]}];
|
||||
true ->
|
||||
[]
|
||||
end;
|
||||
(_) ->
|
||||
[]
|
||||
end, Nodes),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "affiliations", [],
|
||||
Entities}]}]}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
@ -1011,7 +1013,7 @@ insert_item(Info, ItemID, Publisher, Payload) ->
|
|||
end, Items),
|
||||
Items2 = [#item{id = ItemID, publisher = Publisher, payload = Payload} |
|
||||
Items1],
|
||||
Items3 = lists:sublist(Items2, ?MAXITEMS),
|
||||
Items3 = lists:sublist(Items2, get_max_items(Info)),
|
||||
Info#nodeinfo{items = Items3}.
|
||||
|
||||
remove_item(Info, ItemID) ->
|
||||
|
@ -1081,13 +1083,36 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
|||
[#pubsub_node{info = Info}] ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
Present = case get_node_option(
|
||||
Info, presence_based_delivery) of
|
||||
true ->
|
||||
case ets:lookup(
|
||||
gen_mod:get_module_proc(Host, pubsub_presence),
|
||||
{element(1, JID),
|
||||
element(2, JID)}) of
|
||||
[_] ->
|
||||
true;
|
||||
[] ->
|
||||
false
|
||||
end;
|
||||
false ->
|
||||
true
|
||||
end,
|
||||
if
|
||||
(Subscription /= none) and
|
||||
(Subscription /= pending) ->
|
||||
(Subscription /= pending) and
|
||||
Present ->
|
||||
ItemAttrs = case ItemID of
|
||||
"" -> [];
|
||||
_ -> [{"id", ItemID}]
|
||||
end,
|
||||
Content = case get_node_option(
|
||||
Info, deliver_payloads) of
|
||||
true ->
|
||||
Payload;
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
|
@ -1096,7 +1121,7 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
|||
[{"node", node_to_string(Node)}],
|
||||
[{xmlelement, "item",
|
||||
ItemAttrs,
|
||||
Payload}]}]}]},
|
||||
Content}]}]}]},
|
||||
ejabberd_router:route(
|
||||
?MYJID, jlib:make_jid(JID), Stanza);
|
||||
true ->
|
||||
|
@ -1111,6 +1136,8 @@ broadcast_publish_item(Host, Node, ItemID, Payload) ->
|
|||
broadcast_retract_item(Host, Node, ItemID) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_node_option(Info, notify_retract) of
|
||||
true ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
if
|
||||
|
@ -1122,7 +1149,7 @@ broadcast_retract_item(Host, Node, ItemID) ->
|
|||
end,
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
|
@ -1134,6 +1161,9 @@ broadcast_retract_item(Host, Node, ItemID) ->
|
|||
ok
|
||||
end
|
||||
end, ok, Info#nodeinfo.entities);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
@ -1141,7 +1171,10 @@ broadcast_retract_item(Host, Node, ItemID) ->
|
|||
|
||||
broadcast_removed_node(Host, Removed) ->
|
||||
lists:foreach(
|
||||
fun({Node, Entities}) ->
|
||||
fun({Node, Info}) ->
|
||||
case get_node_option(Info, notify_delete) of
|
||||
true ->
|
||||
Entities = Info#nodeinfo.entities,
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
if
|
||||
|
@ -1149,7 +1182,7 @@ broadcast_removed_node(Host, Removed) ->
|
|||
(Subscription /= pending) ->
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "event",
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "delete",
|
||||
[{"node", node_to_string(Node)}],
|
||||
|
@ -1159,10 +1192,74 @@ broadcast_removed_node(Host, Removed) ->
|
|||
true ->
|
||||
ok
|
||||
end
|
||||
end, ok, Entities)
|
||||
end, ok, Entities);
|
||||
false ->
|
||||
ok
|
||||
end
|
||||
end, Removed).
|
||||
|
||||
|
||||
broadcast_config_notification(Host, Node, Lang) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_node_option(Info, notify_config) of
|
||||
true ->
|
||||
?DICT:fold(
|
||||
fun(JID, #entity{subscription = Subscription}, _) ->
|
||||
Present = case get_node_option(
|
||||
Info, presence_based_delivery) of
|
||||
true ->
|
||||
case ets:lookup(
|
||||
gen_mod:get_module_proc(Host, pubsub_presence),
|
||||
{element(1, JID),
|
||||
element(2, JID)}) of
|
||||
[_] ->
|
||||
true;
|
||||
[] ->
|
||||
false
|
||||
end;
|
||||
false ->
|
||||
true
|
||||
end,
|
||||
if
|
||||
(Subscription /= none) and
|
||||
(Subscription /= pending) and
|
||||
Present ->
|
||||
Fields = get_node_config_xfields(
|
||||
Node, Info, Lang),
|
||||
Content = case get_node_option(
|
||||
Info, deliver_payloads) of
|
||||
true ->
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_XDATA},
|
||||
{"type", "form"}],
|
||||
Fields}];
|
||||
false ->
|
||||
[]
|
||||
end,
|
||||
Stanza =
|
||||
{xmlelement, "message", [],
|
||||
[{xmlelement, "x",
|
||||
[{"xmlns", ?NS_PUBSUB_EVENT}],
|
||||
[{xmlelement, "items",
|
||||
[{"node", node_to_string(Node)}],
|
||||
[{xmlelement, "item",
|
||||
[{"id", "configuration"}],
|
||||
Content}]}]}]},
|
||||
ejabberd_router:route(
|
||||
?MYJID, jlib:make_jid(JID), Stanza);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, ok, Info#nodeinfo.entities);
|
||||
false ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end.
|
||||
|
||||
|
||||
|
||||
system_continue(Parent, _, State) ->
|
||||
loop(State, Parent).
|
||||
|
@ -1176,6 +1273,303 @@ system_code_change(State, _Mod, Ver, _Extra) ->
|
|||
|
||||
|
||||
|
||||
iq_pubsub_owner(Host, From, Type, Lang, 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
|
||||
{get, "configure"} ->
|
||||
get_node_config(Host, From, Node, Lang);
|
||||
{set, "configure"} ->
|
||||
set_node_config(Host, From, Node, Els, Lang);
|
||||
_ ->
|
||||
{error, ?ERR_FEATURE_NOT_IMPLEMENTED}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end.
|
||||
|
||||
get_node_config(Host, From, Node, Lang) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info}] ->
|
||||
case get_affiliation(Info, From) of
|
||||
owner ->
|
||||
Fields = get_node_config_xfields(Node, Info, Lang),
|
||||
{result, [{xmlelement, "pubsub",
|
||||
[{"xmlns", ?NS_PUBSUB_OWNER}],
|
||||
[{xmlelement, "configure",
|
||||
[{"node", node_to_string(Node)}],
|
||||
[{xmlelement, "x", [{"xmlns", ?NS_XDATA},
|
||||
{"type", "form"}],
|
||||
Fields}]}]}]};
|
||||
_ ->
|
||||
{error, ?ERR_NOT_AUTHORIZED}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND}
|
||||
end.
|
||||
|
||||
% TODO: move to jlib.hrl
|
||||
-define(NS_PUBSUB_NODE_CONFIG, "http://jabber.org/protocol/pubsub#node_config").
|
||||
|
||||
-define(BOOL_CONFIG_FIELD(Label, Var),
|
||||
?BOOLXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||
get_node_option(Info, Var))).
|
||||
|
||||
-define(STRING_CONFIG_FIELD(Label, Var),
|
||||
?STRINGXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||
get_node_option(Info, Var))).
|
||||
|
||||
-define(INTEGER_CONFIG_FIELD(Label, Var),
|
||||
?STRINGXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||
integer_to_list(get_node_option(Info, Var)))).
|
||||
|
||||
-define(JLIST_CONFIG_FIELD(Label, Var, Opts),
|
||||
?LISTXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||
jlib:jid_to_string(get_node_option(Info, Var)),
|
||||
[jlib:jid_to_string(O) || O <- Opts])).
|
||||
|
||||
-define(ALIST_CONFIG_FIELD(Label, Var, Opts),
|
||||
?LISTXFIELD(Label, "pubsub#" ++ atom_to_list(Var),
|
||||
atom_to_list(get_node_option(Info, Var)),
|
||||
[atom_to_list(O) || O <- Opts])).
|
||||
|
||||
|
||||
-define(DEFAULT_OPTIONS,
|
||||
[{deliver_payloads, true},
|
||||
{notify_config, false},
|
||||
{notify_delete, false},
|
||||
{notify_retract, true},
|
||||
{persist_items, true},
|
||||
{max_items, ?MAXITEMS div 2},
|
||||
{subscribe, true},
|
||||
{subscription_model, open},
|
||||
{publish_model, publishers},
|
||||
{max_payload_size, ?MAX_PAYLOAD_SIZE},
|
||||
{send_item_subscribe, false},
|
||||
{presence_based_delivery, false}]).
|
||||
|
||||
get_node_option(Info, current_approver) ->
|
||||
Default = hd(get_owners_jids(Info)),
|
||||
Options = Info#nodeinfo.options,
|
||||
element(
|
||||
2, element(2, lists:keysearch(
|
||||
current_approver, 1,
|
||||
Options ++ [{current_approver, Default}])));
|
||||
get_node_option(#nodeinfo{options = Options}, Var) ->
|
||||
element(
|
||||
2, element(2, lists:keysearch(Var, 1, Options ++ ?DEFAULT_OPTIONS))).
|
||||
|
||||
get_max_items(Info) ->
|
||||
case get_node_option(Info, persist_items) of
|
||||
true ->
|
||||
get_node_option(Info, max_items);
|
||||
false ->
|
||||
0
|
||||
end.
|
||||
|
||||
get_owners_jids(Info) ->
|
||||
Entities = Info#nodeinfo.entities,
|
||||
Owners =
|
||||
?DICT:fold(
|
||||
fun(JID,
|
||||
#entity{affiliation = Affiliation,
|
||||
subscription = Subscription},
|
||||
Acc) ->
|
||||
case Affiliation of
|
||||
owner ->
|
||||
[JID | Acc];
|
||||
_ ->
|
||||
Acc
|
||||
end
|
||||
end, [], Entities),
|
||||
lists:sort(Owners).
|
||||
|
||||
|
||||
get_node_config_xfields(Node, Info, Lang) ->
|
||||
[?XFIELD("hidden", "", "FORM_TYPE", ?NS_PUBSUB_NODE_CONFIG),
|
||||
?BOOL_CONFIG_FIELD("Deliver payloads with event notifications", deliver_payloads),
|
||||
?BOOL_CONFIG_FIELD("Notify subscribers when the node configuration changes", notify_config),
|
||||
?BOOL_CONFIG_FIELD("Notify subscribers when the node is deleted", notify_delete),
|
||||
?BOOL_CONFIG_FIELD("Notify subscribers when items are removed from the node", notify_retract),
|
||||
?BOOL_CONFIG_FIELD("Persist items to storage", persist_items),
|
||||
?INTEGER_CONFIG_FIELD("Max # of items to persist", max_items),
|
||||
?BOOL_CONFIG_FIELD("Whether to allow subscriptions", subscribe),
|
||||
?ALIST_CONFIG_FIELD("Specify the subscriber model", subscription_model,
|
||||
[open]),
|
||||
?ALIST_CONFIG_FIELD("Specify the publisher model", publish_model,
|
||||
[publishers, subscribers, open]),
|
||||
?INTEGER_CONFIG_FIELD("Max payload size in bytes", max_payload_size),
|
||||
?BOOL_CONFIG_FIELD("Send items to new subscribers", send_item_subscribe),
|
||||
?BOOL_CONFIG_FIELD("Only deliver notifications to available users", presence_based_delivery),
|
||||
?JLIST_CONFIG_FIELD("Specify the current subscription approver", current_approver,
|
||||
get_owners_jids(Info))
|
||||
].
|
||||
|
||||
|
||||
set_node_config(Host, From, Node, Els, Lang) ->
|
||||
case catch mnesia:dirty_read(pubsub_node, {Host, Node}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
case get_affiliation(Info, From) of
|
||||
owner ->
|
||||
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"} ->
|
||||
CurOpts = Info#nodeinfo.options,
|
||||
set_node_config1(
|
||||
Host, From, Node, XEl, CurOpts, Lang);
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_NOT_AUTHORIZED}
|
||||
end;
|
||||
_ ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND}
|
||||
end.
|
||||
|
||||
|
||||
set_node_config1(Host, From, Node, XEl, CurOpts, Lang) ->
|
||||
XData = jlib:parse_xdata_submit(XEl),
|
||||
case XData of
|
||||
invalid ->
|
||||
{error, ?ERR_BAD_REQUEST};
|
||||
_ ->
|
||||
case set_xoption(XData, CurOpts) of
|
||||
NewOpts when is_list(NewOpts) ->
|
||||
change_node_opts(Host, NewOpts, Node, Lang);
|
||||
Err ->
|
||||
Err
|
||||
end
|
||||
end.
|
||||
|
||||
add_opt(Key, Value, Opts) ->
|
||||
Opts1 = lists:keydelete(Key, 1, Opts),
|
||||
[{Key, Value} | Opts1].
|
||||
|
||||
|
||||
-define(SET_BOOL_XOPT(Opt, Val),
|
||||
case Val of
|
||||
"0" -> set_xoption(Opts, add_opt(Opt, false, NewOpts));
|
||||
"1" -> set_xoption(Opts, add_opt(Opt, true, NewOpts));
|
||||
_ -> {error, ?ERR_BAD_REQUEST}
|
||||
end).
|
||||
|
||||
-define(SET_STRING_XOPT(Opt, Val),
|
||||
set_xoption(Opts, add_opt(Opt, Val, NewOpts))).
|
||||
|
||||
-define(SET_INTEGER_XOPT(Opt, Val, Min, Max),
|
||||
case catch list_to_integer(Val) of
|
||||
IVal when is_integer(IVal),
|
||||
IVal >= Min,
|
||||
IVal =< Max ->
|
||||
set_xoption(Opts, add_opt(Opt, IVal, NewOpts));
|
||||
_ ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end).
|
||||
|
||||
-define(SET_ALIST_XOPT(Opt, Val, Vals),
|
||||
case lists:member(Val, [atom_to_list(V) || V <- Vals]) of
|
||||
true ->
|
||||
set_xoption(Opts, add_opt(Opt, list_to_atom(Val), NewOpts));
|
||||
false ->
|
||||
{error, ?ERR_BAD_REQUEST}
|
||||
end).
|
||||
|
||||
|
||||
set_xoption([], NewOpts) ->
|
||||
NewOpts;
|
||||
set_xoption([{"FORM_TYPE", _} | Opts], NewOpts) ->
|
||||
set_xoption(Opts, NewOpts);
|
||||
set_xoption([{"pubsub#deliver_payloads", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(deliver_payloads, Val);
|
||||
set_xoption([{"pubsub#notify_config", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(notify_config, Val);
|
||||
set_xoption([{"pubsub#notify_delete", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(notify_delete, Val);
|
||||
set_xoption([{"pubsub#notify_retract", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(notify_retract, Val);
|
||||
set_xoption([{"pubsub#persist_items", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(persist_items, Val);
|
||||
set_xoption([{"pubsub#max_items", [Val]} | Opts], NewOpts) ->
|
||||
?SET_INTEGER_XOPT(max_items, Val, 0, ?MAXITEMS);
|
||||
set_xoption([{"pubsub#subscribe", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(subscribe, Val);
|
||||
set_xoption([{"pubsub#subscription_model", [Val]} | Opts], NewOpts) ->
|
||||
?SET_ALIST_XOPT(subscription_model, Val, [open]);
|
||||
set_xoption([{"pubsub#publish_model", [Val]} | Opts], NewOpts) ->
|
||||
?SET_ALIST_XOPT(publish_model, Val, [publishers, subscribers, open]);
|
||||
set_xoption([{"pubsub#max_payload_size", [Val]} | Opts], NewOpts) ->
|
||||
?SET_INTEGER_XOPT(max_payload_size, Val, 0, ?MAX_PAYLOAD_SIZE);
|
||||
set_xoption([{"pubsub#send_item_subscribe", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(send_item_subscribe, Val);
|
||||
set_xoption([{"pubsub#presence_based_delivery", [Val]} | Opts], NewOpts) ->
|
||||
?SET_BOOL_XOPT(presence_based_delivery, Val);
|
||||
set_xoption([{"pubsub#current_approver", _} | Opts], NewOpts) ->
|
||||
% TODO
|
||||
set_xoption(Opts, NewOpts);
|
||||
%set_xoption([{"title", [Val]} | Opts], NewOpts) ->
|
||||
% ?SET_STRING_XOPT(title, Val);
|
||||
set_xoption([_ | _Opts], _NewOpts) ->
|
||||
{error, ?ERR_BAD_REQUEST}.
|
||||
|
||||
|
||||
change_node_opts(Host, NewOpts, Node, Lang) ->
|
||||
F = fun() ->
|
||||
case mnesia:read({pubsub_node, {Host, Node}}) of
|
||||
[#pubsub_node{info = Info} = N] ->
|
||||
NewInfo = Info#nodeinfo{options = NewOpts},
|
||||
mnesia:write(
|
||||
N#pubsub_node{info = NewInfo}),
|
||||
{result, []};
|
||||
[] ->
|
||||
{error, ?ERR_ITEM_NOT_FOUND}
|
||||
end
|
||||
end,
|
||||
case mnesia:transaction(F) of
|
||||
{atomic, {error, _} = Error} ->
|
||||
Error;
|
||||
{atomic, {result, Res}} ->
|
||||
broadcast_config_notification(Host, Node, Lang),
|
||||
{result, Res};
|
||||
_ ->
|
||||
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
@ -1222,3 +1616,4 @@ update_table(Host) ->
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user