]> granicus.if.org Git - ejabberd/commitdiff
Support for XEP-0411: Bookmarks Conversion
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 23 Nov 2018 10:33:29 +0000 (13:33 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 23 Nov 2018 10:33:29 +0000 (13:33 +0300)
rebar.config
src/jd2ejd.erl
src/mod_admin_extra.erl
src/mod_private.erl
src/mod_pubsub.erl
src/prosody2ejabberd.erl

index dcd02faa08374410f169acc23228c5dc5254c652..777842c2557a9886da82daae8a01615876f3e0f4 100644 (file)
@@ -25,7 +25,7 @@
         {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "9b25543cf1200e3b216996598771962461ea51c8"}},
         {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.14"}}},
         {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.34"}}},
-        {xmpp, ".*", {git, "https://github.com/processone/xmpp", "032548195547c68e77c7d89f51e3fbe0522fc218"}},
+        {xmpp, ".*", {git, "https://github.com/processone/xmpp", "ff6d9b5"}},
         {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.17"}}},
         {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
         {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.3"}}},
index 35ebeab8d53b736354512b8db47f80302d820140..394a5a471341f9efc840f8e79d7d79dc85687954 100644 (file)
@@ -111,7 +111,6 @@ process_xdb(User, Server,
 xdb_data(_User, _Server, {xmlcdata, _CData}) -> ok;
 xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
     From = jid:make(User, Server),
-    LUser = From#jid.luser,
     LServer = From#jid.lserver,
     case fxml:get_attr_s(<<"xmlns">>, Attrs) of
       ?NS_AUTH ->
@@ -142,7 +141,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
                                (_) -> true
                             end, Attrs),
                catch mod_private:set_data(
-                       LUser, LServer,
+                       From,
                        [{XMLNS, El#xmlel{attrs = NewAttrs}}]);
            _ ->
                ?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS])
index 5db82f82ab82e3c9f7f2a857730abe1e6d3043e1..62e07f9e94690999779480fc5079ae74e291a0db 100644 (file)
@@ -1388,8 +1388,8 @@ private_set(Username, Host, ElementString) ->
 
 private_set2(Username, Host, Xml) ->
     NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
-    mod_private:set_data(jid:nodeprep(Username), jid:nameprep(Host),
-                        [{NS, Xml}]).
+    JID = jid:make(Username, Host),
+    mod_private:set_data(JID, [{NS, Xml}]).
 
 %%%
 %%% Shared Roster Groups
index f6a57c63c5c5601d38a50b0bee2d24118618e096..7d139080a6ecb70c71f980143a89f37358742c23 100644 (file)
 -author('alexey@process-one.net').
 
 -protocol({xep, 49, '1.2'}).
+-protocol({xep, 411, '0.2.0'}).
 
 -behaviour(gen_mod).
 
 -export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0,
         remove_user/2, get_data/2, get_data/3, export/1,
-        import/5, import_start/2, mod_opt_type/1, set_data/3,
-        mod_options/1, depends/2]).
+        import/5, import_start/2, mod_opt_type/1, set_data/2,
+        mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6]).
 
 -include("logger.hrl").
 -include("xmpp.hrl").
@@ -57,16 +58,16 @@ start(Host, Opts) ->
     Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
     Mod:init(Host, Opts),
     init_cache(Mod, Host, Opts),
-    ejabberd_hooks:add(remove_user, Host, ?MODULE,
-                      remove_user, 50),
-    gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
-                                 ?NS_PRIVATE, ?MODULE, process_sm_iq).
+    ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50),
+    ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+    ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50),
+    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq).
 
 stop(Host) ->
-    ejabberd_hooks:delete(remove_user, Host, ?MODULE,
-                         remove_user, 50),
-    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
-                                    ?NS_PRIVATE).
+    ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
+    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+    ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50),
+    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE).
 
 reload(Host, NewOpts, OldOpts) ->
     NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
@@ -78,9 +79,41 @@ reload(Host, NewOpts, OldOpts) ->
     end,
     init_cache(NewMod, Host, NewOpts).
 
