aboutsummaryrefslogtreecommitdiff
path: root/src/mod_disco.erl
diff options
context:
space:
mode:
authorAlexey Shchepin <alexey@process-one.net>2005-09-04 03:31:23 +0000
committerAlexey Shchepin <alexey@process-one.net>2005-09-04 03:31:23 +0000
commita83c99805add005d9bc47a0d80c74a59067f4dda (patch)
tree11318f9abe5fa66279a4d16a6ba036d984fb31ef /src/mod_disco.erl
parent* src/mod_disco.erl: Functions register_sm_feature and (diff)
* src/mod_disco.erl: Disco publishing support (thanks to Magnus
Henoch) SVN Revision: 409
Diffstat (limited to '')
-rw-r--r--src/mod_disco.erl141
1 files changed, 139 insertions, 2 deletions
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index b25f79029..f04d4d952 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -24,6 +24,7 @@
get_sm_identity/5,
get_sm_features/5,
get_sm_items/5,
+ get_publish_items/5,
register_feature/2,
unregister_feature/2,
register_extra_domain/2,
@@ -32,7 +33,15 @@
-include("ejabberd.hrl").
-include("jlib.hrl").
+-record(disco_publish, {owner_node, jid, name, node}).
+
start(Host, Opts) ->
+ mnesia:create_table(disco_publish,
+ [{disc_only_copies, [node()]},
+ {attributes, record_info(fields, disco_publish)},
+ {type, bag}]),
+ mnesia:add_table_index(disco_publish, owner_node),
+
ejabberd_local:refresh_iq_handlers(),
IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),
@@ -49,6 +58,7 @@ start(Host, Opts) ->
register_feature(Host, "iq"),
register_feature(Host, "presence"),
register_feature(Host, "presence-invisible"),
+ register_feature(Host, "http://jabber.org/protocol/disco#publish"),
catch ets:new(disco_extra_domains, [named_table, ordered_set, public]),
ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []),
@@ -62,9 +72,11 @@ start(Host, Opts) ->
ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 100),
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 100),
ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100),
+ ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_publish_items, 75),
ok.
stop(Host) ->
+ ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_publish_items, 75),
ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100),
ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 100),
ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 100),
@@ -237,7 +249,24 @@ get_vh_services(Host) ->
process_sm_iq_items(From, To, #iq{type = Type, lang = Lang, sub_el = SubEl} = IQ) ->
case Type of
set ->
- IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]};
+ #jid{user = User, luser = LTo, lserver = ToServer} = To,
+ #jid{luser = LFrom, lserver = LServer} = From,
+ Self = (LTo == LFrom) andalso (ToServer == LServer),
+ Node = xml:get_tag_attr_s("node", SubEl),
+ if
+ Self, Node /= [] ->
+ %% Here, we treat disco publish attempts to your own JID.
+ {xmlelement, _, _, Items} = SubEl,
+ case process_disco_publish({LFrom, LServer}, Node, Items) of
+ ok ->
+ IQ#iq{type = result, sub_el = []};
+ {error, Err} ->
+ IQ#iq{type = error, sub_el = [SubEl, Err]}
+ end;
+
+ true ->
+ IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}
+ end;
get ->
Host = To#jid.lserver,
SNode = xml:get_tag_attr_s("node", SubEl),
@@ -265,7 +294,7 @@ get_sm_items({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
Acc;
get_sm_items(Acc,
- #jid{luser = LFrom, lserver = LSFrom} = _From,
+ #jid{luser = LFrom, lserver = LSFrom},
#jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
[], _Lang) ->
Items = case Acc of
@@ -349,3 +378,111 @@ get_user_resources(User, Server) ->
{"name", User}], []}
end, lists:sort(Rs)).
+
+get_publish_items(empty,
+ #jid{luser = LFrom, lserver = LSFrom},
+ #jid{user = User, server = Server, luser = LTo, lserver = LSTo} = _To,
+ Node, _Lang) ->
+ if
+ (LFrom == LTo) and (LSFrom == LSTo) ->
+ % Hack
+ SNode = join(Node, "/"),
+ retrieve_disco_publish({LTo, LSTo}, SNode);
+ true ->
+ empty
+ end;
+get_publish_items(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
+join(List, Sep) ->
+ lists:foldl(fun(A, "") -> A;
+ (A, Acc) -> Acc ++ Sep ++ A
+ end, "", List).
+
+process_disco_publish(User, Node, Items) ->
+ F = fun() ->
+ lists:foreach(
+ fun({xmlelement, _Name, _Attrs, _SubEls} = Item) ->
+ Action = xml:get_tag_attr_s("action", Item),
+ Jid = xml:get_tag_attr_s("jid", Item),
+ PNode = xml:get_tag_attr_s("node", Item),
+ Name = xml:get_tag_attr_s("name", Item),
+ ?INFO_MSG("Disco publish: ~p ~p ~p ~p ~p ~p~n",
+ [User, Action, Node, Jid, PNode, Name]),
+
+ %% The disco_publish table isn't strictly a "bag" table, as
+ %% entries with same jid and node combination are considered
+ %% the same, even if they have different names. Therefore,
+ %% we find a list of items to supersede.
+ SupersededItems = mnesia:match_object(
+ #disco_publish{owner_node = {User, Node},
+ jid = Jid,
+ node = PNode,
+ _ = '_'}),
+ case Action of
+ "update" ->
+ lists:map(
+ fun(O) ->
+ mnesia:delete_object(O)
+ end, SupersededItems),
+ mnesia:write(
+ #disco_publish{owner_node = {User, Node},
+ jid = Jid,
+ name = Name,
+ node = PNode});
+ "remove" ->
+ case SupersededItems of
+ [] ->
+ mnesia:abort({error, ?ERR_ITEM_NOT_FOUND});
+ _ ->
+ lists:map(
+ fun(O) ->
+ mnesia:delete_object(O)
+ end, SupersededItems)
+ end;
+ _ ->
+ %% invalid "action" attribute - return an error
+ mnesia:abort({error, ?ERR_BAD_REQUEST})
+ end;
+ ({xmlcdata, _CDATA}) ->
+ ok
+ end, Items)
+ end,
+ case mnesia:transaction(F) of
+ {aborted, {error, _} = Error} ->
+ Error;
+ {atomic, _} ->
+ ok;
+ _ ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR}
+ end.
+
+retrieve_disco_publish(User, Node) ->
+ case catch mnesia:dirty_read({disco_publish, {User, Node}}) of
+ {'EXIT', _Reason} ->
+ {error, ?ERR_INTERNAL_SERVER_ERROR};
+ [] ->
+ empty;
+ Items ->
+ {result,
+ lists:map(
+ fun(#disco_publish{jid = Jid,
+ name = Name,
+ node = PNode}) ->
+ {xmlelement, "item",
+ lists:append([[{"jid", Jid}],
+ case Name of
+ "" ->
+ [];
+ _ ->
+ [{"name", Name}]
+ end,
+ case PNode of
+ "" ->
+ [];
+ _ ->
+ [{"node", PNode}]
+ end]), []}
+ end, Items)}
+ end.
+