]> granicus.if.org Git - ejabberd/commitdiff
* src/mod_disco.erl: Disco publishing support (thanks to Magnus
authorAlexey Shchepin <alexey@process-one.net>
Sun, 4 Sep 2005 03:31:23 +0000 (03:31 +0000)
committerAlexey Shchepin <alexey@process-one.net>
Sun, 4 Sep 2005 03:31:23 +0000 (03:31 +0000)
Henoch)

SVN Revision: 409

ChangeLog
src/mod_disco.erl

index 0579f2e29a23e220ea874633109a8d6fb519cd0b..519b97212d73a1b1dd366846bfa89069ba6c2717 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,12 @@
 2005-09-04  Alexey Shchepin  <alexey@sevcom.net>
 
+       * src/mod_disco.erl: Disco publishing support (thanks to Magnus
+       Henoch)
+
        * src/mod_disco.erl: Functions register_sm_feature and
        register_sm_node replaced with hooks (thanks to Sergei Golovan)
-       * src/mod_vcard.erl: 
-       * src/mod_vcard_ldap.erl: 
+       * src/mod_vcard.erl: Updated
+       * src/mod_vcard_ldap.erl: Likewise
 
        * src/mod_disco.erl: Now mod_disco doesn't depend on mod_configure
        (thanks to Sergei Golovan)
index b25f7902966bcdd98c3f64c646c7f9471375573f..f04d4d952314bdb8f9eab18cbba997c028639228 100644 (file)
@@ -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,
 -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.
+