+depends(_Host, _Opts) ->
+    [{mod_pubsub, soft}].
+
+mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+mod_opt_type(O) when O == cache_life_time; O == cache_size ->
+    fun (I) when is_integer(I), I > 0 -> I;
+        (infinity) -> infinity
+    end;
+mod_opt_type(O) when O == use_cache; O == cache_missed ->
+    fun (B) when is_boolean(B) -> B end.
+
+mod_options(Host) ->
+    [{db_type, ejabberd_config:default_db(Host, ?MODULE)},
+     {use_cache, ejabberd_config:use_cache(Host)},
+     {cache_size, ejabberd_config:cache_size(Host)},
+     {cache_missed, ejabberd_config:cache_missed(Host)},
+     {cache_life_time, ejabberd_config:cache_life_time(Host)}].
+
+-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
+                     jid(), jid(), binary(), binary()) ->
+                            {error, stanza_error()} | empty | {result, [binary()]}.
+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+    Acc;
+get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
+    {result, [?NS_BOOKMARKS_CONVERSION_0 |
+             case Acc of
+                 {result, Features} -> Features;
+                 empty -> []
+             end]};
+get_sm_features(Acc, _From, _To, _Node, _Lang) ->
+    Acc.
+
 -spec process_sm_iq(iq()) -> iq().
 process_sm_iq(#iq{type = Type, lang = Lang,
-                 from = #jid{luser = LUser, lserver = LServer},
+                 from = #jid{luser = LUser, lserver = LServer} = From,
                  to = #jid{luser = LUser, lserver = LServer},
                  sub_els = [#private{sub_els = Els0}]} = IQ) ->
     case filter_xmlels(Els0) of
@@ -88,9 +121,11 @@ process_sm_iq(#iq{type = Type, lang = Lang,
            Txt = <<"No private data found in this query">>,
            xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
        Data when Type == set ->
-           case set_data(LUser, LServer, Data) of
+           case set_data(From, Data) of
                ok ->
                    xmpp:make_iq_result(IQ);
+               {error, #stanza_error{} = Err} ->
+                   xmpp:make_error(IQ, Err);
                {error, _} ->
                    Txt = <<"Database failure">>,
                    Err = xmpp:err_internal_server_error(Txt, Lang),
@@ -120,12 +155,21 @@ filter_xmlels(Els) ->
              end
       end, Els).
 
--spec set_data(binary(), binary(), [{binary(), xmlel()}]) -> ok | {error, _}.
-set_data(LUser, LServer, Data) ->
+-spec set_data(jid(), [{binary(), xmlel()}]) -> ok | {error, _}.
+set_data(JID, Data) ->
+    set_data(JID, Data, true).
+
+-spec set_data(jid(), [{binary(), xmlel()}], boolean()) -> ok | {error, _}.
+set_data(JID, Data, Publish) ->
+    {LUser, LServer, _} = jid:tolower(JID),
     Mod = gen_mod:db_mod(LServer, ?MODULE),
     case Mod:set_data(LUser, LServer, Data) of
        ok ->
-           delete_cache(Mod, LUser, LServer, Data);
+           delete_cache(Mod, LUser, LServer, Data),
+           case Publish of
+               true -> publish_data(JID, Data);
+               false -> ok
+           end;
        {error, _} = Err ->
            Err
     end.
@@ -181,6 +225,43 @@ remove_user(User, Server) ->
     Mod:del_data(LUser, LServer),
     delete_cache(Mod, LUser, LServer, Data).
 
+%%%===================================================================
+%%% Pubsub
+%%%===================================================================
+-spec publish_data(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}.
+publish_data(JID, Data) ->
+    {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)),
+    case gen_mod:is_loaded(LServer, mod_pubsub) of
+       true ->
+           case lists:keyfind(?NS_STORAGE_BOOKMARKS, 1, Data) of
+               false -> ok;
+               {_, El} ->
+                   PubOpts = [{persist_items, true},
+                              {access_model, whitelist}],
+                   case mod_pubsub:publish_item(
+                          LBJID, LServer, ?NS_STORAGE_BOOKMARKS, JID,
+                          <<>>, [El], PubOpts, all) of
+                       {result, _} -> ok;
+                       {error, _} = Err -> Err
+                   end
+           end;
+       false ->
+           ok
+    end.
+
+-spec pubsub_publish_item(binary(), binary(), jid(), jid(),
+                         binary(), [xmlel()]) -> any().
+pubsub_publish_item(LServer, ?NS_STORAGE_BOOKMARKS,
+                   #jid{luser = LUser, lserver = LServer} = From,
+                   #jid{luser = LUser, lserver = LServer},
+                   _ItemId, [Payload|_]) ->
+    set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], false);
+pubsub_publish_item(_, _, _, _, _, _) ->
+    ok.
+
+%%%===================================================================
+%%% Cache
+%%%===================================================================
 -spec delete_cache(module(), binary(), binary(), [{binary(), xmlel()}]) -> ok.
 delete_cache(Mod, LUser, LServer, Data) ->
     case use_cache(Mod, LServer) of
