]> granicus.if.org Git - ejabberd/commitdiff
Rewrite multicast code to use XML generator
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 4 Aug 2016 08:49:17 +0000 (11:49 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 4 Aug 2016 08:49:17 +0000 (11:49 +0300)
src/ejabberd_router_multicast.erl
src/mod_multicast.erl

index fa32c8ed7ac1a62b444e9150501b58a729898e1d..967699007abb483cef24516641429f738d44a30d 100644 (file)
 
 -include("ejabberd.hrl").
 -include("logger.hrl").
--include("jlib.hrl").
+-include("xmpp.hrl").
 
--record(route_multicast, {domain, pid}).
+-record(route_multicast, {domain = <<"">> :: binary(),
+                         pid = self() :: pid()}).
 -record(state, {}).
 
 %%====================================================================
@@ -58,7 +59,7 @@
 start_link() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
-
+-spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok.
 route_multicast(From, Domain, Destinations, Packet) ->
     case catch do_route(From, Domain, Destinations, Packet) of
        {'EXIT', Reason} ->
@@ -68,6 +69,7 @@ route_multicast(From, Domain, Destinations, Packet) ->
            ok
     end.
 
+-spec register_route(binary()) -> any().
 register_route(Domain) ->
     case jid:nameprep(Domain) of
        error ->
@@ -81,6 +83,7 @@ register_route(Domain) ->
            mnesia:transaction(F)
     end.
 
+-spec unregister_route(binary()) -> any().
 unregister_route(Domain) ->
     case jid:nameprep(Domain) of
        error ->
@@ -206,6 +209,7 @@ code_change(_OldVsn, State, _Extra) ->
 %%--------------------------------------------------------------------
 %% From = #jid
 %% Destinations = [#jid]
+-spec do_route(jid(), binary(), [jid()], stanza()) -> any().
 do_route(From, Domain, Destinations, Packet) ->
 
     ?DEBUG("route_multicast~n\tfrom ~s~n\tdomain ~s~n\tdestinations ~p~n\tpacket ~p~n",
@@ -226,6 +230,7 @@ do_route(From, Domain, Destinations, Packet) ->
            Pid ! {route_trusted, From, Destinations, Packet}
     end.
 
+-spec pick_multicast_pid([#route_multicast{}]) -> pid().
 pick_multicast_pid(Rs) ->
     List = case [R || R <- Rs, node(R#route_multicast.pid) == node()] of
        [] -> Rs;
@@ -233,5 +238,6 @@ pick_multicast_pid(Rs) ->
     end,
     (hd(List))#route_multicast.pid.
 
+-spec do_route_normal(jid(), [jid()], stanza()) -> any().
 do_route_normal(From, Destinations, Packet) ->
     [ejabberd_router:route(From, To, Packet) || To <- Destinations].
index df385c28caedfb47a00ab39cead84b9bd0aa871e..f1c090ae4d9b89817ac03ba19fd985c03fc2d7eb 100644 (file)
 -include("ejabberd.hrl").
 -include("logger.hrl").
 
--include("jlib.hrl").
+-include("xmpp.hrl").
 
 -record(state,
        {lserver, lservice, access, service_limits}).
+-type state() :: #state{}.
 
 -record(multicastc, {rserver, response, ts}).
 
 %% ts: timestamp (in seconds) when the cache item was last updated
 
--record(dest, {jid_string, jid_jid, type, full_xml}).
+-record(dest, {jid_string = none :: binary(),
+              jid_jid :: jid(),
+              type :: atom(),
+              full_xml :: address()}).
 
 %% jid_string = string()
 %% jid_jid = jid()
@@ -168,10 +172,8 @@ handle_cast(_Msg, State) -> {noreply, State}.
 %% Description: Handling all non call/cast messages
 %%--------------------------------------------------------------------
 
-handle_info({route, From, To,
-            #xmlel{name = <<"iq">>, attrs = Attrs} = Packet},
-           State) ->
-    case catch handle_iq(From, To, #xmlel{attrs = Attrs} = Packet, State) of
+handle_info({route, From, To, #iq{} = Packet}, State) ->
+    case catch handle_iq(From, To, Packet, State) of
         {'EXIT', Reason} ->
             ?ERROR_MSG("Error when processing IQ stanza: ~p",
                        [Reason]);
@@ -179,13 +181,10 @@ handle_info({route, From, To,
     end,
     {noreply, State};
 %% XEP33 allows only 'message' and 'presence' stanza type
-handle_info({route, From, To,
-            #xmlel{name = Stanza_type} = Packet},
+handle_info({route, From, To, Packet},
            #state{lservice = LServiceS, lserver = LServerS,
                   access = Access, service_limits = SLimits} =
-               State)
-    when (Stanza_type == <<"message">>) or
-          (Stanza_type == <<"presence">>) ->
+               State) when ?is_stanza(Packet) ->
     route_untrusted(LServiceS, LServerS, Access, SLimits,
                    From, To, Packet),
     {noreply, State};
@@ -220,91 +219,59 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
 %%% IQ Request Processing
 %%%------------------------
 
-handle_iq(From, To, #xmlel{attrs = Attrs} = Packet, State) ->
-    IQ = jlib:iq_query_info(Packet),
-    case catch process_iq(From, IQ, State) of
-        Result when is_record(Result, iq) ->
-            ejabberd_router:route(To, From, jlib:iq_to_xml(Result));
-        {'EXIT', Reason} ->
-            ?ERROR_MSG("Error when processing IQ stanza: ~p",
-                       [Reason]),
-            Err = jlib:make_error_reply(Packet,
-                                        ?ERR_INTERNAL_SERVER_ERROR),
-            ejabberd_router:route(To, From, Err);
-        reply ->
-            LServiceS = jts(To),
-            case fxml:get_attr_s(<<"type">>, Attrs) of
-                <<"result">> ->
-                    process_iqreply_result(From, LServiceS, Packet, State);
-                <<"error">> ->
-                    process_iqreply_error(From, LServiceS, Packet)
-            end;
-        ok -> ok
+handle_iq(From, To, Packet, State) ->
+    try
+       IQ = xmpp:decode_els(Packet),
+       case process_iq(From, IQ, State) of
+           {result, SubEl} ->
+               ejabberd_router:route(To, From, xmpp:make_iq_result(Packet, SubEl));
+           {error, Error} ->
+               ejabberd_router:route_error(To, From, Packet, Error);
+           reply ->
+               LServiceS = jid:to_string(To),
+               case Packet#iq.type of
+                   result ->
+                       process_iqreply_result(From, LServiceS, IQ);
+                   error ->
+                       process_iqreply_error(From, LServiceS, IQ)
+               end
+       end
+    catch _:{xmpp_codec, Why} ->
+           Lang = xmpp:get_lang(Packet),
+           Err = xmpp:err_bad_request(xmpp:format_error(Why), Lang),
+           ejabberd_router:route_error(To, From, Packet, Err)
     end.
 
-process_iq(From,
-          #iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} =
-              IQ,
-          State) ->
-    IQ#iq{type = result,
-         sub_el =
-             [#xmlel{name = <<"query">>,
-                     attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
-                     children = iq_disco_info(From, Lang, State)}]};
-%% disco#items request
-process_iq(_,
-          #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ, _) ->
-    IQ#iq{type = result,
-         sub_el =
-             [#xmlel{name = <<"query">>,
-                     attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}],
-                     children = []}]};
-%% vCard request
-process_iq(_,
-          #iq{type = get, xmlns = ?NS_VCARD, lang = Lang} = IQ,
-          _) ->
-    IQ#iq{type = result,
-         sub_el =
-             [#xmlel{name = <<"vCard">>,
-                     attrs = [{<<"xmlns">>, ?NS_VCARD}],
-                     children = iq_vcard(Lang)}]};
-%% Unknown "set" or "get" request
-process_iq(_, #iq{type = Type, sub_el = SubEl} = IQ, _)
-    when Type == get; Type == set ->
-    IQ#iq{type = error,
-         sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
-%% IQ "result" or "error".
-process_iq(_, reply, _) -> reply;
-%% IQ "result" or "error".
-process_iq(_, _, _) -> ok.
-
--define(FEATURE(Feat),
-       #xmlel{name = <<"feature">>,
-              attrs = [{<<"var">>, Feat}], children = []}).
+-spec process_iq(jid(), iq(), state()) -> {result, xmpp_element()} |
+                                         {error, error()} | reply.
+process_iq(From, #iq{type = get, lang = Lang,
+                    sub_els = [#disco_info{}]}, State) ->
+    {result, iq_disco_info(From, Lang, State)};
+process_iq(_, #iq{type = get, sub_els = [#disco_items{}]}, _) ->
+    {result, #disco_items{}};
+process_iq(_, #iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]}, _) ->
+    {result, iq_vcard(Lang)};
+process_iq(_, #iq{type = T}, _) when T == set; T == get ->
+    {error, xmpp:err_service_unavailable()};
+process_iq(_, _, _) ->
+    reply.
+
+-define(FEATURE(Feat), Feat).
 
 iq_disco_info(From, Lang, State) ->
-    [#xmlel{name = <<"identity">>,
-           attrs =
-               [{<<"category">>, <<"service">>},
-                {<<"type">>, <<"multicast">>},
-                {<<"name">>,
-                 translate:translate(Lang, <<"Multicast">>)}],
-           children = []},
-     ?FEATURE((?NS_DISCO_INFO)), ?FEATURE((?NS_DISCO_ITEMS)),
-     ?FEATURE((?NS_VCARD)), ?FEATURE((?NS_ADDRESS))]
-      ++ iq_disco_info_extras(From, State).
+    #disco_info{
+       identities = [#identity{category = <<"service">>,
+                              type = <<"multicast">>,
+                              name = translate:translate(Lang, <<"Multicast">>)}],
+       features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_VCARD, ?NS_ADDRESS],
+       xdata = iq_disco_info_extras(From, State)}.
 
 iq_vcard(Lang) ->
-    [#xmlel{name = <<"FN">>, attrs = [],
-           children = [{xmlcdata, <<"ejabberd/mod_multicast">>}]},
-     #xmlel{name = <<"URL">>, attrs = [],
-           children = [{xmlcdata, ?EJABBERD_URI}]},
-     #xmlel{name = <<"DESC">>, attrs = [],
-           children =
-               [{xmlcdata,
-                  <<(translate:translate(Lang,
-                                      <<"ejabberd Multicast service">>))/binary,
-                                        "\nCopyright (c) 2002-2016 ProcessOne">>}]}].
+    Desc = translate:translate(Lang, <<"ejabberd Multicast service">>),
+    Copyright = <<"Copyright (c) 2002-2016 ProcessOne">>,
+    #vcard_temp{fn = <<"ejabberd/mod_multicast">>,
+               url = ?EJABBERD_URI,
+               desc = <<Desc/binary, $\n, Copyright/binary>>}.
 
 %%%-------------------------
 %%% Route
@@ -313,19 +280,14 @@ iq_vcard(Lang) ->
 route_trusted(LServiceS, LServerS, FromJID,
              Destinations, Packet) ->
     Packet_stripped = Packet,
-    AAttrs = [{<<"xmlns">>, ?NS_ADDRESS}],
+    AAttrs = [],
     Delivereds = [],
-    Dests2 = lists:map(fun (D) ->
-                              DS = jts(D),
-                              XML = #xmlel{name = <<"address">>,
-                                           attrs =
-                                               [{<<"type">>, <<"bcc">>},
-                                                {<<"jid">>, DS}],
-                                           children = []},
-                              #dest{jid_string = DS, jid_jid = D,
-                                    type = <<"bcc">>, full_xml = XML}
-                      end,
-                      Destinations),
+    Dests2 = lists:map(
+              fun(D) ->
+                      #dest{jid_string = jid:to_string(D),
+                            jid_jid = D, type = bcc,
+                            full_xml = #address{type = bcc, jid = D}}
+              end, Destinations),
     Groups = group_dests(Dests2),
     route_common(LServerS, LServiceS, FromJID, Groups,
                 Delivereds, Packet_stripped, AAttrs).
@@ -363,20 +325,19 @@ route_untrusted(LServiceS, LServerS, Access, SLimits,
 route_untrusted2(LServiceS, LServerS, Access, SLimits,
                 FromJID, Packet) ->
     ok = check_access(LServerS, Access, FromJID),
-    {ok, Packet_stripped, AAttrs, Addresses} =
-       strip_addresses_element(Packet),
-    {To_deliver, Delivereds} =
-       split_addresses_todeliver(Addresses),
+    {ok, Packet_stripped, Addresses} = strip_addresses_element(Packet),
+    {To_deliver, Delivereds} = split_addresses_todeliver(Addresses),
     Dests = convert_dest_record(To_deliver),
     {Dests2, Not_jids} = split_dests_jid(Dests),
     report_not_jid(FromJID, Packet, Not_jids),
-    ok = check_limit_dests(SLimits, FromJID, Packet,
-                          Dests2),
+    ok = check_limit_dests(SLimits, FromJID, Packet, Dests2),
     Groups = group_dests(Dests2),
     ok = check_relay(FromJID#jid.server, LServerS, Groups),
     route_common(LServerS, LServiceS, FromJID, Groups,
-                Delivereds, Packet_stripped, AAttrs).
+                Delivereds, Packet_stripped, []).
 
+-spec route_common(binary(), binary(), jid(), [#group{}],
+                  [address()], stanza(), list()) -> any().
 route_common(LServerS, LServiceS, FromJID, Groups,
             Delivereds, Packet_stripped, AAttrs) ->
     Groups2 = look_cached_servers(LServerS, Groups),
@@ -435,52 +396,39 @@ check_access(LServerS, Access, From) ->
 %%% Strip 'addresses' XML element
 %%%-------------------------
 
+-spec strip_addresses_element(stanza()) -> {ok, stanza(), [address()]}.
 strip_addresses_element(Packet) ->
-    case fxml:get_subtag(Packet, <<"addresses">>) of
-      #xmlel{name = <<"addresses">>, attrs = AAttrs,
-            children = Addresses} ->
-         case fxml:get_attr_s(<<"xmlns">>, AAttrs) of
-           ?NS_ADDRESS ->
-               #xmlel{name = Name, attrs = Attrs, children = Els} =
-                   Packet,
-               Els_stripped = lists:keydelete(<<"addresses">>, 2, Els),
-               Packet_stripped = #xmlel{name = Name, attrs = Attrs,
-                                        children = Els_stripped},
-               {ok, Packet_stripped, AAttrs, fxml:remove_cdata(Addresses)};
-           _ -> throw(ewxmlns)
-         end;
-      _ -> throw(eadsele)
+    case xmpp:get_subtag(Packet, #addresses{}) of
+       #addresses{list = Addrs} ->
+           PacketStripped = xmpp:remove_subtag(Packet, #addresses{}),
+           {ok, PacketStripped, Addrs};
+       undefined ->
+           throw(eadsele)
     end.
 
 %%%-------------------------
 %%% Split Addresses
 %%%-------------------------
 
+-spec split_addresses_todeliver([address()]) -> {[address()], [address()]}.
 split_addresses_todeliver(Addresses) ->
-    lists:partition(fun (XML) ->
-                           case XML of
-                             #xmlel{name = <<"address">>, attrs = Attrs} ->
-                                 case fxml:get_attr_s(<<"delivered">>, Attrs) of
-                                   <<"true">> -> false;
-                                   _ ->
-                                       Type = fxml:get_attr_s(<<"type">>,
-                                                             Attrs),
-                                       case Type of
-                                         <<"to">> -> true;
-                                         <<"cc">> -> true;
-                                         <<"bcc">> -> true;
-                                         _ -> false
-                                       end
-                                 end;
-                             _ -> false
-                           end
-                   end,
-                   Addresses).
+    lists:partition(
+      fun(#address{delivered = true}) ->
+             false;
+        (#address{type = Type}) ->
+             case Type of
+                 to -> true;
+                 cc -> true;
+                 bcc -> true;
+                 _ -> false
+             end
+      end, Addresses).
 
 %%%-------------------------
 %%% Check does not exceed limit of destinations
 %%%-------------------------
 
+-spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok.
 check_limit_dests(SLimits, FromJID, Packet,
                  Addresses) ->
     SenderT = sender_type(FromJID),
@@ -497,24 +445,22 @@ check_limit_dests(SLimits, FromJID, Packet,
 %%% Convert Destination XML to record
 %%%-------------------------
 
-convert_dest_record(XMLs) ->
-    lists:map(fun (XML) ->
-                     case fxml:get_tag_attr_s(<<"jid">>, XML) of
-                       <<"">> -> #dest{jid_string = none, full_xml = XML};
-                       JIDS ->
-                           Type = fxml:get_tag_attr_s(<<"type">>, XML),
-                           JIDJ = stj(JIDS),
-                           #dest{jid_string = JIDS, jid_jid = JIDJ,
-                                 type = Type, full_xml = XML}
-                     end
-             end,
-             XMLs).
+-spec convert_dest_record([address()]) -> [#dest{}].
+convert_dest_record(Addrs) ->
+    lists:map(
+      fun(#address{jid = undefined} = Addr) ->
+             #dest{jid_string = none, full_xml = Addr};
+        (#address{jid = JID, type = Type} = Addr) ->
+             #dest{jid_string = jid:to_string(JID), jid_jid = JID,
+                   type = Type, full_xml = Addr}
+      end, Addrs).
 
 %%%-------------------------
 %%% Split destinations by existence of JID
 %%% and send error messages for other dests
 %%%-------------------------
 
+-spec split_dests_jid([#dest{}]) -> {[#dest{}], [#dest{}]}.
 split_dests_jid(Dests) ->
     lists:partition(fun (Dest) ->
                            case Dest#dest.jid_string of
@@ -524,8 +470,9 @@ split_dests_jid(Dests) ->
                    end,
                    Dests).
 
+-spec report_not_jid(jid(), stanza(), #dest{}) -> any().
 report_not_jid(From, Packet, Dests) ->
-    Dests2 = [fxml:element_to_binary(Dest#dest.full_xml)
+    Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml))
              || Dest <- Dests],
     [route_error(From, From, Packet, jid_malformed,
                 <<"This service can not process the address: ",
@@ -536,6 +483,7 @@ report_not_jid(From, Packet, Dests) ->
 %%% Group destinations by their servers
 %%%-------------------------
 
+-spec group_dests([#dest{}]) -> [#group{}].
 group_dests(Dests) ->
     D = lists:foldl(fun (Dest, Dict) ->
                            ServerS = (Dest#dest.jid_jid)#jid.server,
@@ -575,18 +523,17 @@ build_other_xml(Dests) ->
     lists:foldl(fun (Dest, R) ->
                        XML = Dest#dest.full_xml,
                        case Dest#dest.type of
-                         <<"to">> -> [add_delivered(XML) | R];
-                         <<"cc">> -> [add_delivered(XML) | R];
-                         <<"bcc">> -> R;
+                         to -> [add_delivered(XML) | R];
+                         cc -> [add_delivered(XML) | R];
+                         bcc -> R;
                          _ -> [XML | R]
                        end
                end,
                [], Dests).
 
-add_delivered(#xmlel{name = Name, attrs = Attrs,
-                    children = Els}) ->
-    Attrs2 = [{<<"delivered">>, <<"true">>} | Attrs],
-    #xmlel{name = Name, attrs = Attrs2, children = Els}.
+-spec add_delivered(address()) -> address().
+add_delivered(Addr) ->
+    Addr#address{delivered = true}.
 
 %%%-------------------------
 %%% Add preliminary packets
@@ -636,7 +583,7 @@ decide_action_group(Group) ->
 
 route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
     Dests = case ToDest#dest.type of
-             <<"bcc">> -> [];
+             bcc -> [];
              _ -> [ToDest]
            end,
     route_packet2(From, ToDest#dest.jid_string, Dests,
@@ -652,20 +599,20 @@ route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
                   Addresses)
      || DFragment <- Fragmented_dests].
 
-route_packet2(From, ToS, Dests, Packet, AAttrs,
+-spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok.
+route_packet2(From, ToS, Dests, Packet, _AAttrs,
              Addresses) ->
-    #xmlel{name = T, attrs = A, children = C} = Packet,
-    C2 = case append_dests(Dests, Addresses) of
-          [] -> C;
-          ACs ->
-              [#xmlel{name = <<"addresses">>, attrs = AAttrs,
-                      children = ACs}
-               | C]
-        end,
-    Packet2 = #xmlel{name = T, attrs = A, children = C2},
+    Els = case append_dests(Dests, Addresses) of
+             [] ->
+                 xmpp:get_els(Packet);
+             ACs ->
+                 [#addresses{list = ACs}|xmpp:get_els(Packet)]
+         end,
+    Packet2 = xmpp:set_els(Packet, Els),
     ToJID = stj(ToS),
     ejabberd_router:route(From, ToJID, Packet2).
 
+-spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()].
 append_dests(_Dests, {Others, Addresses}) ->
     Addresses++Others;
 append_dests([], Addresses) -> Addresses;
@@ -676,12 +623,14 @@ append_dests([Dest | Dests], Addresses) ->
 %%% Check relay
 %%%-------------------------
 
+-spec check_relay(binary(), binary(), [#group{}]) -> ok.
 check_relay(RS, LS, Gs) ->
     case check_relay_required(RS, LS, Gs) of
       false -> ok;
       true -> throw(edrelay)
     end.
 
+-spec check_relay_required(binary(), binary(), [#group{}]) -> boolean().
 check_relay_required(RServer, LServerS, Groups) ->
     case lists:suffix(str:tokens(LServerS, <<".">>),
                       str:tokens(RServer, <<".">>)) of
@@ -689,6 +638,7 @@ check_relay_required(RServer, LServerS, Groups) ->
       false -> check_relay_required(LServerS, Groups)
     end.
 
+-spec check_relay_required(binary(), [#group{}]) -> boolean().
 check_relay_required(LServerS, Groups) ->
     lists:any(fun (Group) -> Group#group.server /= LServerS
              end,
@@ -701,19 +651,16 @@ check_relay_required(LServerS, Groups) ->
 send_query_info(RServerS, LServiceS) ->
     case str:str(RServerS, <<"echo.">>) of
       1 -> false;
-      _ -> send_query(RServerS, LServiceS, ?NS_DISCO_INFO)
+      _ -> send_query(RServerS, LServiceS, #disco_info{})
     end.
 
 send_query_items(RServerS, LServiceS) ->
-    send_query(RServerS, LServiceS, ?NS_DISCO_ITEMS).
-
-send_query(RServerS, LServiceS, XMLNS) ->
-    Packet = #xmlel{name = <<"iq">>,
-                   attrs = [{<<"to">>, RServerS}, {<<"type">>, <<"get">>}],
-                   children =
-                       [#xmlel{name = <<"query">>,
-                               attrs = [{<<"xmlns">>, XMLNS}],
-                               children = []}]},
+    send_query(RServerS, LServiceS, #disco_items{}).
+
+-spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok.
+send_query(RServerS, LServiceS, SubEl) ->
+    Packet = #iq{id = randoms:get_string(),
+                type = get, sub_els = [SubEl]},
     ejabberd_router:route(stj(LServiceS), stj(RServerS),
                          Packet).
 
@@ -733,49 +680,40 @@ process_iqreply_error(From, LServiceS, _Packet) ->
 %%% Check protocol support: Receive response: Disco
 %%%-------------------------
 
-process_iqreply_result(From, LServiceS, Packet, State) ->
-    #xmlel{name = <<"query">>, attrs = Attrs2,
-          children = Els2} =
-       fxml:get_subtag(Packet, <<"query">>),
-    case fxml:get_attr_s(<<"xmlns">>, Attrs2) of
-      ?NS_DISCO_INFO ->
-         process_discoinfo_result(From, LServiceS, Els2, State);
-      ?NS_DISCO_ITEMS ->
-         process_discoitems_result(From, LServiceS, Els2)
+-spec process_iqreply_result(jid(), binary(), iq()) -> any().
+process_iqreply_result(From, LServiceS, #iq{sub_els = [SubEl]}) ->
+    case SubEl of
+       #disco_info{} ->
+           process_discoinfo_result(From, LServiceS, SubEl);
+       #disco_items{} ->
+           process_discoitems_result(From, LServiceS, SubEl);
+       _ ->
+           ok
     end.
 
 %%%-------------------------
 %%% Check protocol support: Receive response: Disco Info
 %%%-------------------------
 
-process_discoinfo_result(From, LServiceS, Els,
-                        _State) ->
+process_discoinfo_result(From, LServiceS, DiscoInfo) ->
     FromS = jts(From),
     case search_waiter(FromS, LServiceS, info) of
       {found_waiter, Waiter} ->
-         process_discoinfo_result2(From, FromS, LServiceS, Els,
+         process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo,
                                    Waiter);
       _ -> ok
     end.
 
-process_discoinfo_result2(From, FromS, LServiceS, Els,
+process_discoinfo_result2(From, FromS, LServiceS,
+                         #disco_info{features = Feats} = DiscoInfo,
                          Waiter) ->
-    Multicast_support =
-       lists:any(
-           fun(XML) ->
-                   case XML of
-                       #xmlel{name = <<"feature">>, attrs = Attrs} ->
-                           (?NS_ADDRESS) == fxml:get_attr_s(<<"var">>, Attrs);
-                       _ -> false
-                   end
-           end,
-           Els),
+    Multicast_support = lists:member(?NS_ADDRESS, Feats),
     Group = Waiter#waiter.group,
     RServer = Group#group.server,
     case Multicast_support of
        true ->
            SenderT = sender_type(From),
-           RLimits = get_limits_xml(Els, SenderT),
+           RLimits = get_limits_xml(DiscoInfo, SenderT),
            add_response(RServer, {multicast_supported, FromS, RLimits}),
            FromM = Waiter#waiter.sender,
            DestsM = Group#group.dests,
@@ -799,90 +737,58 @@ process_discoinfo_result2(From, FromS, LServiceS, Els,
          end
     end.
 
-get_limits_xml(Els, SenderT) ->
-    LimitOpts = get_limits_els(Els),
+get_limits_xml(DiscoInfo, SenderT) ->
+    LimitOpts = get_limits_els(DiscoInfo),
     build_remote_limit_record(LimitOpts, SenderT).
 
-get_limits_els(Els) ->
-    lists:foldl(fun (XML, R) ->
-                       case XML of
-                         #xmlel{name = <<"x">>, attrs = Attrs,
-                                children = SubEls} ->
-                             case ((?NS_XDATA) ==
-                                     fxml:get_attr_s(<<"xmlns">>, Attrs))
-                                    and
-                                    (<<"result">> ==
-                                       fxml:get_attr_s(<<"type">>, Attrs))
-                                 of
-                               true -> get_limits_fields(SubEls) ++ R;
-                               false -> R
-                             end;
-                         _ -> R
-                       end
-               end,
-               [], Els).
-
-get_limits_fields(Fields) ->
-    {Head, Tail} = lists:partition(fun (Field) ->
-                                          case Field of
-                                            #xmlel{name = <<"field">>,
-                                                   attrs = Attrs} ->
-                                                (<<"FORM_TYPE">> ==
-                                                   fxml:get_attr_s(<<"var">>,
-                                                                  Attrs))
-                                                  and
-                                                  (<<"hidden">> ==
-                                                     fxml:get_attr_s(<<"type">>,
-                                                                    Attrs));
-                                            _ -> false
-                                          end
-                                  end,
-                                  Fields),
+-spec get_limits_els(disco_info()) -> [{atom(), integer()}].
+get_limits_els(DiscoInfo) ->
+    lists:flatmap(
+      fun(#xdata{type = result} = X) ->
+             get_limits_fields(X);
+        (_) ->
+             []
+      end, DiscoInfo#disco_info.xdata).
+
+-spec get_limits_fields(xdata()) -> [{atom(), integer()}].
+get_limits_fields(X) ->
+    {Head, Tail} = lists:partition(
+                    fun(#xdata_field{var = Var, type = Type}) ->
+                            Var == <<"FORM_TYPE">> andalso Type == hidden
+                    end, X#xdata.fields),
     case Head of
       [] -> [];
       _ -> get_limits_values(Tail)
     end.
 
-get_limits_values(Values) ->
-    lists:foldl(fun (Value, R) ->
-                       case Value of
-                         #xmlel{name = <<"field">>, attrs = Attrs,
-                                children = SubEls} ->
-                             [#xmlel{name = <<"value">>, children = SubElsV}] =
-                                 SubEls,
-                             Number = fxml:get_cdata(SubElsV),
-                             Name = fxml:get_attr_s(<<"var">>, Attrs),
-                             [{jlib:binary_to_atom(Name),
-                               jlib:binary_to_integer(Number)}
-                              | R];
-                         _ -> R
-                       end
-               end,
-               [], Values).
+-spec get_limits_values([xdata_field()]) -> [{atom(), integer()}].
+get_limits_values(Fields) ->
+    lists:flatmap(
+      fun(#xdata_field{var = Name, values = [Number]}) ->
+             try
+                 [{binary_to_atom(Name, utf8), binary_to_integer(Number)}]
+             catch _:badarg ->
+                     []
+             end;
+        (_) ->
+             []
+      end, Fields).
 
 %%%-------------------------
 %%% Check protocol support: Receive response: Disco Items
 %%%-------------------------
 
-process_discoitems_result(From, LServiceS, Els) ->
+process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
     FromS = jts(From),
     case search_waiter(FromS, LServiceS, items) of
         {found_waiter, Waiter} ->
-            List = lists:foldl(
-                     fun(XML, Res) ->
-                             case XML of
-                                 #xmlel{name = <<"item">>, attrs = Attrs} ->
-                                     SJID = fxml:get_attr_s(<<"jid">>, Attrs),
-                                     case jid:from_string(SJID) of
-                                         #jid{luser = <<"">>,
-                                              lresource = <<"">>} ->
-                                             [SJID | Res];
-                                         _ -> Res
-                                     end;
-                                 _ -> Res
-                             end
-                     end,
-                     [], Els),
+            List = lists:flatmap(
+                    fun(#disco_item{jid = #jid{luser = <<"">>,
+                                               lresource = <<"">>} = J}) ->
+                            [J];
+                       (_) ->
+                            []
+                    end, Items),
             case List of
                 [] ->
                     received_awaiter(FromS, Waiter, LServiceS);
@@ -1109,9 +1015,7 @@ get_limit_value(Name, Default, LimitOpts) ->
       false -> {default, Default}
     end.
 
-type_of_stanza(#xmlel{name = <<"message">>}) -> message;
-type_of_stanza(#xmlel{name = <<"presence">>}) ->
-    presence.
+type_of_stanza(Stanza) -> element(1, Stanza).
 
 get_limit_number(message, Limits) ->
     Limits#limits.message;
@@ -1144,17 +1048,10 @@ fragment_dests(Dests, Limit_number) ->
 %% Some parts of code are borrowed from mod_muc_room.erl
 
 -define(RFIELDT(Type, Var, Val),
-       #xmlel{name = <<"field">>,
-              attrs = [{<<"var">>, Var}, {<<"type">>, Type}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, Val}]}]}).
+       #xdata_field{type = Type, var = Var, values = [Val]}).
 
 -define(RFIELDV(Var, Val),
-       #xmlel{name = <<"field">>, attrs = [{<<"var">>, Var}],
-              children =
-                  [#xmlel{name = <<"value">>, attrs = [],
-                          children = [{xmlcdata, Val}]}]}).
+       #xdata_field{var = Var, values = [Val]}).
 
 iq_disco_info_extras(From, State) ->
     SenderT = sender_type(From),
@@ -1162,12 +1059,9 @@ iq_disco_info_extras(From, State) ->
     case iq_disco_info_extras2(SenderT, Service_limits) of
       [] -> [];
       List_limits_xmpp ->
-         [#xmlel{name = <<"x">>,
-                 attrs =
-                     [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"result">>}],
-                 children =
-                     [?RFIELDT(<<"hidden">>, <<"FORM_TYPE">>, (?NS_ADDRESS))]
-                       ++ List_limits_xmpp}]
+           #xdata{type = result,
+                  fields = [?RFIELDT(hidden, <<"FORM_TYPE">>, ?NS_ADDRESS)
+                            | List_limits_xmpp]}
     end.
 
 sender_type(From) ->
@@ -1198,22 +1092,20 @@ to_binary(A) -> list_to_binary(hd(io_lib:format("~p", [A]))).
 %%%-------------------------
 
 route_error(From, To, Packet, ErrType, ErrText) ->
-    #xmlel{attrs = Attrs} = Packet,
-    Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
-    Reply = make_reply(ErrType, Lang, ErrText),
-    Err = jlib:make_error_reply(Packet, Reply),
-    ejabberd_router:route(From, To, Err).
+    Lang = xmpp:get_lang(Packet),
+    Err = make_reply(ErrType, Lang, ErrText),
+    ejabberd_router:route_error(From, To, Packet, Err).
 
 make_reply(bad_request, Lang, ErrText) ->
-    ?ERRT_BAD_REQUEST(Lang, ErrText);
+    xmpp:err_bad_request(ErrText, Lang);
 make_reply(jid_malformed, Lang, ErrText) ->
-    ?ERRT_JID_MALFORMED(Lang, ErrText);
+    xmpp:err_jid_malformed(ErrText, Lang);
 make_reply(not_acceptable, Lang, ErrText) ->
-    ?ERRT_NOT_ACCEPTABLE(Lang, ErrText);
+    xmpp:err_not_acceptable(ErrText, Lang);
 make_reply(internal_server_error, Lang, ErrText) ->
-    ?ERRT_INTERNAL_SERVER_ERROR(Lang, ErrText);
+    xmpp:err_internal_server_error(ErrText, Lang);
 make_reply(forbidden, Lang, ErrText) ->
-    ?ERRT_FORBIDDEN(Lang, ErrText).
+    xmpp:err_forbidden(ErrText, Lang).
 
 stj(String) -> jid:from_string(String).