25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-22 16:20:52 +01:00

Rewrite PIEFXIS code to use XML generator

This commit is contained in:
Evgeniy Khramtsov 2016-08-04 13:37:07 +03:00
parent cbdc106427
commit 8b81b9ecb1
4 changed files with 118 additions and 145 deletions

View File

@ -48,7 +48,7 @@
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-include("xmpp.hrl").
-include("mod_privacy.hrl").
-include("mod_roster.hrl").
@ -209,31 +209,28 @@ parse_scram_password(PassData) ->
iterationcount = list_to_integer(binary_to_list(IterationCountBin))
}.
-spec get_vcard(binary(), binary()) -> [xmlel()].
get_vcard(User, Server) ->
JID = jid:make(User, Server, <<>>),
case mod_vcard:process_sm_iq(JID, JID, #iq{type = get}) of
#iq{type = result, sub_el = [_|_] = VCardEls} ->
VCardEls;
_ ->
[]
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
case mod_vcard:get_vcard(LUser, LServer) of
error -> [];
Els -> Els
end.
-spec get_offline(binary(), binary()) -> [xmlel()].
get_offline(User, Server) ->
case mod_offline:get_offline_els(User, Server) of
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
case mod_offline:get_offline_els(LUser, LServer) of
[] ->
[];
Els ->
NewEls = lists:map(
fun(#xmlel{attrs = Attrs} = El) ->
NewAttrs = lists:keystore(<<"xmlns">>, 1,
Attrs,
{<<"xmlns">>,
<<"jabber:client">>}),
El#xmlel{attrs = NewAttrs}
end, Els),
NewEls = lists:map(fun xmpp:encode/1, Els),
[#xmlel{name = <<"offline-messages">>, children = NewEls}]
end.
-spec get_privacy(binary(), binary()) -> [xmlel()].
get_privacy(User, Server) ->
case mod_privacy:get_user_lists(User, Server) of
{ok, #privacy{default = Default,
@ -241,25 +238,16 @@ get_privacy(User, Server) ->
XLists = lists:map(
fun({Name, Items}) ->
XItems = lists:map(
fun mod_privacy:item_to_xml/1, Items),
#xmlel{name = <<"list">>,
attrs = [{<<"name">>, Name}],
children = XItems}
fun mod_privacy:encode_list_item/1,
Items),
#privacy_list{name = Name, items = XItems}
end, Lists),
DefaultEl = case Default of
none ->
[];
_ ->
[#xmlel{name = <<"default">>,
attrs = [{<<"name">>, Default}]}]
end,
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_PRIVACY}],
children = DefaultEl ++ XLists}];
[xmpp:encode(#privacy_query{default = Default, lists = XLists})];
_ ->
[]
end.
-spec get_roster(binary(), binary()) -> [xmlel()].
get_roster(User, Server) ->
JID = jid:make(User, Server, <<>>),
case mod_roster:get_roster(User, Server) of
@ -272,18 +260,11 @@ get_roster(User, Server) ->
Status = if is_binary(Msg) -> (Msg);
true -> <<"">>
end,
[#xmlel{name = <<"presence">>,
attrs =
[{<<"from">>,
jid:to_string(R#roster.jid)},
{<<"to">>, jid:to_string(JID)},
{<<"xmlns">>, <<"jabber:client">>},
{<<"type">>, <<"subscribe">>}],
children =
[#xmlel{name = <<"status">>,
attrs = [],
children =
[{xmlcdata, Status}]}]}];
[xmpp:encode(
#presence{from = jid:make(R#roster.jid),
to = JID,
type = subscribe,
status = xmpp:mk_text(Status)})];
(_) ->
[]
end, Items),
@ -291,21 +272,18 @@ get_roster(User, Server) ->
fun(#roster{ask = in, subscription = none}) ->
[];
(R) ->
[mod_roster:item_to_xml(R)]
[mod_roster:encode_item(R)]
end, Items),
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_ROSTER}],
children = Rs} | Subs];
[xmpp:encode(#roster_query{items = Rs}) | Subs];
_ ->
[]
end.
-spec get_private(binary(), binary()) -> [xmlel()].
get_private(User, Server) ->
case mod_private:get_data(User, Server) of
[_|_] = Els ->
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_PRIVATE}],
children = Els}];
[xmpp:encode(#private{xml_els = Els})];
_ ->
[]
end.
@ -451,129 +429,124 @@ process_user_els([], State) ->
process_user_el(#xmlel{name = Name, attrs = Attrs, children = Els} = El,
State) ->
case {Name, fxml:get_attr_s(<<"xmlns">>, Attrs)} of
{<<"query">>, ?NS_ROSTER} ->
process_roster(El, State);
{<<"query">>, ?NS_PRIVACY} ->
%% Make sure <list/> elements go before <active/> and <default/>
NewEls = lists:reverse(lists:keysort(#xmlel.name, Els)),
process_privacy_el(El#xmlel{children = NewEls}, State);
{<<"query">>, ?NS_PRIVATE} ->
process_private(El, State);
{<<"vCard">>, ?NS_VCARD} ->
process_vcard(El, State);
{<<"offline-messages">>, _} ->
process_offline_msgs(Els, State);
{<<"presence">>, <<"jabber:client">>} ->
process_presence(El, State);
_ ->
{ok, State}
try
case {Name, fxml:get_attr_s(<<"xmlns">>, Attrs)} of
{<<"query">>, ?NS_ROSTER} ->
process_roster(xmpp:decode(El), State);
{<<"query">>, ?NS_PRIVACY} ->
%% Make sure <list/> elements go before <active/> and <default/>
process_privacy(xmpp:decode(El), State);
{<<"query">>, ?NS_PRIVATE} ->
process_private(xmpp:decode(El), State);
{<<"vCard">>, ?NS_VCARD} ->
process_vcard(El, State);
{<<"offline-messages">>, _} ->
Msgs = [xmpp:decode(E, [ignore_els]) || E <- Els],
process_offline_msgs(Msgs, State);
{<<"presence">>, <<"jabber:client">>} ->
process_presence(xmpp:decode(El, [ignore_els]), State);
_ ->
{ok, State}
end
catch _:{xmpp_codec, Why} ->
ErrTxt = xmpp:format_error(Why),
stop("failed to decode XML '~s': ~s",
[fxml:element_to_binary(El), ErrTxt])
end.
process_privacy_el(#xmlel{children = [#xmlel{} = SubEl|SubEls]} = El, State) ->
case process_privacy(#xmlel{children = [SubEl]}, State) of
-spec process_offline_msgs([stanza()], state()) -> {ok, state()} | {error, _}.
process_offline_msgs([#message{} = Msg|Msgs], State) ->
case process_offline_msg(Msg, State) of
{ok, NewState} ->
process_privacy_el(El#xmlel{children = SubEls}, NewState);
process_offline_msgs(Msgs, NewState);
Err ->
Err
end;
process_privacy_el(#xmlel{children = [_|SubEls]} = El, State) ->
process_privacy_el(El#xmlel{children = SubEls}, State);
process_privacy_el(#xmlel{children = []}, State) ->
{ok, State}.
process_offline_msgs([#xmlel{} = El|Els], State) ->
case process_offline_msg(El, State) of
{ok, NewState} ->
process_offline_msgs(Els, NewState);
Err ->
Err
end;
process_offline_msgs([_|Els], State) ->
process_offline_msgs(Els, State);
process_offline_msgs([_|Msgs], State) ->
process_offline_msgs(Msgs, State);
process_offline_msgs([], State) ->
{ok, State}.
process_roster(El, State = #state{user = U, server = S}) ->
case mod_roster:set_items(U, S, El) of
-spec process_roster(roster_query(), state()) -> {ok, state()} | {error, _}.
process_roster(RosterQuery, State = #state{user = U, server = S}) ->
case mod_roster:set_items(U, S, RosterQuery) of
{atomic, _} ->
{ok, State};
Err ->
stop("Failed to write roster: ~p", [Err])
end.
process_privacy(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_privacy:process_iq_set(
[], JID, JID, #iq{type = set, sub_el = El}) of
{error, Error} = Err ->
#xmlel{children = Els} = El,
Name = case fxml:remove_cdata(Els) of
[#xmlel{name = N}] -> N;
_ -> undefined
end,
#xmlel{attrs = Attrs} = Error,
ErrorCode = case lists:keysearch(<<"code">>, 1, Attrs) of
{value, {_, V}} -> V;
false -> undefined
end,
if
ErrorCode == <<"404">>, Name == <<"default">> ->
{ok, State};
true ->
stop("Failed to write privacy: ~p", [Err])
-spec process_privacy(privacy_query(), state()) -> {ok, state()} | {error, _}.
process_privacy(#privacy_query{lists = Lists,
default = Default,
active = Active} = PrivacyQuery,
State = #state{user = U, server = S}) ->
JID = jid:make(U, S),
IQ = #iq{type = set, id = randoms:get_string(),
from = JID, to = JID, sub_els = [PrivacyQuery]},
Txt = <<"No module is handling this query">>,
Error = {error, xmpp:err_feature_not_implemented(Txt, ?MYLANG)},
case mod_privacy:process_iq_set(Error, IQ) of
{error, #error{reason = Reason}} = Err ->
if Reason == 'item-not-found', Lists == [],
Active == undefined, Default /= undefined ->
%% Failed to set default list because there is no
%% list with such name. We shouldn't stop here.
{ok, State};
true ->
stop("Failed to write privacy: ~p", [Err])
end;
_ ->
{ok, State}
end.
process_private(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_private:process_sm_iq(
JID, JID, #iq{type = set, sub_el = El}) of
-spec process_private(private(), state()) -> {ok, state()} | {error, _}.
process_private(Private, State = #state{user = U, server = S}) ->
JID = jid:make(U, S),
IQ = #iq{type = set, id = randoms:get_string(),
from = JID, to = JID, sub_els = [Private]},
case mod_private:process_sm_iq(IQ) of
#iq{type = result} ->
{ok, State};
Err ->
stop("Failed to write private: ~p", [Err])
end.
-spec process_vcard(xmlel(), state()) -> {ok, state()} | {error, _}.
process_vcard(El, State = #state{user = U, server = S}) ->
JID = jid:make(U, S, <<"">>),
case mod_vcard:process_sm_iq(
JID, JID, #iq{type = set, sub_el = El}) of
JID = jid:make(U, S),
IQ = #iq{type = set, id = randoms:get_string(),
from = JID, to = JID, sub_els = [El]},
case mod_vcard:process_sm_iq(IQ) of
#iq{type = result} ->
{ok, State};
Err ->
stop("Failed to write vcard: ~p", [Err])
end.
process_offline_msg(El, State = #state{user = U, server = S}) ->
FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
#jid{} = From ->
To = jid:make(U, S, <<>>),
NewEl = jlib:replace_from_to(From, To, El),
case catch mod_offline:store_packet(From, To, NewEl) of
{'EXIT', _} = Err ->
stop("Failed to store offline message: ~p", [Err]);
_ ->
{ok, State}
end;
_ ->
stop("Invalid 'from' = ~s", [FromS])
-spec process_offline_msg(message(), state()) -> {ok, state()} | {error, _}.
process_offline_msg(#message{from = undefined}, _State) ->
stop("No 'from' attribute found", []);
process_offline_msg(Msg, State = #state{user = U, server = S}) ->
From = xmpp:get_from(Msg),
To = jid:make(U, S, <<>>),
NewMsg = xmpp:set_from_to(Msg, From, To),
case catch mod_offline:store_packet(From, To, NewMsg) of
{'EXIT', _} = Err ->
stop("Failed to store offline message: ~p", [Err]);
_ ->
{ok, State}
end.
process_presence(El, #state{user = U, server = S} = State) ->
FromS = fxml:get_attr_s(<<"from">>, El#xmlel.attrs),
case jid:from_string(FromS) of
#jid{} = From ->
To = jid:make(U, S, <<>>),
NewEl = jlib:replace_from_to(From, To, El),
ejabberd_router:route(From, To, NewEl),
{ok, State};
_ ->
stop("Invalid 'from' = ~s", [FromS])
end.
-spec process_presence(presence(), state()) -> {ok, state()} | {error, _}.
process_presence(#presence{from = undefined}, _State) ->
stop("No 'from' attribute found", []);
process_presence(Pres, #state{user = U, server = S} = State) ->
From = xmpp:get_from(Pres),
To = jid:make(U, S, <<>>),
NewPres = xmpp:set_from_to(Pres, From, To),
ejabberd_router:route(From, To, NewPres),
{ok, State}.
stop(Fmt, Args) ->
?ERROR_MSG(Fmt, Args),

View File

@ -120,7 +120,8 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
ejabberd_auth:set_password(User, Server, Password),
ok;
?NS_ROSTER ->
catch mod_roster:set_items(User, Server, El), ok;
catch mod_roster:set_items(User, Server, xmpp:decode(El)),
ok;
?NS_LAST ->
TimeStamp = fxml:get_attr_s(<<"last">>, Attrs),
Status = fxml:get_tag_cdata(El),

View File

@ -33,7 +33,7 @@
-export([start/2, stop/1, process_iq/1, export/1, import/1,
process_iq_set/2, process_iq_get/3, get_user_list/3,
check_packet/6, remove_user/2,
check_packet/6, remove_user/2, encode_list_item/1,
is_list_needdb/1, updated_list/3,
item_to_xml/1, get_user_lists/2, import/3,
set_privacy_list/1, mod_opt_type/1, depends/2]).

View File

@ -688,15 +688,14 @@ send_unsubscribing_presence(From, Item) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
set_items(User, Server, SubEl) ->
#xmlel{children = Els} = SubEl,
-spec set_items(binary(), binary(), roster_query()) -> any().
set_items(User, Server, #roster_query{items = Items}) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
F = fun () ->
lists:foreach(fun (El) ->
process_item_set_t(LUser, LServer, El)
end,
Els)
lists:foreach(fun (Item) ->
process_item_set_t(LUser, LServer, Item)
end, Items)
end,
transaction(LServer, F).