From 1cdca1ab99c8a0dfd7cab8a760a65840067bea16 Mon Sep 17 00:00:00 2001 From: Evgeny Khramtsov Date: Fri, 23 Nov 2018 13:33:29 +0300 Subject: [PATCH] Support for XEP-0411: Bookmarks Conversion --- rebar.config | 2 +- src/jd2ejd.erl | 3 +- src/mod_admin_extra.erl | 4 +- src/mod_private.erl | 132 +++++++++++++++++++++++++++++---------- src/mod_pubsub.erl | 2 +- src/prosody2ejabberd.erl | 4 +- 6 files changed, 105 insertions(+), 42 deletions(-) diff --git a/rebar.config b/rebar.config index dcd02faa0..777842c25 100644 --- a/rebar.config +++ b/rebar.config @@ -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"}}}, diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl index 35ebeab8d..394a5a471 100644 --- a/src/jd2ejd.erl +++ b/src/jd2ejd.erl @@ -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]) diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 5db82f82a..62e07f9e9 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -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 diff --git a/src/mod_private.erl b/src/mod_private.erl index f6a57c63c..7d139080a 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -28,13 +28,14 @@ -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)}]. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 2609beb86..3367c192e 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -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]). diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index b95faad51..b8d9f4da1 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -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