@@ -230,6 +311,9 @@ cache_nodes(Mod, Host) ->
        false -> ejabberd_cluster:get_nodes()
     end.
 
+%%%===================================================================
+%%% Import/Export
+%%%===================================================================
 import_info() ->
     [{<<"private_storage">>, 4}].
 
@@ -244,21 +328,3 @@ export(LServer) ->
 import(LServer, {sql, _}, DBType, Tab, L) ->
     Mod = gen_mod:db_mod(DBType, ?MODULE),
     Mod:import(LServer, Tab, L).
-
-depends(_Host, _Opts) ->
-    [].
-
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(O) when O == cache_life_time; O == cache_size ->
-    fun (I) when is_integer(I), I > 0 -> I;
-        (infinity) -> infinity
-    end;
-mod_opt_type(O) when O == use_cache; O == cache_missed ->
-    fun (B) when is_boolean(B) -> B end.
-
-mod_options(Host) ->
-    [{db_type, ejabberd_config:default_db(Host, ?MODULE)},
-     {use_cache, ejabberd_config:use_cache(Host)},
-     {cache_size, ejabberd_config:cache_size(Host)},
-     {cache_missed, ejabberd_config:cache_missed(Host)},
-     {cache_life_time, ejabberd_config:cache_life_time(Host)}].
index 2609beb86bd53f9c499291e3559a231732149a56..3367c192ece27c60dc883615660cf19e6726d3d2 100644 (file)
@@ -65,7 +65,7 @@
 
 %% exports for console debug manual use
 -export([create_node/5, create_node/7, delete_node/3,
-    subscribe_node/5, unsubscribe_node/5, publish_item/6,
+    subscribe_node/5, unsubscribe_node/5, publish_item/6, publish_item/8,
     delete_item/4, delete_item/5, send_items/7, get_items/2, get_item/3,
     get_cached_item/2, get_configure/5, set_configure/5,
     tree_action/3, node_action/4, node_call/4]).
index b95faad51c5ff8a77794aa7a03c6b8b6831ca134..b8d9f4da129203140bb34a1c481d106ed174cc7b 100644 (file)
@@ -169,8 +169,6 @@ convert_data(Host, "roster", User, [Data]) ->
          end, Data),
     lists:foreach(fun mod_roster:set_roster/1, Rosters);
 convert_data(Host, "private", User, [Data]) ->
-    LUser = jid:nodeprep(User),
-    LServer = jid:nameprep(Host),
     PrivData = lists:flatmap(
                 fun({_TagXMLNS, Raw}) ->
                         case deserialize(Raw) of
@@ -181,7 +179,7 @@ convert_data(Host, "private", User, [Data]) ->
                                 []
                         end
                 end, Data),
-    mod_private:set_data(LUser, LServer, PrivData);
+    mod_private:set_data(jid:make(User, Host), PrivData);
 convert_data(Host, "vcard", User, [Data]) ->
     LServer = jid:nameprep(Host),
     case deserialize(Data) of