]> granicus.if.org Git - ejabberd/commitdiff
Rewrite mod_irc to use XML generator
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Wed, 3 Aug 2016 17:57:05 +0000 (20:57 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Wed, 3 Aug 2016 17:57:05 +0000 (20:57 +0300)
src/mod_irc.erl
src/mod_irc_connection.erl
src/mod_irc_mnesia.erl
src/mod_irc_riak.erl
src/mod_irc_sql.erl
src/xmpp.erl

index 2206028b7209c45e728c9aa66d4b072ea7c2f9d9..91f43716feb84012deb760bcee74b1a66567302b 100644 (file)
@@ -34,7 +34,8 @@
 %% API
 -export([start_link/2, start/2, stop/1, export/1, import/1,
         import/3, closed_connection/3, get_connection_params/3,
-        data_to_binary/2]).
+        data_to_binary/2, process_disco_info/1, process_disco_items/1,
+        process_register/1, process_vcard/1, process_command/1]).
 
 -export([init/1, handle_call/3, handle_cast/2,
         handle_info/2, terminate/2, code_change/3,
 
 -include("ejabberd.hrl").
 -include("logger.hrl").
-
--include("jlib.hrl").
-
--include("adhoc.hrl").
-
+-include("xmpp.hrl").
 -include("mod_irc.hrl").
 
 -define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
@@ -125,6 +122,18 @@ init([Host, Opts]) ->
     catch ets:new(irc_connection,
                  [named_table, public,
                   {keypos, #irc_connection.jid_server_host}]),
+    IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
+                             one_queue),
+    gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
+                                 ?MODULE, process_disco_info, IQDisc),
+    gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS,
+                                 ?MODULE, process_disco_items, IQDisc),
+    gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER,
+                                 ?MODULE, process_register, IQDisc),
+    gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_VCARD,
+                                 ?MODULE, process_vcard, IQDisc),
+    gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_COMMANDS,
+                                 ?MODULE, process_command, IQDisc),
     ejabberd_router:register_route(MyHost, Host),
     {ok,
      #state{host = MyHost, server_host = Host,
@@ -176,8 +185,13 @@ handle_info(_Info, State) -> {noreply, State}.
 %% cleaning up. When it returns, the gen_server terminates with Reason.
 %% The return value is ignored.
 %%--------------------------------------------------------------------
-terminate(_Reason, State) ->
-    ejabberd_router:unregister_route(State#state.host), ok.
+terminate(_Reason, #state{host = MyHost}) ->
+    ejabberd_router:unregister_route(MyHost),
+    gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO),
+    gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_ITEMS),
+    gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_REGISTER),
+    gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_VCARD),
+    gen_iq_handler:remove_iq_handler(ejabberd_local, MyHost, ?NS_COMMANDS).
 
 %%--------------------------------------------------------------------
 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -203,287 +217,222 @@ stop_supervisor(Host) ->
     supervisor:terminate_child(ejabberd_sup, Proc),
     supervisor:delete_child(ejabberd_sup, Proc).
 
-do_route(Host, ServerHost, Access, From, To, Packet) ->
+do_route(Host, ServerHost, Access, From,
+        #jid{luser = LUser, lresource = LResource} = To, Packet) ->
     case acl:match_rule(ServerHost, Access, From) of
-      allow -> do_route1(Host, ServerHost, From, To, Packet);
-      _ ->
-         #xmlel{attrs = Attrs} = Packet,
-         Lang = fxml:get_attr_s(<<"xml:lang">>, Attrs),
-         ErrText = <<"Access denied by service policy">>,
-         Err = jlib:make_error_reply(Packet,
-                                     ?ERRT_FORBIDDEN(Lang, ErrText)),
-         ejabberd_router:route(To, From, Err)
+       allow ->
+           case Packet of
+               #iq{} when LUser == <<"">>, LResource == <<"">> ->
+                   ejabberd_router:process_iq(From, To, Packet);
+               #iq{} when LUser == <<"">>, LResource /= <<"">> ->
+                   Err = xmpp:err_service_unavailable(),
+                   ejabberd_router:route_error(To, From, Packet, Err);
+               _ ->
+                   sm_route(Host, ServerHost, From, To, Packet)
+           end;
+       deny ->
+           Lang = xmpp:get_lang(Packet),
+           Err = xmpp:err_forbidden(<<"Denied by ACL">>, Lang),
+           ejabberd_router:route_error(To, From, Packet, Err)
+    end.
+
+process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
+    Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+    xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_disco_info(#iq{type = get, lang = Lang, to = To,
+                      sub_els = [#disco_info{node = Node}]} = IQ) ->
+    ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
+    Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
+                                  [], [ServerHost, ?MODULE, <<"">>, <<"">>]),
+    case iq_disco(ServerHost, Node, Lang) of
+       undefined ->
+           xmpp:make_iq_result(IQ, #disco_info{});
+       DiscoInfo ->
+           xmpp:make_iq_result(IQ, DiscoInfo#disco_info{xdata = Info})
+    end.
+
+process_disco_items(#iq{type = set, lang = Lang} = IQ) ->
+    Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+    xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_disco_items(#iq{type = get, lang = Lang, to = To,
+                       sub_els = [#disco_items{node = Node}]} = IQ) ->
+    case Node of
+       undefined ->
+           xmpp:make_iq_result(IQ, #disco_items{});
+       <<"join">> ->
+           xmpp:make_iq_result(IQ, #disco_items{});
+       <<"register">> ->
+           xmpp:make_iq_result(IQ, #disco_items{});
+       ?NS_COMMANDS ->
+           Host = To#jid.lserver,
+           ServerHost = ejabberd_router:host_of_route(Host),
+           xmpp:make_iq_result(
+             IQ, #disco_items{node = Node,
+                              items = command_items(ServerHost, Host, Lang)});
+       _ ->
+           Txt = <<"Node not found">>,
+           xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang))
+    end.
+
+process_register(#iq{type = get, to = To, from = From, lang = Lang} = IQ) ->
+    Host = To#jid.lserver,
+    ServerHost = ejabberd_router:host_of_route(Host),
+    case get_form(ServerHost, Host, From, Lang) of
+       {result, Res} ->
+           xmpp:make_iq_result(IQ, Res);
+       {error, Error} ->
+           xmpp:make_error(IQ, Error)
+    end;
+process_register(#iq{type = set, lang = Lang, to = To, from = From,
+                    sub_els = [#register{xdata = #xdata{} = X}]} = IQ) ->
+    case X#xdata.type of
+       cancel ->
+           xmpp:make_iq_result(IQ, #register{});
+       submit ->
+           Host = To#jid.lserver,
+           ServerHost = ejabberd_router:host_of_route(Host),
+           case set_form(ServerHost, Host, From, Lang, X) of
+               {result, Res} ->
+                   xmpp:make_iq_result(IQ, Res);
+               {error, Error} ->
+                   xmpp:make_error(IQ, Error)
+           end;
+       _ ->
+           Txt = <<"Incorrect value of 'type' attribute">>,
+           xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang))
+    end;
+process_register(#iq{type = set, lang = Lang} = IQ) ->
+    Txt = <<"No data form found">>,
+    xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)).
+
+process_vcard(#iq{type = set, lang = Lang} = IQ) ->
+    Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
+    xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_vcard(#iq{type = get, lang = Lang} = IQ) ->
+    xmpp:make_iq_result(IQ, iq_get_vcard(Lang)).
+
+process_command(#iq{type = get, lang = Lang} = IQ) ->
+    Txt = <<"Value 'get' of 'type' attribute is not allowed">>,
+    xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
+process_command(#iq{type = set, lang = Lang, to = To, from = From,
+                   sub_els = [#adhoc_command{node = Node} = Request]} = IQ) ->
+    Host = To#jid.lserver,
+    ServerHost = ejabberd_router:host_of_route(Host),
+    case lists:keyfind(Node, 1, commands(ServerHost)) of
+       {_, _, Function} ->
+           try Function(From, To, Request) of
+               ignore ->
+                   ignore;
+               {error, Error} ->
+                   xmpp:make_error(IQ, Error);
+               Command ->
+                   xmpp:make_iq_result(IQ, Command)
+           catch E:R ->
+                   ?ERROR_MSG("ad-hoc handler failed: ~p",
+                              [{E, {R, erlang:get_stacktrace()}}]),
+                   xmpp:make_error(IQ, xmpp:internal_server_error())
+           end;
+       _ ->
+           Txt = <<"Node not found">>,
+           xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang))
     end.
 
-do_route1(Host, ServerHost, From, To, Packet) ->
+sm_route(Host, ServerHost, From, To, Packet) ->
     #jid{user = ChanServ, resource = Resource} = To,
-    #xmlel{} = Packet,
-    case ChanServ of
-      <<"">> ->
-         case Resource of
-           <<"">> ->
-               case jlib:iq_query_info(Packet) of
-                 #iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS,
-                     sub_el = SubEl, lang = Lang} =
-                     IQ ->
-                     Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
-                     Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
-                                                    [],
-                                                    [ServerHost, ?MODULE,
-                                                     <<"">>, <<"">>]),
-                     case iq_disco(ServerHost, Node, Lang) of
-                       [] ->
-                           Res = IQ#iq{type = result,
-                                       sub_el =
-                                           [#xmlel{name = <<"query">>,
-                                                   attrs =
-                                                       [{<<"xmlns">>, XMLNS}],
-                                                   children = []}]},
-                           ejabberd_router:route(To, From,
-                                                 jlib:iq_to_xml(Res));
-                       DiscoInfo ->
-                           Res = IQ#iq{type = result,
-                                       sub_el =
-                                           [#xmlel{name = <<"query">>,
-                                                   attrs =
-                                                       [{<<"xmlns">>, XMLNS}],
-                                                   children =
-                                                       DiscoInfo ++ Info}]},
-                           ejabberd_router:route(To, From, jlib:iq_to_xml(Res))
-                     end;
-                 #iq{type = get, xmlns = (?NS_DISCO_ITEMS) = XMLNS,
-                     sub_el = SubEl, lang = Lang} =
-                     IQ ->
-                     Node = fxml:get_tag_attr_s(<<"node">>, SubEl),
-                     case Node of
-                       <<>> ->
-                           ResIQ = IQ#iq{type = result,
-                                         sub_el =
-                                             [#xmlel{name = <<"query">>,
-                                                     attrs =
-                                                         [{<<"xmlns">>,
-                                                           XMLNS}],
-                                                     children = []}]},
-                           Res = jlib:iq_to_xml(ResIQ);
-                       <<"join">> ->
-                           ResIQ = IQ#iq{type = result,
-                                         sub_el =
-                                             [#xmlel{name = <<"query">>,
-                                                     attrs =
-                                                         [{<<"xmlns">>,
-                                                           XMLNS}],
-                                                     children = []}]},
-                           Res = jlib:iq_to_xml(ResIQ);
-                       <<"register">> ->
-                           ResIQ = IQ#iq{type = result,
-                                         sub_el =
-                                             [#xmlel{name = <<"query">>,
-                                                     attrs =
-                                                         [{<<"xmlns">>,
-                                                           XMLNS}],
-                                                     children = []}]},
-                           Res = jlib:iq_to_xml(ResIQ);
-                       ?NS_COMMANDS ->
-                           ResIQ = IQ#iq{type = result,
-                                         sub_el =
-                                             [#xmlel{name = <<"query">>,
-                                                     attrs =
-                                                         [{<<"xmlns">>, XMLNS},
-                                                          {<<"node">>, Node}],
-                                                     children =
-                                                         command_items(ServerHost,
-                                                                       Host,
-                                                                       Lang)}]},
-                           Res = jlib:iq_to_xml(ResIQ);
-                       _ ->
-                           Txt = <<"Node not found">>,
-                           Res = jlib:make_error_reply(
-                                   Packet, ?ERRT_ITEM_NOT_FOUND(Lang, Txt))
-                     end,
-                     ejabberd_router:route(To, From, Res);
-                 #iq{xmlns = ?NS_REGISTER} = IQ ->
-                     process_register(ServerHost, Host, From, To, IQ);
-                 #iq{type = get, xmlns = (?NS_VCARD) = XMLNS,
-                     lang = Lang} =
-                     IQ ->
-                     Res = IQ#iq{type = result,
-                                 sub_el =
-                                     [#xmlel{name = <<"vCard">>,
-                                             attrs = [{<<"xmlns">>, XMLNS}],
-                                             children = iq_get_vcard(Lang)}]},
-                     ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
-                 #iq{type = set, xmlns = ?NS_COMMANDS, lang = Lang,
-                     sub_el = SubEl} =
-                     IQ ->
-                     Request = adhoc:parse_request(IQ),
-                     case lists:keysearch(Request#adhoc_request.node, 1,
-                                          commands(ServerHost))
-                         of
-                       {value, {_, _, Function}} ->
-                           case catch Function(From, To, Request) of
-                             {'EXIT', Reason} ->
-                                 ?ERROR_MSG("~p~nfor ad-hoc handler of ~p",
-                                            [Reason, {From, To, IQ}]),
-                                 Res = IQ#iq{type = error,
-                                             sub_el =
-                                                 [SubEl,
-                                                  ?ERR_INTERNAL_SERVER_ERROR]};
-                             ignore -> Res = ignore;
-                             {error, Error} ->
-                                 Res = IQ#iq{type = error,
-                                             sub_el = [SubEl, Error]};
-                             Command ->
-                                 Res = IQ#iq{type = result, sub_el = [Command]}
-                           end,
-                           if Res /= ignore ->
-                                  ejabberd_router:route(To, From,
-                                                        jlib:iq_to_xml(Res));
-                              true -> ok
-                           end;
-                       _ ->
-                           Txt = <<"Node not found">>,
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)),
-                           ejabberd_router:route(To, From, Err)
-                     end;
-                 #iq{} = _IQ ->
-                     Err = jlib:make_error_reply(Packet,
-                                                 ?ERR_FEATURE_NOT_IMPLEMENTED),
-                     ejabberd_router:route(To, From, Err);
-                 _ -> ok
-               end;
-           _ ->
-               Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST),
-               ejabberd_router:route(To, From, Err)
-         end;
-      _ ->
-         case str:tokens(ChanServ, <<"%">>) of
-           [<<_, _/binary>> = Channel, <<_, _/binary>> = Server] ->
-               case ets:lookup(irc_connection, {From, Server, Host}) of
-                 [] ->
-                     ?DEBUG("open new connection~n", []),
-                     {Username, Encoding, Port, Password} =
-                         get_connection_params(Host, ServerHost, From, Server),
-                     ConnectionUsername = case Packet of
+    case str:tokens(ChanServ, <<"%">>) of
+       [<<_, _/binary>> = Channel, <<_, _/binary>> = Server] ->
+           case ets:lookup(irc_connection, {From, Server, Host}) of
+               [] ->
+                   ?DEBUG("open new connection~n", []),
+                   {Username, Encoding, Port, Password} =
+                       get_connection_params(Host, ServerHost, From, Server),
+                   ConnectionUsername = case Packet of
                                             %% If the user tries to join a
                                             %% chatroom, the packet for sure
                                             %% contains the desired username.
-                                            #xmlel{name = <<"presence">>} ->
-                                                Resource;
+                                            #presence{} -> Resource;
                                             %% Otherwise, there is no firm
                                             %% conclusion from the packet.
                                             %% Better to use the configured
                                             %% username (which defaults to the
                                             %% username part of the JID).
                                             _ -> Username
-                                          end,
-                     Ident = extract_ident(Packet),
-                     RemoteAddr = extract_ip_address(Packet),
-                     RealName = get_realname(ServerHost),
-                     WebircPassword = get_webirc_password(ServerHost),
-                     {ok, Pid} = mod_irc_connection:start(From, Host,
-                                                          ServerHost, Server,
-                                                          ConnectionUsername,
-                                                          Encoding, Port,
-                                                          Password, Ident, RemoteAddr, RealName, WebircPassword, ?MODULE),
-                     ets:insert(irc_connection,
-                                #irc_connection{jid_server_host =
-                                                    {From, Server, Host},
-                                                pid = Pid}),
-                     mod_irc_connection:route_chan(Pid, Channel, Resource,
-                                                   Packet),
-                     ok;
-                 [R] ->
-                     Pid = R#irc_connection.pid,
-                     ?DEBUG("send to process ~p~n", [Pid]),
-                     mod_irc_connection:route_chan(Pid, Channel, Resource,
-                                                   Packet),
-                     ok
-               end;
-           _ ->
-               Lang = fxml:get_tag_attr_s(<<"xml:lang">>, Packet),
-               case str:tokens(ChanServ, <<"!">>) of
-                 [<<_, _/binary>> = Nick, <<_, _/binary>> = Server] ->
-                     case ets:lookup(irc_connection, {From, Server, Host}) of
+                                        end,
+                   Ident = extract_ident(Packet),
+                   RemoteAddr = extract_ip_address(Packet),
+                   RealName = get_realname(ServerHost),
+                   WebircPassword = get_webirc_password(ServerHost),
+                   {ok, Pid} = mod_irc_connection:start(
+                                 From, Host, ServerHost, Server,
+                                 ConnectionUsername, Encoding, Port,
+                                 Password, Ident, RemoteAddr, RealName,
+                                 WebircPassword, ?MODULE),
+                   ets:insert(irc_connection,
+                              #irc_connection{
+                                 jid_server_host = {From, Server, Host},
+                                 pid = Pid}),
+                   mod_irc_connection:route_chan(Pid, Channel, Resource, Packet);
+               [R] ->
+                   Pid = R#irc_connection.pid,
+                   ?DEBUG("send to process ~p~n", [Pid]),
+                   mod_irc_connection:route_chan(Pid, Channel, Resource, Packet)
+           end;
+       _ ->
+           Lang = xmpp:get_lang(Packet),
+           case str:tokens(ChanServ, <<"!">>) of
+               [<<_, _/binary>> = Nick, <<_, _/binary>> = Server] ->
+                   case ets:lookup(irc_connection, {From, Server, Host}) of
                        [] ->
                            Txt = <<"IRC connection not found">>,
-                           Err = jlib:make_error_reply(
-                                   Packet, ?ERRT_SERVICE_UNAVAILABLE(Lang, Txt)),
-                           ejabberd_router:route(To, From, Err);
+                           Err = xmpp:err_service_unavailable(Txt, Lang),
+                           ejabberd_router:route_error(To, From, Packet, Err);
                        [R] ->
                            Pid = R#irc_connection.pid,
                            ?DEBUG("send to process ~p~n", [Pid]),
-                           mod_irc_connection:route_nick(Pid, Nick, Packet),
-                           ok
-                     end;
-                 _ ->
-                     Txt = <<"Failed to parse chanserv">>,
-                     Err = jlib:make_error_reply(
-                             Packet, ?ERRT_BAD_REQUEST(Lang, Txt)),
-                     ejabberd_router:route(To, From, Err)
-               end
-         end
+                           mod_irc_connection:route_nick(Pid, Nick, Packet)
+                   end;
+               _ ->
+                   Txt = <<"Failed to parse chanserv">>,
+                   Err = xmpp:err_bad_request(Txt, Lang),
+                   ejabberd_router:route_error(To, From, Packet, Err)
+           end
     end.
 
 closed_connection(Host, From, Server) ->
     ets:delete(irc_connection, {From, Server, Host}).
 
-iq_disco(_ServerHost, <<>>, Lang) ->
-    [#xmlel{name = <<"identity">>,
-           attrs =
-               [{<<"category">>, <<"conference">>},
-                {<<"type">>, <<"irc">>},
-                {<<"name">>,
-                 translate:translate(Lang, <<"IRC Transport">>)}],
-           children = []},
-     #xmlel{name = <<"feature">>,
-           attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []},
-     #xmlel{name = <<"feature">>,
-           attrs = [{<<"var">>, ?NS_MUC}], children = []},
-     #xmlel{name = <<"feature">>,
-           attrs = [{<<"var">>, ?NS_REGISTER}], children = []},
-     #xmlel{name = <<"feature">>,
-           attrs = [{<<"var">>, ?NS_VCARD}], children = []},
-     #xmlel{name = <<"feature">>,
-           attrs = [{<<"var">>, ?NS_COMMANDS}], children = []}];
+iq_disco(_ServerHost, undefined, Lang) ->
+    #disco_info{
+       identities = [#identity{category = <<"conference">>,
+                              type = <<"irc">>,
+                              name = translate:translate(Lang, <<"IRC Transport">>)}],
+       features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC,
+                  ?NS_REGISTER, ?NS_VCARD, ?NS_COMMANDS]};
 iq_disco(ServerHost, Node, Lang) ->
-    case lists:keysearch(Node, 1, commands(ServerHost)) of
-      {value, {_, Name, _}} ->
-         [#xmlel{name = <<"identity">>,
-                 attrs =
-                     [{<<"category">>, <<"automation">>},
-                      {<<"type">>, <<"command-node">>},
-                      {<<"name">>, translate:translate(Lang, Name)}],
-                 children = []},
-          #xmlel{name = <<"feature">>,
-                 attrs = [{<<"var">>, ?NS_COMMANDS}], children = []},
-          #xmlel{name = <<"feature">>,
-                 attrs = [{<<"var">>, ?NS_XDATA}], children = []}];
-      _ -> []
+    case lists:keyfind(Node, commands(ServerHost)) of
+       {_, Name, _} ->
+           #disco_info{
+              identities = [#identity{category = <<"automation">>,
+                                      type = <<"command-node">>,
+                                      name = translate:translate(Lang, Name)}],
+              features = [?NS_COMMANDS, ?NS_XDATA]};
+       _ ->
+           undefined
     end.
 
 iq_get_vcard(Lang) ->
-    [#xmlel{name = <<"FN">>, attrs = [],
-           children = [{xmlcdata, <<"ejabberd/mod_irc">>}]},
-     #xmlel{name = <<"URL">>, attrs = [],
-           children = [{xmlcdata, ?EJABBERD_URI}]},
-     #xmlel{name = <<"DESC">>, attrs = [],
-           children =
-               [{xmlcdata,
-                 <<(translate:translate(Lang,
-                                        <<"ejabberd IRC module">>))/binary,
-                   "\nCopyright (c) 2003-2016 ProcessOne">>}]}].
+    Desc = translate:translate(Lang, <<"ejabberd IRC module">>),
+    Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
+    #vcard_temp{fn = <<"ejabberd/mod_irc">>,
+               url = ?EJABBERD_URI,
+               desc = <<Desc/binary, $\n, Copyright/binary>>}.
 
 command_items(ServerHost, Host, Lang) ->
-    lists:map(fun ({Node, Name, _Function}) ->
-                     #xmlel{name = <<"item">>,
-                            attrs =
-                                [{<<"jid">>, Host}, {<<"node">>, Node},
-                                 {<<"name">>,
-                                  translate:translate(Lang, Name)}],
-                            children = []}
-             end,
-             commands(ServerHost)).
+    lists:map(fun({Node, Name, _Function}) ->
+                     #disco_item{jid = jid:make(Host),
+                                 node = Node,
+                                 name = translate:translate(Lang, Name)}
+             end, commands(ServerHost)).
 
 commands(ServerHost) ->
     [{<<"join">>, <<"Join channel">>, fun adhoc_join/3},
@@ -494,243 +443,120 @@ commands(ServerHost) ->
              adhoc_register(ServerHost, From, To, Request)
       end}].
 
-process_register(ServerHost, Host, From, To,
-                #iq{} = IQ) ->
-    case catch process_irc_register(ServerHost, Host, From,
-                                   To, IQ)
-       of
-      {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
-      ResIQ ->
-         if ResIQ /= ignore ->
-                ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ));
-            true -> ok
-         end
-    end.
-
-find_xdata_el(#xmlel{children = SubEls}) ->
-    find_xdata_el1(SubEls).
-
-find_xdata_el1([]) -> false;
-find_xdata_el1([#xmlel{name = Name, attrs = Attrs,
-                      children = SubEls}
-               | Els]) ->
-    case fxml:get_attr_s(<<"xmlns">>, Attrs) of
-      ?NS_XDATA ->
-         #xmlel{name = Name, attrs = Attrs, children = SubEls};
-      _ -> find_xdata_el1(Els)
-    end;
-find_xdata_el1([_ | Els]) -> find_xdata_el1(Els).
-
-process_irc_register(ServerHost, Host, From, _To,
-                    #iq{type = Type, xmlns = XMLNS, lang = Lang,
-                        sub_el = SubEl} =
-                        IQ) ->
-    case Type of
-      set ->
-         XDataEl = find_xdata_el(SubEl),
-         case XDataEl of
-           false ->
-               Txt1 = <<"No data form found">>,
-               IQ#iq{type = error,
-                     sub_el = [SubEl, ?ERRT_NOT_ACCEPTABLE(Lang, Txt1)]};
-           #xmlel{attrs = Attrs} ->
-               case fxml:get_attr_s(<<"type">>, Attrs) of
-                 <<"cancel">> ->
-                     IQ#iq{type = result,
-                           sub_el =
-                               [#xmlel{name = <<"query">>,
-                                       attrs = [{<<"xmlns">>, XMLNS}],
-                                       children = []}]};
-                 <<"submit">> ->
-                     XData = jlib:parse_xdata_submit(XDataEl),
-                     case XData of
-                       invalid ->
-                           Txt2 = <<"Incorrect data form">>,
-                           IQ#iq{type = error,
-                                 sub_el = [SubEl, ?ERRT_BAD_REQUEST(Lang, Txt2)]};
-                       _ ->
-                           Node = str:tokens(fxml:get_tag_attr_s(<<"node">>,
-                                                                SubEl),
-                                             <<"/">>),
-                           case set_form(ServerHost, Host, From, Node, Lang,
-                                         XData)
-                               of
-                             {result, Res} ->
-                                 IQ#iq{type = result,
-                                       sub_el =
-                                           [#xmlel{name = <<"query">>,
-                                                   attrs =
-                                                       [{<<"xmlns">>, XMLNS}],
-                                                   children = Res}]};
-                             {error, Error} ->
-                                 IQ#iq{type = error, sub_el = [SubEl, Error]}
-                           end
-                     end;
-                 _ ->
-                     Txt3 = <<"Incorrect value of 'type' attribute">>,
-                     IQ#iq{type = error,
-                           sub_el = [SubEl, ?ERRT_BAD_REQUEST(Lang, Txt3)]}
-               end
-         end;
-      get ->
-         Node = str:tokens(fxml:get_tag_attr_s(<<"node">>, SubEl),
-                           <<"/">>),
-         case get_form(ServerHost, Host, From, Node, Lang) of
-           {result, Res} ->
-               IQ#iq{type = result,
-                     sub_el =
-                         [#xmlel{name = <<"query">>,
-                                 attrs = [{<<"xmlns">>, XMLNS}],
-                                 children = Res}]};
-           {error, Error} ->
-               IQ#iq{type = error, sub_el = [SubEl, Error]}
-         end
-    end.
-
 get_data(ServerHost, Host, From) ->
     LServer = jid:nameprep(ServerHost),
     Mod = gen_mod:db_mod(LServer, ?MODULE),
     Mod:get_data(LServer, Host, From).
 
-get_form(ServerHost, Host, From, [], Lang) ->
+get_form(ServerHost, Host, From, Lang) ->
     #jid{user = User, server = Server} = From,
     DefaultEncoding = get_default_encoding(Host),
     Customs = case get_data(ServerHost, Host, From) of
-               error ->
+                 error ->
                      Txt1 = <<"Database failure">>,
-                     {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt1)};
-               empty -> {User, []};
-               Data -> get_username_and_connection_params(Data)
+                     {error, xmpp:err_internal_server_error(Txt1, Lang)};
+                 empty -> {User, []};
+                 Data -> get_username_and_connection_params(Data)
              end,
     case Customs of
-      {error, _Error} -> Customs;
-      {Username, ConnectionsParams} ->
-         {result,
-          [#xmlel{name = <<"instructions">>, attrs = [],
-                  children =
-                      [{xmlcdata,
-                        translate:translate(Lang,
-                                            <<"You need an x:data capable client to "
-                                              "configure mod_irc settings">>)}]},
-           #xmlel{name = <<"x">>,
-                  attrs = [{<<"xmlns">>, ?NS_XDATA}],
-                  children =
-                      [#xmlel{name = <<"title">>, attrs = [],
-                              children =
-                                  [{xmlcdata,
-                                    <<(translate:translate(Lang,
-                                                           <<"Registration in mod_irc for ">>))/binary,
-                                      User/binary, "@", Server/binary>>}]},
-                       #xmlel{name = <<"instructions">>, attrs = [],
-                              children =
-                                  [{xmlcdata,
-                                    translate:translate(Lang,
-                                                        <<"Enter username, encodings, ports and "
-                                                          "passwords you wish to use for connecting "
-                                                          "to IRC servers">>)}]},
-                       #xmlel{name = <<"field">>,
-                              attrs =
-                                  [{<<"type">>, <<"text-single">>},
-                                   {<<"label">>,
-                                    translate:translate(Lang,
-                                                        <<"IRC Username">>)},
-                                   {<<"var">>, <<"username">>}],
-                              children =
-                                  [#xmlel{name = <<"value">>, attrs = [],
-                                          children = [{xmlcdata, Username}]}]},
-                       #xmlel{name = <<"field">>,
-                              attrs = [{<<"type">>, <<"fixed">>}],
-                              children =
-                                  [#xmlel{name = <<"value">>, attrs = [],
-                                          children =
-                                              [{xmlcdata,
-                                                iolist_to_binary(
-                                                   io_lib:format(
-                                                     translate:translate(
-                                                       Lang,
-                                                       <<"If you want to specify"
-                                                         " different ports, "
-                                                         "passwords, encodings "
-                                                         "for IRC servers, "
-                                                         "fill this list with "
-                                                         "values in format "
-                                                         "'{\"irc server\", "
-                                                         "\"encoding\", port, "
-                                                         "\"password\"}'.  "
-                                                         "By default this "
-                                                         "service use \"~s\" "
-                                                         "encoding, port ~p, "
-                                                         "empty password.">>),
-                                                     [DefaultEncoding,
-                                                      ?DEFAULT_IRC_PORT]))}]}]},
-                       #xmlel{name = <<"field">>,
-                              attrs = [{<<"type">>, <<"fixed">>}],
-                              children =
-                                  [#xmlel{name = <<"value">>, attrs = [],
-                                          children =
-                                              [{xmlcdata,
-                                                translate:translate(Lang,
-                                                                    <<"Example: [{\"irc.lucky.net\", \"koi8-r\", "
-                                                                      "6667, \"secret\"}, {\"vendetta.fef.net\", "
-                                                                      "\"iso8859-1\", 7000}, {\"irc.sometestserver.n"
-                                                                      "et\", \"utf-8\"}].">>)}]}]},
-                       #xmlel{name = <<"field">>,
-                              attrs =
-                                  [{<<"type">>, <<"text-multi">>},
-                                   {<<"label">>,
-                                    translate:translate(Lang,
-                                                        <<"Connections parameters">>)},
-                                   {<<"var">>, <<"connections_params">>}],
-                              children =
-                                  lists:map(fun (S) ->
-                                                    #xmlel{name = <<"value">>,
-                                                           attrs = [],
-                                                           children =
-                                                               [{xmlcdata, S}]}
-                                            end,
-                                            str:tokens(list_to_binary(
-                                                          io_lib:format(
-                                                            "~p.",
-                                                            [conn_params_to_list(
-                                                               ConnectionsParams)])),
-                                                       <<"\n">>))}]}]}
-    end;
-get_form(_ServerHost, _Host, _, _, _Lang) ->
-    {error, ?ERR_SERVICE_UNAVAILABLE}.
+       {error, _Error} ->
+           Customs;
+       {Username, ConnectionsParams} ->
+           Fs = [#xdata_field{type = 'text-single',
+                              label =  translate:translate(Lang, <<"IRC Username">>),
+                              var = <<"username">>,
+                              values = [Username]},
+                 #xdata_field{type = fixed,
+                              values = [iolist_to_binary(
+                                          io_lib:format(
+                                            translate:translate(
+                                              Lang,
+                                              <<"If you want to specify"
+                                                " different ports, "
+                                                "passwords, encodings "
+                                                "for IRC servers, "
+                                                "fill this list with "
+                                                "values in format "
+                                                "'{\"irc server\", "
+                                                "\"encoding\", port, "
+                                                "\"password\"}'.  "
+                                                "By default this "
+                                                "service use \"~s\" "
+                                                "encoding, port ~p, "
+                                                "empty password.">>),
+                                            [DefaultEncoding, ?DEFAULT_IRC_PORT]))]},
+                 #xdata_field{type = fixed,
+                              values = [translate:translate(
+                                          Lang,
+                                          <<"Example: [{\"irc.lucky.net\", \"koi8-r\", "
+                                            "6667, \"secret\"}, {\"vendetta.fef.net\", "
+                                            "\"iso8859-1\", 7000}, {\"irc.sometestserver.n"
+                                            "et\", \"utf-8\"}].">>)]},
+                 #xdata_field{type = 'text-multi',
+                              label = translate:translate(
+                                        Lang, <<"Connections parameters">>),
+                              var = <<"connections_params">>,
+                              values = str:tokens(list_to_binary(
+                                                    io_lib:format(
+                                                      "~p.",
+                                                      [conn_params_to_list(
+                                                         ConnectionsParams)])),
+                                                  <<"\n">>)}],
+           X = #xdata{type = form,
+                      title = <<(translate:translate(
+                                   Lang, <<"Registration in mod_irc for ">>))/binary,
+                                User/binary, "@", Server/binary>>,
+                      instructions =
+                          [translate:translate(
+                             Lang,
+                             <<"Enter username, encodings, ports and "
+                               "passwords you wish to use for connecting "
+                               "to IRC servers">>)],
+                      fields = Fs},
+           {result,
+            #register{instructions = 
+                          translate:translate(Lang,
+                                              <<"You need an x:data capable client to "
+                                                "configure mod_irc settings">>),
+                      xdata = X}}
+    end.
 
 set_data(ServerHost, Host, From, Data) ->
     LServer = jid:nameprep(ServerHost),
     Mod = gen_mod:db_mod(LServer, ?MODULE),
     Mod:set_data(LServer, Host, From, data_to_binary(From, Data)).
 
-set_form(ServerHost, Host, From, [], Lang, XData) ->
-    case {lists:keysearch(<<"username">>, 1, XData),
-         lists:keysearch(<<"connections_params">>, 1, XData)}
-       of
-      {{value, {_, [Username]}}, {value, {_, Strings}}} ->
-         EncString = lists:foldl(fun (S, Res) ->
-                                         <<Res/binary, S/binary, "\n">>
-                                 end,
-                                 <<"">>, Strings),
-         case erl_scan:string(binary_to_list(EncString)) of
-           {ok, Tokens, _} ->
-               case erl_parse:parse_term(Tokens) of
-                 {ok, ConnectionsParams} ->
-                     case set_data(ServerHost, Host, From,
-                                   [{username, Username},
-                                    {connections_params, ConnectionsParams}])
-                         of
-                       {atomic, _} -> {result, []};
-                       _ -> {error, ?ERRT_NOT_ACCEPTABLE(Lang, <<"Database failure">>)}
-                     end;
-                 _ -> {error, ?ERRT_NOT_ACCEPTABLE(Lang, <<"Parse error">>)}
-               end;
-           _ -> {error, ?ERRT_NOT_ACCEPTABLE(Lang, <<"Scan error">>)}
-         end;
-      _ -> {error, ?ERR_NOT_ACCEPTABLE}
-    end;
-set_form(_ServerHost, _Host, _, _, _Lang, _XData) ->
-    {error, ?ERR_SERVICE_UNAVAILABLE}.
+set_form(ServerHost, Host, From, Lang, XData) ->
+    case {xmpp_util:get_xdata_values(<<"username">>, XData),
+         xmpp_util:get_xdata_values(<<"connections_params">>, XData)} of
+       {[Username], [_|_] = Strings} ->
+           EncString = lists:foldl(fun (S, Res) ->
+                                           <<Res/binary, S/binary, "\n">>
+                                   end, <<"">>, Strings),
+           case erl_scan:string(binary_to_list(EncString)) of
+               {ok, Tokens, _} ->
+                   case erl_parse:parse_term(Tokens) of
+                       {ok, ConnectionsParams} ->
+                           case set_data(ServerHost, Host, From,
+                                         [{username, Username},
+                                          {connections_params, ConnectionsParams}]) of
+                               {atomic, _} ->
+                                   {result, undefined};
+                               _ ->
+                                   Txt = <<"Database failure">>,
+                                   {error, xmpp:err_internal_server_error(Txt, Lang)}
+                           end;
+                       _ ->
+                           Txt = <<"Parse error">>,
+                           {error, xmpp:err_not_acceptable(Txt, Lang)}
+                   end;
+               _ ->
+                   {error, xmpp:err_not_acceptable(<<"Scan error">>, Lang)}
+           end;
+       _ ->
+           Txt = <<"Incorrect value in data form">>,
+           {error, xmpp:err_not_acceptable(Txt, Lang)}
+    end.
 
 get_connection_params(Host, From, IRCServer) ->
     [_ | HostTail] = str:tokens(Host, <<".">>),
@@ -805,212 +631,118 @@ get_connection_params(Host, ServerHost, From,
           iolist_to_binary(NewPassword)}
     end.
 
-adhoc_join(_From, _To,
-          #adhoc_request{action = <<"cancel">>} = Request) ->
-    adhoc:produce_response(Request,
-                          #adhoc_response{status = canceled});
-adhoc_join(From, To,
-          #adhoc_request{lang = Lang, node = _Node,
-                         action = _Action, xdata = XData} =
-              Request) ->
-    if XData == false ->
-          Form = #xmlel{name = <<"x">>,
-                        attrs =
-                            [{<<"xmlns">>, ?NS_XDATA},
-                             {<<"type">>, <<"form">>}],
-                        children =
-                            [#xmlel{name = <<"title">>, attrs = [],
-                                    children =
-                                        [{xmlcdata,
-                                          translate:translate(Lang,
-                                                              <<"Join IRC channel">>)}]},
-                             #xmlel{name = <<"field">>,
-                                    attrs =
-                                        [{<<"var">>, <<"channel">>},
-                                         {<<"type">>, <<"text-single">>},
-                                         {<<"label">>,
-                                          translate:translate(Lang,
-                                                              <<"IRC channel (don't put the first #)">>)}],
-                                    children =
-                                        [#xmlel{name = <<"required">>,
-                                                attrs = [], children = []}]},
-                             #xmlel{name = <<"field">>,
-                                    attrs =
-                                        [{<<"var">>, <<"server">>},
-                                         {<<"type">>, <<"text-single">>},
-                                         {<<"label">>,
-                                          translate:translate(Lang,
-                                                              <<"IRC server">>)}],
-                                    children =
-                                        [#xmlel{name = <<"required">>,
-                                                attrs = [], children = []}]}]},
-          adhoc:produce_response(Request,
-                                 #adhoc_response{status = executing,
-                                                 elements = [Form]});
+adhoc_join(_From, _To, #adhoc_command{action = cancel} = Request) ->
+    xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled});
+adhoc_join(_From, _To, #adhoc_command{lang = Lang, xdata = undefined} = Request) ->
+    X = #xdata{type = form,
+              title = translate:translate(Lang, <<"Join IRC channel">>),
+              fields = [#xdata_field{var = <<"channel">>,
+                                     type = 'text-single',
+                                     label = translate:translate(
+                                               Lang, <<"IRC channel (don't put the first #)">>),
+                                     required = true},
+                        #xdata_field{var = <<"server">>,
+                                     type = 'text-single',
+                                     label = translate:translate(Lang, <<"IRC server">>),
+                                     required = true}]},
+    xmpp_utils:make_adhoc_response(
+      Request, #adhoc_command{status = executing, xdata = X});
+adhoc_join(From, To, #adhoc_command{lang = Lang, xdata = X} = Request) ->
+    Channel = case xmpp_util:get_xdata_values(<<"channel">>, X) of
+                 [C] -> C;
+                 _ -> false
+             end,
+    Server = case xmpp_util:get_xdata_values(<<"server">>, X) of
+                [S] -> S;
+                _ -> false
+            end,
+    if Channel /= false, Server /= false ->
+           RoomJID = jid:make(<<Channel/binary, "%", Server/binary>>,
+                              To#jid.server),
+           Reason = translate:translate(Lang, <<"Join the IRC channel here.">>),
+           Body = iolist_to_binary(
+                    io_lib:format(
+                      translate:translate(
+                        Lang, <<"Join the IRC channel in this Jabber ID: ~s">>),
+                      [jid:to_string(RoomJID)])),
+           Invite = #message{
+                       body = xmpp:mk_text(Body, Lang),
+                       sub_els = [#muc_user{
+                                     invites = [#muc_invite{from = From,
+                                                            reason = Reason}]},
+                                  #x_conference{reason = Reason,
+                                                jid = RoomJID}]},
+           ejabberd_router:route(RoomJID, From, Invite),
+           xmpp_util:make_adhoc_response(
+             Request, #adhoc_command{status = completed});
        true ->
-          case jlib:parse_xdata_submit(XData) of
-            invalid ->
-                Txt1 = <<"Incorrect data form">>,
-                {error, ?ERRT_BAD_REQUEST(Lang, Txt1)};
-            Fields ->
-                Channel = case lists:keysearch(<<"channel">>, 1, Fields)
-                              of
-                            {value, {<<"channel">>, [C]}} -> C;
-                            _ -> false
-                          end,
-                Server = case lists:keysearch(<<"server">>, 1, Fields)
-                             of
-                           {value, {<<"server">>, [S]}} -> S;
-                           _ -> false
-                         end,
-                if Channel /= false, Server /= false ->
-                       RoomJID = <<Channel/binary, "%", Server/binary, "@",
-                                   (To#jid.server)/binary>>,
-                       Invite = #xmlel{name = <<"message">>, attrs = [],
-                                       children =
-                                           [#xmlel{name = <<"x">>,
-                                                   attrs =
-                                                       [{<<"xmlns">>,
-                                                         ?NS_MUC_USER}],
-                                                   children =
-                                                       [#xmlel{name =
-                                                                   <<"invite">>,
-                                                               attrs =
-                                                                   [{<<"from">>,
-                                                                     jid:to_string(From)}],
-                                                               children =
-                                                                   [#xmlel{name
-                                                                               =
-                                                                               <<"reason">>,
-                                                                           attrs
-                                                                               =
-                                                                               [],
-                                                                           children
-                                                                               =
-                                                                               [{xmlcdata,
-                                                                                 translate:translate(Lang,
-                                                                                                     <<"Join the IRC channel here.">>)}]}]}]},
-                                            #xmlel{name = <<"x">>,
-                                                   attrs =
-                                                       [{<<"xmlns">>,
-                                                         ?NS_XCONFERENCE}],
-                                                   children =
-                                                       [{xmlcdata,
-                                                         translate:translate(Lang,
-                                                                             <<"Join the IRC channel here.">>)}]},
-                                            #xmlel{name = <<"body">>,
-                                                   attrs = [],
-                                                   children =
-                                                       [{xmlcdata,
-                                                         iolist_to_binary(
-                                                            io_lib:format(
-                                                              translate:translate(
-                                                                Lang,
-                                                                <<"Join the IRC channel in this Jabber ID: ~s">>),
-                                                              [RoomJID]))}]}]},
-                       ejabberd_router:route(jid:from_string(RoomJID), From,
-                                             Invite),
-                       adhoc:produce_response(Request,
-                                              #adhoc_response{status =
-                                                                  completed});
-                   true -> {error, ?ERR_BAD_REQUEST}
-                end
-          end
+           Txt = <<"Missing 'channel' or 'server' in the data form">>,
+           {error, xmpp:err_bad_request(Txt, Lang)}
     end.
 
+-spec adhoc_register(binary(), jid(), jid(), adhoc_command()) ->
+                           adhoc_command() | {error, error()}.
 adhoc_register(_ServerHost, _From, _To,
-              #adhoc_request{action = <<"cancel">>} = Request) ->
-    adhoc:produce_response(Request,
-                          #adhoc_response{status = canceled});
+              #adhoc_command{action = cancel} = Request) ->
+    xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled});
 adhoc_register(ServerHost, From, To,
-              #adhoc_request{lang = Lang, node = _Node, xdata = XData,
-                             action = Action} =
-                  Request) ->
+              #adhoc_command{lang = Lang, xdata = X,
+                             action = Action} = Request) ->
     #jid{user = User} = From,
     #jid{lserver = Host} = To,
-    if XData == false ->
-          case get_data(ServerHost, Host, From) of
-            error -> Username = User, ConnectionsParams = [];
-            empty -> Username = User, ConnectionsParams = [];
-            Data ->
-                {Username, ConnectionsParams} =
-                       get_username_and_connection_params(Data)
-          end,
-          Error = false;
-       true ->
-          case jlib:parse_xdata_submit(XData) of
-            invalid ->
-                Txt1 = <<"Incorrect data form">>,
-                Error = {error, ?ERRT_BAD_REQUEST(Lang, Txt1)},
-                Username = false,
-                ConnectionsParams = false;
-            Fields ->
-                Username = case lists:keysearch(<<"username">>, 1,
-                                                Fields)
-                               of
-                             {value, {<<"username">>, U}} -> U;
-                             _ -> User
-                           end,
-                ConnectionsParams = parse_connections_params(Fields),
-                Error = false
-          end
-    end,
-    if Error /= false -> Error;
-       Action == <<"complete">> ->
-          case set_data(ServerHost, Host, From,
-                        [{username, Username},
-                         {connections_params, ConnectionsParams}])
-              of
-            {atomic, _} ->
-                adhoc:produce_response(Request,
-                                       #adhoc_response{status = completed});
-            _ ->
-                Txt2 = <<"Database failure">>,
-                {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, Txt2)}
-          end;
+    {Username, ConnectionsParams} =
+       if X == undefined ->
+               case get_data(ServerHost, Host, From) of
+                   error -> {User, []};
+                   empty -> {User, []};
+                   Data -> get_username_and_connection_params(Data)
+               end;
+          true ->
+               {case xmpp_util:get_xdata_values(<<"username">>, X) of
+                    [U] -> U;
+                    _ -> User
+                end, parse_connections_params(X)}
+       end,
+    if Action == complete ->
+           case set_data(ServerHost, Host, From,
+                         [{username, Username},
+                          {connections_params, ConnectionsParams}]) of
+               {atomic, _} ->
+                   xmpp_util:make_adhoc_response(
+                     Request, #adhoc_command{status = completed});
+               _ ->
+                   Txt = <<"Database failure">>,
+                   {error, xmpp:err_internal_server_error(Txt, Lang)}
+           end;
        true ->
-          Form = generate_adhoc_register_form(Lang, Username,
-                                              ConnectionsParams),
-          adhoc:produce_response(Request,
-                                 #adhoc_response{status = executing,
-                                                 elements = [Form],
-                                                 actions =
-                                                     [<<"next">>,
-                                                      <<"complete">>]})
+           Form = generate_adhoc_register_form(Lang, Username,
+                                               ConnectionsParams),
+           xmpp_util:make_adhoc_response(
+             Request, #adhoc_command{
+                         status = executing,
+                         xdata = Form,
+                         actions = #adhoc_actions{next = true,
+                                                  complete = true}})
     end.
 
 generate_adhoc_register_form(Lang, Username,
                             ConnectionsParams) ->
-    #xmlel{name = <<"x">>,
-          attrs =
-              [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
-          children =
-              [#xmlel{name = <<"title">>, attrs = [],
-                      children =
-                          [{xmlcdata,
-                            translate:translate(Lang, <<"IRC settings">>)}]},
-               #xmlel{name = <<"instructions">>, attrs = [],
-                      children =
-                          [{xmlcdata,
-                            translate:translate(Lang,
-                                                <<"Enter username and encodings you wish "
-                                                  "to use for connecting to IRC servers. "
-                                                  " Press 'Next' to get more fields to "
-                                                  "fill in.  Press 'Complete' to save settings.">>)}]},
-               #xmlel{name = <<"field">>,
-                      attrs =
-                          [{<<"var">>, <<"username">>},
-                           {<<"type">>, <<"text-single">>},
-                           {<<"label">>,
-                            translate:translate(Lang, <<"IRC username">>)}],
-                      children =
-                          [#xmlel{name = <<"required">>, attrs = [],
-                                  children = []},
-                           #xmlel{name = <<"value">>, attrs = [],
-                                  children = [{xmlcdata, Username}]}]}]
-                ++
-                generate_connection_params_fields(Lang,
-                                                  ConnectionsParams, 1, [])}.
+    #xdata{type = form,
+          title = translate:translate(Lang, <<"IRC settings">>),
+          instructions = [translate:translate(
+                            Lang,
+                            <<"Enter username and encodings you wish "
+                              "to use for connecting to IRC servers. "
+                              " Press 'Next' to get more fields to "
+                              "fill in.  Press 'Complete' to save settings.">>)],
+          fields = [#xdata_field{
+                       var = <<"username">>,
+                       type = 'text-single',
+                       label = translate:translate(Lang, <<"IRC username">>),
+                       required = true,
+                       values = [Username]}
+                    | generate_connection_params_fields(
+                        Lang, ConnectionsParams, 1, [])]}.
 
 generate_connection_params_fields(Lang, [], Number,
                                  Acc) ->
@@ -1061,91 +793,67 @@ generate_connection_params_field(Lang, Server, Encoding,
                   end,
     NumberString =
        iolist_to_binary(integer_to_list(Number)),
-    [#xmlel{name = <<"field">>,
-           attrs =
-               [{<<"var">>, <<"password", NumberString/binary>>},
-                {<<"type">>, <<"text-single">>},
-                {<<"label">>,
-                 iolist_to_binary(
-                    io_lib:format(
-                      translate:translate(Lang, <<"Password ~b">>),
-                      [Number]))}],
-           children =
-               [#xmlel{name = <<"value">>, attrs = [],
-                       children = [{xmlcdata, PasswordUsed}]}]},
-     #xmlel{name = <<"field">>,
-           attrs =
-               [{<<"var">>, <<"port", NumberString/binary>>},
-                {<<"type">>, <<"text-single">>},
-                {<<"label">>,
-                 iolist_to_binary(
-                    io_lib:format(translate:translate(Lang, <<"Port ~b">>),
-                                  [Number]))}],
-           children =
-               [#xmlel{name = <<"value">>, attrs = [],
-                       children = [{xmlcdata, PortUsed}]}]},
-     #xmlel{name = <<"field">>,
-           attrs =
-               [{<<"var">>, <<"encoding", NumberString/binary>>},
-                {<<"type">>, <<"list-single">>},
-                {<<"label">>,
-                 list_to_binary(
-                    io_lib:format(translate:translate(
-                                    Lang,
-                                    <<"Encoding for server ~b">>),
-                                  [Number]))}],
-           children =
-               [#xmlel{name = <<"value">>, attrs = [],
-                       children = [{xmlcdata, EncodingUsed}]}
-                | lists:map(fun (E) ->
-                                    #xmlel{name = <<"option">>,
-                                           attrs = [{<<"label">>, E}],
-                                           children =
-                                               [#xmlel{name = <<"value">>,
-                                                       attrs = [],
-                                                       children =
-                                                           [{xmlcdata, E}]}]}
-                            end,
-                            ?POSSIBLE_ENCODINGS)]},
-     #xmlel{name = <<"field">>,
-           attrs =
-               [{<<"var">>, <<"server", NumberString/binary>>},
-                {<<"type">>, <<"text-single">>},
-                {<<"label">>,
-                 list_to_binary(
-                    io_lib:format(translate:translate(Lang, <<"Server ~b">>),
-                                  [Number]))}],
-           children =
-               [#xmlel{name = <<"value">>, attrs = [],
-                       children = [{xmlcdata, Server}]}]}].
-
-parse_connections_params(Fields) ->
+    [#xdata_field{var = <<"password", NumberString/binary>>,
+                 type = 'text-single',
+                 label =  iolist_to_binary(
+                            io_lib:format(
+                              translate:translate(Lang, <<"Password ~b">>),
+                              [Number])),
+                 values = [PasswordUsed]},
+     #xdata_field{var = <<"port", NumberString/binary>>,
+                 type = 'text-single',
+                 label = iolist_to_binary(
+                           io_lib:format(
+                             translate:translate(Lang, <<"Port ~b">>),
+                             [Number])),
+                 values = [PortUsed]},
+     #xdata_field{var = <<"encoding", NumberString/binary>>,
+                 type = 'list-single',
+                 label = list_to_binary(
+                           io_lib:format(
+                             translate:translate(Lang, <<"Encoding for server ~b">>),
+                             [Number])),
+                 values = [EncodingUsed],
+                 options = [#xdata_option{label = E, value = E}
+                            || E <- ?POSSIBLE_ENCODINGS]},
+     #xdata_field{var = <<"server", NumberString/binary>>,
+                 type = 'text-single',
+                 label = list_to_binary(
+                           io_lib:format(
+                             translate:translate(Lang, <<"Server ~b">>),
+                             [Number])),
+                 values = [Server]}].
+
+parse_connections_params(#xdata{fields = Fields}) ->
     Servers = lists:flatmap(
-                fun({<<"server", Var/binary>>, Value}) ->
-                        [{Var, Value}];
+                fun(#xdata_field{var = <<"server", Var/binary>>,
+                                values = Values}) ->
+                        [{Var, Values}];
                    (_) ->
                         []
                 end, Fields),
     Encodings = lists:flatmap(
-                  fun({<<"encoding", Var/binary>>, Value}) ->
-                          [{Var, Value}];
+                  fun(#xdata_field{var = <<"encoding", Var/binary>>,
+                                  values = Values}) ->
+                          [{Var, Values}];
                      (_) ->
                           []
                   end, Fields),
     Ports = lists:flatmap(
-              fun({<<"port", Var/binary>>, Value}) ->
-                      [{Var, Value}];
+              fun(#xdata_field{var = <<"port", Var/binary>>,
+                              values = Values}) ->
+                      [{Var, Values}];
                  (_) ->
                       []
               end, Fields),
     Passwords = lists:flatmap(
-                  fun({<<"password", Var/binary>>, Value}) ->
-                          [{Var, Value}];
+                  fun(#xdata_field{var = <<"password", Var/binary>>,
+                                  values = Values}) ->
+                          [{Var, Values}];
                      (_) ->
                           []
                   end, Fields),
-    parse_connections_params(Servers, Encodings, Ports,
-                            Passwords).
+    parse_connections_params(Servers, Encodings, Ports, Passwords).
 
 retrieve_connections_params(ConnectionParams,
                            ServerN) ->
@@ -1263,28 +971,19 @@ mod_opt_type(host) -> fun iolist_to_binary/1;
 mod_opt_type(_) ->
     [access, db_type, default_encoding, host].
 
+-spec extract_ident(stanza()) -> binary().
 extract_ident(Packet) ->
-    case fxml:get_subtag(Packet, <<"headers">>) of
-       {xmlel, _Name, _Attrs, Headers} ->
-           extract_header(<<"X-Irc-Ident">>, Headers);
-       _ ->
-           "chatmovil"
-    end.
+    Hdrs = extract_headers(Packet),
+    proplists:get_value(<<"X-Irc-Ident">>, Hdrs, <<"chatmovil">>).
 
+-spec extract_ip_address(stanza()) -> binary().
 extract_ip_address(Packet) ->
-    case fxml:get_subtag(Packet, <<"headers">>) of
-       {xmlel, _Name, _Attrs, Headers} ->
-           extract_header(<<"X-Forwarded-For">>, Headers);
-       _ ->
-           "127.0.0.1"
+    Hdrs = extract_headers(Packet),
+    proplists:get_value(<<"X-Forwarded-For">>, Hdrs, <<"127.0.0.1">>).
+
+-spec extract_headers(stanza()) -> [{binary(), binary()}].
+extract_headers(Packet) ->
+    case xmpp:get_subtag(Packet, #shim{}) of
+       #shim{headers = Hs} -> Hs;
+       false -> []
     end.
-
-extract_header(HeaderName, [{xmlel, _Name, _Attrs, [{xmlcdata, Value}]} | Tail]) ->
-    case fxml:get_attr(<<"name">>, _Attrs) of
-       {value, HeaderName} ->
-           binary_to_list(Value);
-       _ ->
-           extract_header(HeaderName, Tail)
-    end;
-extract_header(_HeaderName, _Headers) ->
-       false.
index 098c8c28617f98469dfd56d371b30dfa26ab9cfe..fb301330a1ce16741e5e4f62b142603471d4b366 100644 (file)
@@ -41,8 +41,7 @@
 
 -include("ejabberd.hrl").
 -include("logger.hrl").
-
--include("jlib.hrl").
+-include("xmpp.hrl").
 
 -define(SETS, gb_sets).
 
@@ -66,6 +65,8 @@
         inbuf = <<"">>        :: binary(),
          outbuf = <<"">>       :: binary()}).
 
+-type state() :: #state{}.
+
 %-define(DBGFSM, true).
 
 -ifdef(DBGFSM).
@@ -228,27 +229,13 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
                                           (iolist_to_binary(S))/binary>>}
        end).
 
-get_password_from_presence(#xmlel{name = <<"presence">>,
-                                 children = Els}) ->
-    case lists:filter(fun (El) ->
-                             case El of
-                               #xmlel{name = <<"x">>, attrs = Attrs} ->
-                                   case fxml:get_attr_s(<<"xmlns">>, Attrs) of
-                                     ?NS_MUC -> true;
-                                     _ -> false
-                                   end;
-                               _ -> false
-                             end
-                     end,
-                     Els)
-       of
-      [ElXMUC | _] ->
-         case fxml:get_subtag(ElXMUC, <<"password">>) of
-           #xmlel{name = <<"password">>} = PasswordTag ->
-               {true, fxml:get_tag_cdata(PasswordTag)};
-           _ -> false
-         end;
-      _ -> false
+-spec get_password_from_presence(presence()) -> {true, binary()} | false.
+get_password_from_presence(#presence{} = Pres) ->
+    case xmpp:get_subtag(Pres, #muc{}) of
+       #muc{password = Password} ->
+           {true, Password};
+       _ ->
+           false
     end.
 
 %%----------------------------------------------------------------------
@@ -257,284 +244,243 @@ get_password_from_presence(#xmlel{name = <<"presence">>,
 %%          {next_state, NextStateName, NextStateData, Timeout} |
 %%          {stop, Reason, NewStateData}
 %%----------------------------------------------------------------------
-handle_info({route_chan, Channel, Resource,
-            #xmlel{name = <<"presence">>, attrs = Attrs} =
-                Presence},
+handle_info({route_chan, _, _, #presence{type = error}}, _, StateData) ->
+    {stop, normal, StateData};
+handle_info({route_chan, Channel, _, #presence{type = unavailable}},
            StateName, StateData) ->
-    NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
-                    <<"unavailable">> ->
-                        send_stanza_unavailable(Channel, StateData),
-                        S1 = (?SEND((io_lib:format("PART #~s\r\n",
-                                                   [Channel])))),
-                        S1#state{channels =
-                                     dict:erase(Channel, S1#state.channels)};
-                    <<"subscribe">> -> StateData;
-                    <<"subscribed">> -> StateData;
-                    <<"unsubscribe">> -> StateData;
-                    <<"unsubscribed">> -> StateData;
-                    <<"error">> -> stop;
-                    _ ->
-                        Nick = case Resource of
-                                 <<"">> -> StateData#state.nick;
-                                 _ -> Resource
-                               end,
-                        S1 = if Nick /= StateData#state.nick ->
-                                    S11 = (?SEND((io_lib:format("NICK ~s\r\n",
-                                                                [Nick])))),
-                                    S11#state{nickchannel = Channel};
-                                true -> StateData
-                             end,
-                        case dict:is_key(Channel, S1#state.channels) of
-                          true -> S1;
-                          _ ->
-                              case get_password_from_presence(Presence) of
-                                {true, Password} ->
-                                    S2 =
-                                        (?SEND((io_lib:format("JOIN #~s ~s\r\n",
-                                                              [Channel,
-                                                               Password]))));
-                                _ ->
-                                    S2 = (?SEND((io_lib:format("JOIN #~s\r\n",
-                                                               [Channel]))))
-                              end,
-                              S2#state{channels =
-                                           dict:store(Channel, (?SETS):new(),
-                                                      S1#state.channels)}
-                        end
-                  end,
-    if NewStateData == stop -> {stop, normal, StateData};
-       true ->
-          case dict:fetch_keys(NewStateData#state.channels) of
-            [] -> {stop, normal, NewStateData};
-            _ -> {next_state, StateName, NewStateData}
-          end
-    end;
+    send_stanza_unavailable(Channel, StateData),
+    S1 = (?SEND((io_lib:format("PART #~s\r\n", [Channel])))),
+    S2 = S1#state{channels = dict:erase(Channel, S1#state.channels)},
+    {next_state, StateName, S2};
 handle_info({route_chan, Channel, Resource,
-            #xmlel{name = <<"message">>, attrs = Attrs} = El},
+            #presence{type = available} = Presence},
            StateName, StateData) ->
-    NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
-                    <<"groupchat">> ->
-                        case fxml:get_path_s(El, [{elem, <<"subject">>}, cdata])
-                            of
-                          <<"">> ->
-                              ejabberd_router:route(
-                                 jid:make(
-                                   iolist_to_binary([Channel,
-                                                     <<"%">>,
-                                                     StateData#state.server]),
-                                   StateData#state.host,
-                                   StateData#state.nick),
-                                 StateData#state.user, El),
-                              Body = fxml:get_path_s(El,
-                                                    [{elem, <<"body">>},
-                                                     cdata]),
-                              case Body of
-                                <<"/quote ", Rest/binary>> ->
-                                    ?SEND(<<Rest/binary, "\r\n">>);
-                                <<"/msg ", Rest/binary>> ->
-                                    ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
-                                <<"/me ", Rest/binary>> ->
-                                    Strings = str:tokens(Rest, <<"\n">>),
-                                    Res = iolist_to_binary(
-                                             lists:map(
-                                               fun (S) ->
-                                                       io_lib:format(
-                                                         "PRIVMSG #~s :\001ACTION ~s\001\r\n",
-                                                         [Channel, S])
-                                               end,
-                                               Strings)),
-                                    ?SEND(Res);
-                                <<"/ctcp ", Rest/binary>> ->
-                                    Words = str:tokens(Rest, <<" ">>),
-                                    case Words of
-                                      [CtcpDest | _] ->
-                                          CtcpCmd = str:to_upper(
-                                                       str:substr(Rest,
-                                                                  str:str(Rest,
-                                                                          <<" ">>)
-                                                                  + 1)),
-                                          Res =
-                                              io_lib:format("PRIVMSG ~s :\001~s\001\r\n",
-                                                            [CtcpDest,
-                                                             CtcpCmd]),
-                                          ?SEND(Res);
-                                      _ -> ok
-                                    end;
-                                _ ->
-                                    Strings = str:tokens(Body, <<"\n">>),
-                                    Res = iolist_to_binary(
-                                             lists:map(
-                                               fun (S) ->
-                                                       io_lib:format("PRIVMSG #~s :~s\r\n",
-                                                                     [Channel, S])
-                                               end,
-                                               Strings)),
-                                    ?SEND(Res)
-                              end;
-                          Subject ->
-                              Strings = str:tokens(Subject, <<"\n">>),
-                              Res = iolist_to_binary(
-                                       lists:map(
-                                         fun (S) ->
-                                                 io_lib:format("TOPIC #~s :~s\r\n",
-                                                               [Channel, S])
-                                         end,
-                                         Strings)),
-                              ?SEND(Res)
-                        end;
-                    Type
-                        when Type == <<"chat">>;
-                             Type == <<"">>;
-                             Type == <<"normal">> ->
-                        Body = fxml:get_path_s(El, [{elem, <<"body">>}, cdata]),
-                        case Body of
-                          <<"/quote ", Rest/binary>> ->
-                              ?SEND(<<Rest/binary, "\r\n">>);
-                          <<"/msg ", Rest/binary>> ->
-                              ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
-                          <<"/me ", Rest/binary>> ->
-                              Strings = str:tokens(Rest, <<"\n">>),
-                              Res = iolist_to_binary(
-                                       lists:map(
-                                         fun (S) ->
-                                                 io_lib:format(
-                                                   "PRIVMSG ~s :\001ACTION ~s\001\r\n",
-                                                   [Resource, S])
-                                         end,
-                                         Strings)),
-                              ?SEND(Res);
-                          <<"/ctcp ", Rest/binary>> ->
-                              Words = str:tokens(Rest, <<" ">>),
-                              case Words of
-                                [CtcpDest | _] ->
-                                    CtcpCmd = str:to_upper(
-                                                 str:substr(Rest,
-                                                            str:str(Rest,
-                                                                    <<" ">>)
-                                                            + 1)),
-                                    Res = io_lib:format("PRIVMSG ~s :~s\r\n",
-                                                        [CtcpDest,
-                                                         <<"\001",
-                                                           CtcpCmd/binary,
-                                                           "\001">>]),
-                                    ?SEND(Res);
-                                _ -> ok
-                              end;
-                          _ ->
-                              Strings = str:tokens(Body, <<"\n">>),
-                              Res = iolist_to_binary(
-                                       lists:map(
-                                         fun (S) ->
-                                                 io_lib:format(
-                                                   "PRIVMSG ~s :~s\r\n",
-                                                   [Resource, S])
-                                         end,
-                                         Strings)),
-                              ?SEND(Res)
-                        end;
-                    <<"error">> -> stop;
-                    _ -> StateData
-                  end,
-    if NewStateData == stop -> {stop, normal, StateData};
-       true -> {next_state, StateName, NewStateData}
-    end;
-handle_info({route_chan, Channel, Resource,
-            #xmlel{name = <<"iq">>} = El},
+    Nick = case Resource of
+              <<"">> -> StateData#state.nick;
+              _ -> Resource
+          end,
+    S1 = if Nick /= StateData#state.nick ->
+                S11 = (?SEND((io_lib:format("NICK ~s\r\n", [Nick])))),
+                S11#state{nickchannel = Channel};
+           true -> StateData
+        end,
+    {next_state, StateName,
+     case dict:is_key(Channel, S1#state.channels) of
+        true -> S1;
+        _ ->
+            case get_password_from_presence(Presence) of
+                {true, Password} ->
+                    S2 = ?SEND((io_lib:format("JOIN #~s ~s\r\n",
+                                              [Channel, Password])));
+                _ ->
+                    S2 = ?SEND((io_lib:format("JOIN #~s\r\n", [Channel])))
+            end,
+            S2#state{channels = dict:store(Channel, ?SETS:new(),
+                                           S1#state.channels)}
+     end};
+handle_info({route_chan, Channel, _Resource, #message{type = groupchat} = Msg},
            StateName, StateData) ->
+    {next_state, StateName,
+    case xmpp:get_text(Msg#message.subject) of
+       <<"">> ->
+           ejabberd_router:route(
+             jid:make(
+               iolist_to_binary([Channel,
+                                 <<"%">>,
+                                 StateData#state.server]),
+               StateData#state.host,
+               StateData#state.nick),
+             StateData#state.user, Msg),
+           Body = xmpp:get_text(Msg#message.body),
+           case Body of
+               <<"/quote ", Rest/binary>> ->
+                   ?SEND(<<Rest/binary, "\r\n">>);
+               <<"/msg ", Rest/binary>> ->
+                   ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
+               <<"/me ", Rest/binary>> ->
+                   Strings = str:tokens(Rest, <<"\n">>),
+                   Res = iolist_to_binary(
+                           lists:map(
+                             fun (S) ->
+                                     io_lib:format(
+                                       "PRIVMSG #~s :\001ACTION ~s\001\r\n",
+                                       [Channel, S])
+                             end,
+                             Strings)),
+                   ?SEND(Res);
+               <<"/ctcp ", Rest/binary>> ->
+                   Words = str:tokens(Rest, <<" ">>),
+                   case Words of
+                       [CtcpDest | _] ->
+                           CtcpCmd = str:to_upper(
+                                       str:substr(
+                                         Rest, str:str(Rest, <<" ">>) + 1)),
+                           Res = io_lib:format("PRIVMSG ~s :\001~s\001\r\n",
+                                               [CtcpDest, CtcpCmd]),
+                           ?SEND(Res);
+                       _ -> ok
+                   end;
+               _ ->
+                   Strings = str:tokens(Body, <<"\n">>),
+                   Res = iolist_to_binary(
+                           lists:map(
+                             fun (S) ->
+                                     io_lib:format("PRIVMSG #~s :~s\r\n",
+                                                   [Channel, S])
+                             end, Strings)),
+                   ?SEND(Res)
+           end;
+       Subject ->
+           Strings = str:tokens(Subject, <<"\n">>),
+           Res = iolist_to_binary(
+                   lists:map(
+                     fun (S) ->
+                             io_lib:format("TOPIC #~s :~s\r\n",
+                                           [Channel, S])
+                     end,
+                     Strings)),
+           ?SEND(Res)
+    end};
+handle_info({route_chan, _Channel, Resource, #message{type = Type} = Msg},
+           StateName, StateData) when Type == chat; Type == normal ->
+    Body = xmpp:get_text(Msg#message.body),
+    {next_state, StateName,
+     case Body of
+        <<"/quote ", Rest/binary>> ->
+            ?SEND(<<Rest/binary, "\r\n">>);
+        <<"/msg ", Rest/binary>> ->
+            ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
+        <<"/me ", Rest/binary>> ->
+            Strings = str:tokens(Rest, <<"\n">>),
+            Res = iolist_to_binary(
+                    lists:map(
+                      fun (S) ->
+                              io_lib:format(
+                                "PRIVMSG ~s :\001ACTION ~s\001\r\n",
+                                [Resource, S])
+                      end, Strings)),
+            ?SEND(Res);
+        <<"/ctcp ", Rest/binary>> ->
+            Words = str:tokens(Rest, <<" ">>),
+            case Words of
+                [CtcpDest | _] ->
+                    CtcpCmd = str:to_upper(
+                                str:substr(
+                                  Rest, str:str(Rest, <<" ">>) + 1)),
+                    Res = io_lib:format("PRIVMSG ~s :~s\r\n",
+                                        [CtcpDest,
+                                         <<"\001", CtcpCmd/binary, "\001">>]),
+                    ?SEND(Res);
+                _ -> ok
+            end;
+        _ ->
+            Strings = str:tokens(Body, <<"\n">>),
+            Res = iolist_to_binary(
+                    lists:map(
+                      fun (S) ->
+                              io_lib:format("PRIVMSG ~s :~s\r\n",
+                                            [Resource, S])
+                      end, Strings)),
+            ?SEND(Res)
+     end};
+handle_info({route_chan, _, _, #message{type = error}}, _, StateData) ->
+    {stop, normal, StateData};
+handle_info({route_chan, Channel, Resource,
+            #iq{type = T, sub_els = [_]} = Packet},
+           StateName, StateData) when T == set; T == get ->
     From = StateData#state.user,
-    To = jid:make(iolist_to_binary([Channel, <<"%">>,
-                                         StateData#state.server]),
-                      StateData#state.host, StateData#state.nick),
-    _ = case jlib:iq_query_info(El) of
-         #iq{xmlns = ?NS_MUC_ADMIN} = IQ ->
-             iq_admin(StateData, Channel, From, To, IQ);
-         #iq{xmlns = ?NS_VERSION} ->
-             Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n",
-                                 [Resource]),
-             _ = (?SEND(Res)),
-             Err = jlib:make_error_reply(El,
-                                         ?ERR_FEATURE_NOT_IMPLEMENTED),
-             ejabberd_router:route(To, From, Err);
-         #iq{xmlns = ?NS_TIME} ->
-             Res = io_lib:format("PRIVMSG ~s :\001TIME\001\r\n",
-                                 [Resource]),
-             _ = (?SEND(Res)),
-             Err = jlib:make_error_reply(El,
-                                         ?ERR_FEATURE_NOT_IMPLEMENTED),
-             ejabberd_router:route(To, From, Err);
-         #iq{xmlns = ?NS_VCARD} ->
-             Res = io_lib:format("WHOIS ~s \r\n", [Resource]),
-             _ = (?SEND(Res)),
-             Err = jlib:make_error_reply(El,
-                                         ?ERR_FEATURE_NOT_IMPLEMENTED),
-             ejabberd_router:route(To, From, Err);
-         #iq{} ->
-             Err = jlib:make_error_reply(El,
-                                         ?ERR_FEATURE_NOT_IMPLEMENTED),
-             ejabberd_router:route(To, From, Err);
-         _ -> ok
-       end,
+    To = jid:make(iolist_to_binary([Channel, <<"%">>, StateData#state.server]),
+                 StateData#state.host, StateData#state.nick),
+    try xmpp:decode_els(Packet) of
+       #iq{sub_els = [SubEl]} = IQ ->
+           case xmpp:get_ns(SubEl) of
+               ?NS_MUC_ADMIN ->
+                   iq_admin(StateData, Channel, From, To, IQ);
+               ?NS_VERSION ->
+                   Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n",
+                                       [Resource]),
+                   _ = (?SEND(Res)),
+                   Err = xmpp:err_feature_not_implemented(),
+                   ejabberd_router:route_error(To, From, Packet, Err);
+               ?NS_TIME ->
+                   Res = io_lib:format("PRIVMSG ~s :\001TIME\001\r\n",
+                                       [Resource]),
+                   _ = (?SEND(Res)),
+                   Err = xmpp:err_feature_not_implemented(),
+                   ejabberd_router:route_error(To, From, Packet, Err);
+               ?NS_VCARD ->
+                   Res = io_lib:format("WHOIS ~s \r\n", [Resource]),
+                   _ = (?SEND(Res)),
+                   Err = xmpp:err_feature_not_implemented(),
+                   ejabberd_router:route_error(To, From, Packet, Err);
+               _ ->
+                   Err = xmpp:err_feature_not_implemented(),
+                   ejabberd_router:route_error(To, From, Packet, Err)
+           end
+    catch _:{xmpp_codec, Why} ->
+           Err = xmpp:err_bad_request(
+                   xmpp:format_error(Why), xmpp:get_lang(Packet)),
+           ejabberd_router:route_error(To, From, Packet, Err)
+    end,
     {next_state, StateName, StateData};
-handle_info({route_chan, _Channel, _Resource, _Packet},
-           StateName, StateData) ->
+handle_info({route_chan, Channel, _, #iq{} = IQ}, StateName, StateData) ->
+    From = StateData#state.user,
+    To = jid:make(iolist_to_binary([Channel, <<"%">>, StateData#state.server]),
+                 StateData#state.host, StateData#state.nick),
+    Err = xmpp:err_feature_not_implemented(),
+    ejabberd_router:route_error(To, From, IQ, Err),
     {next_state, StateName, StateData};
-handle_info({route_nick, Nick,
-            #xmlel{name = <<"message">>, attrs = Attrs} = El},
+handle_info({route_nick, Nick, #message{type = chat} = Msg},
            StateName, StateData) ->
-    NewStateData = case fxml:get_attr_s(<<"type">>, Attrs) of
-                    <<"chat">> ->
-                        Body = fxml:get_path_s(El, [{elem, <<"body">>}, cdata]),
-                        case Body of
-                          <<"/quote ", Rest/binary>> ->
-                              ?SEND(<<Rest/binary, "\r\n">>);
-                          <<"/msg ", Rest/binary>> ->
-                              ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
-                          <<"/me ", Rest/binary>> ->
-                              Strings = str:tokens(Rest, <<"\n">>),
-                              Res = iolist_to_binary(
-                                       lists:map(
-                                         fun (S) ->
-                                                 io_lib:format(
-                                                   "PRIVMSG ~s :\001ACTION ~s\001\r\n",
-                                                   [Nick, S])
-                                         end,
-                                         Strings)),
-                              ?SEND(Res);
-                          <<"/ctcp ", Rest/binary>> ->
-                              Words = str:tokens(Rest, <<" ">>),
-                              case Words of
-                                [CtcpDest | _] ->
-                                    CtcpCmd = str:to_upper(
-                                                 str:substr(Rest,
-                                                            str:str(Rest,
-                                                                    <<" ">>)
-                                                            + 1)),
-                                    Res = io_lib:format("PRIVMSG ~s :~s\r\n",
-                                                        [CtcpDest,
-                                                         <<"\001",
-                                                           CtcpCmd/binary,
-                                                           "\001">>]),
-                                    ?SEND(Res);
-                                _ -> ok
-                              end;
-                          _ ->
-                              Strings = str:tokens(Body, <<"\n">>),
-                              Res = iolist_to_binary(
-                                       lists:map(
-                                         fun (S) ->
-                                                 io_lib:format(
-                                                   "PRIVMSG ~s :~s\r\n",
-                                                   [Nick, S])
-                                         end,
-                                         Strings)),
-                              ?SEND(Res)
-                        end;
-                    <<"error">> -> stop;
-                    _ -> StateData
-                  end,
-    if NewStateData == stop -> {stop, normal, StateData};
-       true -> {next_state, StateName, NewStateData}
-    end;
+    Body = xmpp:get_text(Msg#message.body),
+    {next_state, StateName,
+     case Body of
+        <<"/quote ", Rest/binary>> ->
+            ?SEND(<<Rest/binary, "\r\n">>);
+        <<"/msg ", Rest/binary>> ->
+            ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>);
+        <<"/me ", Rest/binary>> ->
+            Strings = str:tokens(Rest, <<"\n">>),
+            Res = iolist_to_binary(
+                    lists:map(
+                      fun (S) ->
+                              io_lib:format(
+                                "PRIVMSG ~s :\001ACTION ~s\001\r\n",
+                                [Nick, S])
+                      end,
+                      Strings)),
+            ?SEND(Res);
+        <<"/ctcp ", Rest/binary>> ->
+            Words = str:tokens(Rest, <<" ">>),
+            case Words of
+                [CtcpDest | _] ->
+                    CtcpCmd = str:to_upper(
+                                str:substr(Rest,
+                                           str:str(Rest,
+                                                   <<" ">>)
+                                           + 1)),
+                    Res = io_lib:format("PRIVMSG ~s :~s\r\n",
+                                        [CtcpDest,
+                                         <<"\001",
+                                           CtcpCmd/binary,
+                                           "\001">>]),
+                    ?SEND(Res);
+                _ -> ok
+            end;
+        _ ->
+            Strings = str:tokens(Body, <<"\n">>),
+            Res = iolist_to_binary(
+                    lists:map(
+                      fun (S) ->
+                              io_lib:format(
+                                "PRIVMSG ~s :~s\r\n",
+                                [Nick, S])
+                      end,
+                      Strings)),
+            ?SEND(Res)
+     end};
+handle_info({route_nick, _, #message{type = error}}, _, StateData) ->
+    {stop, normal, StateData};
 handle_info({route_nick, _Nick, _Packet}, StateName,
            StateData) ->
     {next_state, StateName, StateData};
@@ -561,13 +507,13 @@ handle_info({ircstring, <<$:, String/binary>>},
                                     {error,
                                      {error,
                                       error_unknown_num(StateData, String,
-                                                        <<"cancel">>),
+                                                        cancel),
                                       StateData}};
                                 [_, <<$5, _, _>> | _] ->
                                     {error,
                                      {error,
                                       error_unknown_num(StateData, String,
-                                                        <<"cancel">>),
+                                                        cancel),
                                       StateData}};
                                 _ ->
                                     ?DEBUG("unknown irc command '~s'~n",
@@ -702,11 +648,8 @@ terminate(_Reason, _StateName, FullStateData) ->
     {Error, StateData} = case FullStateData of
                           {error, SError, SStateData} -> {SError, SStateData};
                           _ ->
-                              {#xmlel{name = <<"error">>,
-                                      attrs = [{<<"code">>, <<"502">>}],
-                                      children =
-                                          [{xmlcdata,
-                                            <<"Server Connect Failed">>}]},
+                              {xmpp:err_internal_server_error(
+                                 <<"Server Connect Failed">>, ?MYLANG),
                                FullStateData}
                         end,
     (StateData#state.mod):closed_connection(StateData#state.host,
@@ -714,9 +657,7 @@ terminate(_Reason, _StateName, FullStateData) ->
                                             StateData#state.server),
     bounce_messages(<<"Server Connect Failed">>),
     lists:foreach(fun (Chan) ->
-                         Stanza = #xmlel{name = <<"presence">>,
-                                         attrs = [{<<"type">>, <<"error">>}],
-                                         children = [Error]},
+                         Stanza = xmpp:make_error(#presence{}, Error),
                          send_stanza(Chan, StateData, Stanza)
                  end,
                  dict:fetch_keys(StateData#state.channels)),
@@ -726,34 +667,24 @@ terminate(_Reason, _StateName, FullStateData) ->
     end,
     ok.
 
+-spec send_stanza(binary(), state(), stanza()) -> ok.
 send_stanza(Chan, StateData, Stanza) ->
     ejabberd_router:route(
-      jid:make(
-        iolist_to_binary([Chan,
-                          <<"%">>,
-                          StateData#state.server]),
-        StateData#state.host,
-        StateData#state.nick),
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host,
+              StateData#state.nick),
       StateData#state.user, Stanza).
 
+-spec send_stanza_unavailable(binary(), state()) -> ok.
 send_stanza_unavailable(Chan, StateData) ->
-    Affiliation = <<"member">>,
-    Role = <<"none">>,
-    Stanza = #xmlel{name = <<"presence">>,
-                   attrs = [{<<"type">>, <<"unavailable">>}],
-                   children =
-                       [#xmlel{name = <<"x">>,
-                               attrs = [{<<"xmlns">>, ?NS_MUC_USER}],
-                               children =
-                                   [#xmlel{name = <<"item">>,
-                                           attrs =
-                                               [{<<"affiliation">>,
-                                                 Affiliation},
-                                                {<<"role">>, Role}],
-                                           children = []},
-                                    #xmlel{name = <<"status">>,
-                                           attrs = [{<<"code">>, <<"110">>}],
-                                           children = []}]}]},
+    Affiliation = member,
+    Role = none,
+    Stanza = #presence{
+               type = unavailable,
+               sub_els = [#muc_user{
+                             items = [#muc_item{affiliation = Affiliation,
+                                                role = Role}],
+                             status_codes = [110]}]},
     send_stanza(Chan, StateData, Stanza).
 
 %%%----------------------------------------------------------------------
@@ -776,20 +707,14 @@ send_text(#state{socket = Socket, encoding = Encoding},
 
 bounce_messages(Reason) ->
     receive
-      {send_element, El} ->
-         #xmlel{attrs = Attrs} = El,
-         case fxml:get_attr_s(<<"type">>, Attrs) of
-           <<"error">> -> ok;
-           _ ->
-               Err = jlib:make_error_reply(El, <<"502">>, Reason),
-               From = jid:from_string(fxml:get_attr_s(<<"from">>,
-                                                        Attrs)),
-               To = jid:from_string(fxml:get_attr_s(<<"to">>,
-                                                      Attrs)),
-               ejabberd_router:route(To, From, Err)
-         end,
-         bounce_messages(Reason)
-      after 0 -> ok
+       {send_element, El} ->
+           From = xmpp:get_from(El),
+           To = xmpp:get_to(El),
+           Lang = xmpp:get_lang(El),
+           Err = xmpp:err_internal_server_error(Reason, Lang),
+           ejabberd_router:route_error(To, From, El, Err),
+           bounce_messages(Reason)
+    after 0 -> ok
     end.
 
 route_chan(Pid, Channel, Resource, Packet) ->
@@ -842,51 +767,32 @@ process_channel_list_user(StateData, Chan, User) ->
                                       {U2, <<"admin">>, <<"moderator">>};
                                   _ -> {User1, <<"member">>, <<"participant">>}
                                 end,
-    ejabberd_router:route(jid:make(iolist_to_binary([Chan,
-                                                          <<"%">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, User2),
-                         StateData#state.user,
-                         #xmlel{name = <<"presence">>, attrs = [],
-                                children =
-                                    [#xmlel{name = <<"x">>,
-                                            attrs =
-                                                [{<<"xmlns">>, ?NS_MUC_USER}],
-                                            children =
-                                                [#xmlel{name = <<"item">>,
-                                                        attrs =
-                                                            [{<<"affiliation">>,
-                                                              Affiliation},
-                                                             {<<"role">>,
-                                                              Role}],
-                                                        children = []}]}]}),
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, User2),
+      StateData#state.user,
+      #presence{
+        sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation,
+                                                role = Role}]}]}),
     case catch dict:update(Chan,
                           fun (Ps) -> (?SETS):add_element(User2, Ps) end,
-                          StateData#state.channels)
-       of
-      {'EXIT', _} -> StateData;
-      NS -> StateData#state{channels = NS}
+                          StateData#state.channels) of
+       {'EXIT', _} -> StateData;
+       NS -> StateData#state{channels = NS}
     end.
 
 process_channel_topic(StateData, Chan, String) ->
     Msg = ejabberd_regexp:replace(String, <<".*332[^:]*:">>,
                                  <<"">>),
-    Msg1 = filter_message(Msg),
-    ejabberd_router:route(jid:make(iolist_to_binary([Chan,
-                                                          <<"%">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"subject">>, attrs = [],
-                                            children = [{xmlcdata, Msg1}]},
-                                     #xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  <<"Topic for #", Chan/binary,
-                                                    ": ", Msg1/binary>>}]}]}).
+    Subject = filter_message(Msg),
+    Body = <<"Topic for #", Chan/binary, ": ", Subject/binary>>,
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host),
+      StateData#state.user,
+      #message{type = groupchat,
+              subject = xmpp:mk_text(Subject),
+              body = xmpp:mk_text(Body)}).
 
 process_channel_topic_who(StateData, Chan, String) ->
     Words = str:tokens(String, <<" ">>),
@@ -901,30 +807,17 @@ process_channel_topic_who(StateData, Chan, String) ->
             _ -> String
           end,
     Msg2 = filter_message(Msg1),
-    ejabberd_router:route(jid:make(iolist_to_binary([Chan,
-                                                          <<"%">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, <<"">>),
+      StateData#state.user,
+      #message{type = groupchat, body = xmpp:mk_text(Msg2)}).
 
 error_nick_in_use(_StateData, String) ->
     Msg = ejabberd_regexp:replace(String,
                                  <<".*433 +[^ ]* +">>, <<"">>),
     Msg1 = filter_message(Msg),
-    #xmlel{name = <<"error">>,
-          attrs =
-              [{<<"code">>, <<"409">>}, {<<"type">>, <<"cancel">>}],
-          children =
-              [#xmlel{name = <<"conflict">>,
-                      attrs = [{<<"xmlns">>, ?NS_STANZAS}], children = []},
-               #xmlel{name = <<"text">>,
-                      attrs = [{<<"xmlns">>, ?NS_STANZAS}],
-                      children = [{xmlcdata, Msg1}]}]}.
+    xmpp:err_conflict(Msg1, ?MYLANG).
 
 process_nick_in_use(StateData, String) ->
     Error = error_nick_in_use(StateData, String),
@@ -933,121 +826,73 @@ process_nick_in_use(StateData, String) ->
          % Shouldn't happen with a well behaved server
          StateData;
       Chan ->
-         ejabberd_router:route(jid:make(iolist_to_binary([Chan,
-                                                                <<"%">>,
-                                                                StateData#state.server]),
-                                             StateData#state.host,
-                                             StateData#state.nick),
-                               StateData#state.user,
-                               #xmlel{name = <<"presence">>,
-                                      attrs = [{<<"type">>, <<"error">>}],
-                                      children = [Error]}),
-         StateData#state{nickchannel = undefined}
+         ejabberd_router:route(
+           jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+                    StateData#state.host,
+                    StateData#state.nick),
+           StateData#state.user,
+           xmpp:make_error(#presence{}, Error)),
+           StateData#state{nickchannel = undefined}
     end.
 
 process_num_error(StateData, String) ->
-    Error = error_unknown_num(StateData, String,
-                             <<"continue">>),
-    lists:foreach(fun (Chan) ->
-                         ejabberd_router:route(
-                            jid:make(
-                              iolist_to_binary(
-                                [Chan,
-                                 <<"%">>,
-                                 StateData#state.server]),
-                              StateData#state.host,
-                              StateData#state.nick),
-                            StateData#state.user,
-                            #xmlel{name = <<"message">>,
-                                   attrs =
-                                       [{<<"type">>,
-                                         <<"error">>}],
-                                   children = [Error]})
-                 end,
-                 dict:fetch_keys(StateData#state.channels)),
+    Error = error_unknown_num(StateData, String, continue),
+    lists:foreach(
+      fun(Chan) ->
+             ejabberd_router:route(
+               jid:make(iolist_to_binary([Chan, $%, StateData#state.server]),
+                        StateData#state.host,
+                        StateData#state.nick),
+               StateData#state.user,
+               xmpp:make_error(#message{}, Error))
+      end, dict:fetch_keys(StateData#state.channels)),
     StateData.
 
 process_endofwhois(StateData, _String, Nick) ->
-    ejabberd_router:route(jid:make(iolist_to_binary([Nick,
-                                                          <<"!">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  <<"End of WHOIS">>}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]),
+              StateData#state.host),
+      StateData#state.user,
+      #message{type = chat, body = xmpp:mk_text(<<"End of WHOIS">>)}).
 
 process_whois311(StateData, String, Nick, Ident,
                 Irchost) ->
     Fullname = ejabberd_regexp:replace(String,
                                       <<".*311[^:]*:">>, <<"">>),
-    ejabberd_router:route(jid:make(iolist_to_binary([Nick,
-                                                          <<"!">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  iolist_to_binary(
-                                                     [<<"WHOIS: ">>,
-                                                      Nick,
-                                                      <<" is ">>,
-                                                      Ident,
-                                                      <<"@">>,
-                                                      Irchost,
-                                                      <<" : ">>,
-                                                      Fullname])}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]),
+              StateData#state.host, <<"">>),
+      StateData#state.user,
+      #message{type = chat,
+              body = xmpp:mk_text(
+                       iolist_to_binary(
+                         [<<"WHOIS: ">>, Nick, <<" is ">>, Ident,
+                          <<"@">>, Irchost, <<" : ">>, Fullname]))}).
 
 process_whois312(StateData, String, Nick, Ircserver) ->
     Ircserverdesc = ejabberd_regexp:replace(String,
                                            <<".*312[^:]*:">>, <<"">>),
-    ejabberd_router:route(jid:make(iolist_to_binary([Nick,
-                                                          <<"!">>,
-                                                          StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  iolist_to_binary(
-                                                     [<<"WHOIS: ">>,
-                                                      Nick,
-                                                      <<" use ">>,
-                                                      Ircserver,
-                                                      <<" : ">>,
-                                                      Ircserverdesc])}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]),
+              StateData#state.host, <<"">>),
+      StateData#state.user,
+      #message{type = chat,
+              body = xmpp:mk_text(
+                       iolist_to_binary(
+                         [<<"WHOIS: ">>, Nick, <<" use ">>, Ircserver,
+                          <<" : ">>, Ircserverdesc]))}).
 
 process_whois319(StateData, String, Nick) ->
     Chanlist = ejabberd_regexp:replace(String,
                                       <<".*319[^:]*:">>, <<"">>),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Nick,
-                                           <<"!">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  iolist_to_binary(
-                                                     [<<"WHOIS: ">>,
-                                                      Nick,
-                                                      <<" is on ">>,
-                                                      Chanlist])}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]),
+              StateData#state.host, <<"">>),
+      StateData#state.user,
+      #message{type = chat,
+              body = xmpp:mk_text(
+                       iolist_to_binary(
+                         [<<"WHOIS: ">>, Nick, <<" is on ">>, Chanlist]))}).
 
 process_chanprivmsg(StateData, Chan, From, String) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
@@ -1059,17 +904,11 @@ process_chanprivmsg(StateData, Chan, From, String) ->
             _ -> Msg
           end,
     Msg2 = filter_message(Msg1),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, FromUser),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, FromUser),
+      StateData#state.user,
+      #message{type = groupchat, body = xmpp:mk_text(Msg2)}).
 
 process_channotice(StateData, Chan, From, String) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
@@ -1081,17 +920,11 @@ process_channotice(StateData, Chan, From, String) ->
             _ -> <<"/me NOTICE: ", Msg/binary>>
           end,
     Msg2 = filter_message(Msg1),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, FromUser),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, FromUser),
+      StateData#state.user,
+      #message{type = groupchat, body = xmpp:mk_text(Msg2)}).
 
 process_privmsg(StateData, _Nick, From, String) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
@@ -1103,17 +936,11 @@ process_privmsg(StateData, _Nick, From, String) ->
             _ -> Msg
           end,
     Msg2 = filter_message(Msg1),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [FromUser,
-                                           <<"!">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]),
+              StateData#state.host, <<"">>),
+      StateData#state.user,
+      #message{type = chat, body = xmpp:mk_text(Msg2)}).
 
 process_notice(StateData, _Nick, From, String) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
@@ -1125,17 +952,11 @@ process_notice(StateData, _Nick, From, String) ->
             _ -> <<"/me NOTICE: ", Msg/binary>>
           end,
     Msg2 = filter_message(Msg1),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [FromUser,
-                                           <<"!">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"chat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]),
+              StateData#state.host),
+      StateData#state.user,
+      #message{type = chat, body = xmpp:mk_text(Msg2)}).
 
 process_version(StateData, _Nick, From) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
@@ -1160,54 +981,30 @@ process_topic(StateData, Chan, From, String) ->
     Msg = ejabberd_regexp:replace(String,
                                  <<".*TOPIC[^:]*:">>, <<"">>),
     Msg1 = filter_message(Msg),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, FromUser),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"subject">>, attrs = [],
-                                            children = [{xmlcdata, Msg1}]},
-                                     #xmlel{name = <<"body">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                  <<"/me has changed the subject to: ",
-                                                    Msg1/binary>>}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, FromUser),
+      StateData#state.user,
+      #message{type = groupchat,
+              subject = xmpp:mk_text(Msg1),
+              body = xmpp:mk_text(<<"/me has changed the subject to: ",
+                                    Msg1/binary>>)}).
 
 process_part(StateData, Chan, From, String) ->
     [FromUser | FromIdent] = str:tokens(From, <<"!">>),
     Msg = ejabberd_regexp:replace(String,
                                  <<".*PART[^:]*:">>, <<"">>),
     Msg1 = filter_message(Msg),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, FromUser),
-                         StateData#state.user,
-                         #xmlel{name = <<"presence">>,
-                                attrs = [{<<"type">>, <<"unavailable">>}],
-                                children =
-                                    [#xmlel{name = <<"x">>,
-                                            attrs =
-                                                [{<<"xmlns">>, ?NS_MUC_USER}],
-                                            children =
-                                                [#xmlel{name = <<"item">>,
-                                                        attrs =
-                                                            [{<<"affiliation">>,
-                                                              <<"member">>},
-                                                             {<<"role">>,
-                                                              <<"none">>}],
-                                                        children = []}]},
-                                     #xmlel{name = <<"status">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                   list_to_binary(
-                                                     [Msg1, " (",
-                                                      FromIdent, ")"])}]}]}),
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, FromUser),
+      StateData#state.user,
+      #presence{type = unavailable,
+               sub_els = [#muc_user{
+                             items = [#muc_item{affiliation = member,
+                                                role = none}]}],
+               status = xmpp:mk_text(
+                          list_to_binary([Msg1, " (", FromIdent, ")"]))}),
     case catch dict:update(Chan,
                           fun (Ps) -> remove_element(FromUser, Ps) end,
                           StateData#state.channels)
@@ -1221,81 +1018,40 @@ process_quit(StateData, From, String) ->
     Msg = ejabberd_regexp:replace(String,
                                  <<".*QUIT[^:]*:">>, <<"">>),
     Msg1 = filter_message(Msg),
-    dict:map(fun (Chan, Ps) ->
-                    case (?SETS):is_member(FromUser, Ps) of
-                      true ->
-                          ejabberd_router:route(jid:make(iolist_to_binary(
-                                                                 [Chan,
-                                                                  <<"%">>,
-                                                                  StateData#state.server]),
-                                                              StateData#state.host,
-                                                              FromUser),
-                                                StateData#state.user,
-                                                #xmlel{name = <<"presence">>,
-                                                       attrs =
-                                                           [{<<"type">>,
-                                                             <<"unavailable">>}],
-                                                       children =
-                                                           [#xmlel{name =
-                                                                       <<"x">>,
-                                                                   attrs =
-                                                                       [{<<"xmlns">>,
-                                                                         ?NS_MUC_USER}],
-                                                                   children =
-                                                                       [#xmlel{name
-                                                                                   =
-                                                                                   <<"item">>,
-                                                                               attrs
-                                                                                   =
-                                                                                   [{<<"affiliation">>,
-                                                                                     <<"member">>},
-                                                                                    {<<"role">>,
-                                                                                     <<"none">>}],
-                                                                               children
-                                                                                   =
-                                                                                   []}]},
-                                                            #xmlel{name =
-                                                                       <<"status">>,
-                                                                   attrs = [],
-                                                                   children =
-                                                                       [{xmlcdata,
-                                                                          list_to_binary(
-                                                                            [Msg1, " (",
-                                                                             FromIdent,
-                                                                             ")"])}]}]}),
-                          remove_element(FromUser, Ps);
-                      _ -> Ps
-                    end
-            end,
-            StateData#state.channels),
+    dict:map(
+      fun(Chan, Ps) ->
+             case (?SETS):is_member(FromUser, Ps) of
+                 true ->
+                     ejabberd_router:route(
+                       jid:make(iolist_to_binary([Chan, $%, StateData#state.server]),
+                                StateData#state.host,
+                                FromUser),
+                       StateData#state.user,
+                       #presence{type = unavailable,
+                                 sub_els = [#muc_user{
+                                               items = [#muc_item{
+                                                           affiliation = member,
+                                                           role = none}]}],
+                                 status = xmpp:mk_text(
+                                            list_to_binary([Msg1, " (", FromIdent, ")"]))}),
+                     remove_element(FromUser, Ps);
+                 _ ->
+                     Ps
+             end
+      end, StateData#state.channels),
     StateData.
 
 process_join(StateData, Channel, From, _String) ->
     [FromUser | FromIdent] = str:tokens(From, <<"!">>),
     [Chan | _] = binary:split(Channel, <<":#">>),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, FromUser),
-                         StateData#state.user,
-                         #xmlel{name = <<"presence">>, attrs = [],
-                                children =
-                                    [#xmlel{name = <<"x">>,
-                                            attrs =
-                                                [{<<"xmlns">>, ?NS_MUC_USER}],
-                                            children =
-                                                [#xmlel{name = <<"item">>,
-                                                        attrs =
-                                                            [{<<"affiliation">>,
-                                                              <<"member">>},
-                                                             {<<"role">>,
-                                                              <<"participant">>}],
-                                                        children = []}]},
-                                     #xmlel{name = <<"status">>, attrs = [],
-                                            children =
-                                                [{xmlcdata,
-                                                   list_to_binary(FromIdent)}]}]}),
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, FromUser),
+      StateData#state.user,
+      #presence{
+        sub_els = [#muc_user{items = [#muc_item{affiliation = member,
+                                                role = participant}]}],
+        status = xmpp:mk_text(list_to_binary(FromIdent))}),
     case catch dict:update(Chan,
                           fun (Ps) -> (?SETS):add_element(FromUser, Ps) end,
                           StateData#state.channels)
@@ -1306,160 +1062,67 @@ process_join(StateData, Channel, From, _String) ->
 
 process_mode_o(StateData, Chan, _From, Nick,
               Affiliation, Role) ->
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, Nick),
-                         StateData#state.user,
-                         #xmlel{name = <<"presence">>, attrs = [],
-                                children =
-                                    [#xmlel{name = <<"x">>,
-                                            attrs =
-                                                [{<<"xmlns">>, ?NS_MUC_USER}],
-                                            children =
-                                                [#xmlel{name = <<"item">>,
-                                                        attrs =
-                                                            [{<<"affiliation">>,
-                                                              Affiliation},
-                                                             {<<"role">>,
-                                                              Role}],
-                                                        children = []}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, Nick),
+      StateData#state.user,
+      #presence{
+        sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation,
+                                                role = Role}]}]}).
 
 process_kick(StateData, Chan, From, Nick, String) ->
     Msg = lists:last(str:tokens(String, <<":">>)),
     Msg2 = <<Nick/binary, " kicked by ", From/binary, " (",
             (filter_message(Msg))/binary, ")">>,
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, <<"">>),
-                         StateData#state.user,
-                         #xmlel{name = <<"message">>,
-                                attrs = [{<<"type">>, <<"groupchat">>}],
-                                children =
-                                    [#xmlel{name = <<"body">>, attrs = [],
-                                            children = [{xmlcdata, Msg2}]}]}),
-    ejabberd_router:route(jid:make(iolist_to_binary(
-                                          [Chan,
-                                           <<"%">>,
-                                           StateData#state.server]),
-                                       StateData#state.host, Nick),
-                         StateData#state.user,
-                         #xmlel{name = <<"presence">>,
-                                attrs = [{<<"type">>, <<"unavailable">>}],
-                                children =
-                                    [#xmlel{name = <<"x">>,
-                                            attrs =
-                                                [{<<"xmlns">>, ?NS_MUC_USER}],
-                                            children =
-                                                [#xmlel{name = <<"item">>,
-                                                        attrs =
-                                                            [{<<"affiliation">>,
-                                                              <<"none">>},
-                                                             {<<"role">>,
-                                                              <<"none">>}],
-                                                        children = []},
-                                                 #xmlel{name = <<"status">>,
-                                                        attrs =
-                                                            [{<<"code">>,
-                                                              <<"307">>}],
-                                                        children = []}]}]}).
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host),
+      StateData#state.user,
+      #message{type = groupchat, body = xmpp:mk_text(Msg2)}),
+    ejabberd_router:route(
+      jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]),
+              StateData#state.host, Nick),
+      StateData#state.user,
+      #presence{type = unavailable,
+               sub_els = [#muc_user{items = [#muc_item{
+                                                affiliation = none,
+                                                role = none}],
+                                    status_codes = [307]}]}).
 
 process_nick(StateData, From, NewNick) ->
     [FromUser | _] = str:tokens(From, <<"!">>),
     [Nick | _] = binary:split(NewNick, <<":">>),
-    NewChans = dict:map(fun (Chan, Ps) ->
-                               case (?SETS):is_member(FromUser, Ps) of
-                                 true ->
-                                     ejabberd_router:route(jid:make(
-                                                              iolist_to_binary(
-                                                                [Chan,
-                                                                 <<"%">>,
-                                                                 StateData#state.server]),
-                                                              StateData#state.host,
-                                                              FromUser),
-                                                           StateData#state.user,
-                                                           #xmlel{name =
-                                                                      <<"presence">>,
-                                                                  attrs =
-                                                                      [{<<"type">>,
-                                                                        <<"unavailable">>}],
-                                                                  children =
-                                                                      [#xmlel{name
-                                                                                  =
-                                                                                  <<"x">>,
-                                                                              attrs
-                                                                                  =
-                                                                                  [{<<"xmlns">>,
-                                                                                    ?NS_MUC_USER}],
-                                                                              children
-                                                                                  =
-                                                                                  [#xmlel{name
-                                                                                              =
-                                                                                              <<"item">>,
-                                                                                          attrs
-                                                                                              =
-                                                                                              [{<<"affiliation">>,
-                                                                                                <<"member">>},
-                                                                                               {<<"role">>,
-                                                                                                <<"participant">>},
-                                                                                               {<<"nick">>,
-                                                                                                Nick}],
-                                                                                          children
-                                                                                              =
-                                                                                              []},
-                                                                                   #xmlel{name
-                                                                                              =
-                                                                                              <<"status">>,
-                                                                                          attrs
-                                                                                              =
-                                                                                              [{<<"code">>,
-                                                                                                <<"303">>}],
-                                                                                          children
-                                                                                              =
-                                                                                              []}]}]}),
-                                     ejabberd_router:route(jid:make(
-                                                              iolist_to_binary(
-                                                                [Chan,
-                                                                 <<"%">>,
-                                                                 StateData#state.server]),
-                                                              StateData#state.host,
-                                                              Nick),
-                                                           StateData#state.user,
-                                                           #xmlel{name =
-                                                                      <<"presence">>,
-                                                                  attrs = [],
-                                                                  children =
-                                                                      [#xmlel{name
-                                                                                  =
-                                                                                  <<"x">>,
-                                                                              attrs
-                                                                                  =
-                                                                                  [{<<"xmlns">>,
-                                                                                    ?NS_MUC_USER}],
-                                                                              children
-                                                                                  =
-                                                                                  [#xmlel{name
-                                                                                              =
-                                                                                              <<"item">>,
-                                                                                          attrs
-                                                                                              =
-                                                                                              [{<<"affiliation">>,
-                                                                                                <<"member">>},
-                                                                                               {<<"role">>,
-                                                                                                <<"participant">>}],
-                                                                                          children
-                                                                                              =
-                                                                                              []}]}]}),
-                                     (?SETS):add_element(Nick,
-                                                         remove_element(FromUser,
-                                                                        Ps));
-                                 _ -> Ps
-                               end
-                       end,
-                       StateData#state.channels),
+    NewChans =
+       dict:map(
+         fun(Chan, Ps) ->
+                 case (?SETS):is_member(FromUser, Ps) of
+                     true ->
+                         ejabberd_router:route(
+                           jid:make(iolist_to_binary([Chan, $%, StateData#state.server]),
+                                    StateData#state.host,
+                                    FromUser),
+                           StateData#state.user,
+                           #presence{
+                              type = unavailable,
+                              sub_els = [#muc_user{
+                                            items = [#muc_item{
+                                                        affiliation = member,
+                                                        role = participant,
+                                                        nick = Nick}],
+                                            status_codes = [303]}]}),
+                         ejabberd_router:route(
+                           jid:make(iolist_to_binary([Chan, $%, StateData#state.server]),
+                                    StateData#state.host, Nick),
+                           StateData#state.user,
+                           #presence{
+                              sub_els = [#muc_user{
+                                            items = [#muc_item{
+                                                        affiliation = member,
+                                                        role = participant}]}]}),
+                         (?SETS):add_element(Nick, remove_element(FromUser, Ps));
+                     _ -> Ps
+                 end
+         end, StateData#state.channels),
     if FromUser == StateData#state.nick ->
           StateData#state{nick = Nick, nickchannel = undefined,
                           channels = NewChans};
@@ -1467,43 +1130,23 @@ process_nick(StateData, From, NewNick) ->
     end.
 
 process_error(StateData, String) ->
-    lists:foreach(fun (Chan) ->
-                         ejabberd_router:route(jid:make(
-                                                  iolist_to_binary(
-                                                    [Chan,
-                                                     <<"%">>,
-                                                     StateData#state.server]),
-                                                  StateData#state.host,
-                                                  StateData#state.nick),
-                                               StateData#state.user,
-                                               #xmlel{name = <<"presence">>,
-                                                      attrs =
-                                                          [{<<"type">>,
-                                                            <<"error">>}],
-                                                      children =
-                                                          [#xmlel{name =
-                                                                      <<"error">>,
-                                                                  attrs =
-                                                                      [{<<"code">>,
-                                                                        <<"502">>}],
-                                                                  children =
-                                                                      [{xmlcdata,
-                                                                        String}]}]})
-                 end,
-                 dict:fetch_keys(StateData#state.channels)).
+    lists:foreach(
+      fun(Chan) ->
+             ejabberd_router:route(
+               jid:make(iolist_to_binary([Chan, $%, StateData#state.server]),
+                        StateData#state.host,
+                        StateData#state.nick),
+               StateData#state.user,
+               xmpp:make_error(
+                 #presence{},
+                 xmpp:err_internal_server_error(String, ?MYLANG)))
+      end, dict:fetch_keys(StateData#state.channels)).
 
 error_unknown_num(_StateData, String, Type) ->
     Msg = ejabberd_regexp:replace(String,
                                  <<".*[45][0-9][0-9] +[^ ]* +">>, <<"">>),
     Msg1 = filter_message(Msg),
-    #xmlel{name = <<"error">>,
-          attrs = [{<<"code">>, <<"500">>}, {<<"type">>, Type}],
-          children =
-              [#xmlel{name = <<"undefined-condition">>,
-                      attrs = [{<<"xmlns">>, ?NS_STANZAS}], children = []},
-               #xmlel{name = <<"text">>,
-                      attrs = [{<<"xmlns">>, ?NS_STANZAS}],
-                      children = [{xmlcdata, Msg1}]}]}.
+    xmpp:err_undefined_condition(Type, Msg1, ?MYLANG).
 
 remove_element(E, Set) ->
     case (?SETS):is_element(E, Set) of
@@ -1512,49 +1155,33 @@ remove_element(E, Set) ->
     end.
 
 iq_admin(StateData, Channel, From, To,
-        #iq{type = Type, xmlns = XMLNS, sub_el = SubEl} = IQ) ->
-    case catch process_iq_admin(StateData, Channel, Type,
-                               SubEl)
-       of
-      {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]);
-      Res ->
-         if Res /= ignore ->
-                ResIQ = case Res of
-                          {result, ResEls} ->
-                              IQ#iq{type = result,
-                                    sub_el =
-                                        [#xmlel{name = <<"query">>,
-                                                attrs = [{<<"xmlns">>, XMLNS}],
-                                                children = ResEls}]};
-                          {error, Error} ->
-                              IQ#iq{type = error, sub_el = [SubEl, Error]}
-                        end,
-                ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ));
-            true -> ok
-         end
+        #iq{type = Type, sub_els = [SubEl]} = IQ) ->
+    try process_iq_admin(StateData, Channel, Type, SubEl) of
+       ignore ->
+           ignore;
+       {result, Result} ->
+           ejabberd_router:route(To, From, xmpp:make_iq_result(IQ, Result));
+       {error, Error} ->
+           ejabberd_router:route_error(To, From, IQ, Error)
+    catch E:R ->
+           ?ERROR_MSG("failed to process admin query from ~s: ~p",
+                      [jid:to_string(From), {E, {R, erlang:get_stacktrace()}}]),
+           ejabberd_router:route_error(
+             To, From, IQ, xmpp:internal_server_error())
     end.
 
-process_iq_admin(StateData, Channel, set, SubEl) ->
-    case fxml:get_subtag(SubEl, <<"item">>) of
-      false -> {error, ?ERR_BAD_REQUEST};
-      ItemEl ->
-         Nick = fxml:get_tag_attr_s(<<"nick">>, ItemEl),
-         Affiliation = fxml:get_tag_attr_s(<<"affiliation">>,
-                                          ItemEl),
-         Role = fxml:get_tag_attr_s(<<"role">>, ItemEl),
-         Reason = fxml:get_path_s(ItemEl,
-                                 [{elem, <<"reason">>}, cdata]),
-         process_admin(StateData, Channel, Nick, Affiliation,
-                       Role, Reason)
-    end;
-process_iq_admin(_StateData, _Channel, get, _SubEl) ->
-    {error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
-
-process_admin(_StateData, _Channel, <<"">>,
-             _Affiliation, _Role, _Reason) ->
-    {error, ?ERR_FEATURE_NOT_IMPLEMENTED};
-process_admin(StateData, Channel, Nick, _Affiliation,
-             <<"none">>, Reason) ->
+process_iq_admin(_StateData, _Channel, set, #muc_admin{items = []}) ->
+    {error, xmpp:err_bad_request()};
+process_iq_admin(StateData, Channel, set, #muc_admin{items = [Item|_]}) ->
+    process_admin(StateData, Channel, Item);
+process_iq_admin(_StateData, _Channel, _, _SubEl) ->
+    {error, xmpp:err_feature_not_implemented()}.
+
+process_admin(_StateData, _Channel, #muc_item{nick = <<"">>}) ->
+    {error, xmpp:err_feature_not_implemented()};
+process_admin(StateData, Channel, #muc_item{nick = Nick,
+                                           reason = Reason,
+                                           role = none}) ->
     case Reason of
       <<"">> ->
          send_text(StateData,
@@ -1564,10 +1191,9 @@ process_admin(StateData, Channel, Nick, _Affiliation,
                    io_lib:format("KICK #~s ~s :~s\r\n",
                                  [Channel, Nick, Reason]))
     end,
-    {result, []};
-process_admin(_StateData, _Channel, _Nick, _Affiliation,
-             _Role, _Reason) ->
-    {error, ?ERR_FEATURE_NOT_IMPLEMENTED}.
+    {result, undefined};
+process_admin(_StateData, _Channel, _Item) ->
+    {error, xmpp:err_feature_not_implemented()}.
 
 filter_message(Msg) ->
     list_to_binary(
index 9f8117ad300db64a47a6110b6864fe631d3d1e96..95cceb54c645f073181ba10d021292da124b324b 100644 (file)
@@ -13,7 +13,7 @@
 %% API
 -export([init/2, get_data/3, set_data/4, import/2]).
 
--include("jlib.hrl").
+-include("jid.hrl").
 -include("mod_irc.hrl").
 -include("logger.hrl").
 
index 6ac7befdf7afecdb4a2a6d43f21b5bee8f9549b5..a71859c5c2ee478b61365122ea85e217d15c9f20 100644 (file)
@@ -13,7 +13,7 @@
 %% API
 -export([init/2, get_data/3, set_data/4, import/2]).
 
--include("jlib.hrl").
+-include("jid.hrl").
 -include("mod_irc.hrl").
 
 %%%===================================================================
index 8aa428e54d016e6fe15107a2f4e96e9bd645167a..7905db91feeef2bdfba4937b24283a4fd5b08c15 100644 (file)
@@ -15,7 +15,7 @@
 %% API
 -export([init/2, get_data/3, set_data/4, import/1, import/2, export/1]).
 
--include("jlib.hrl").
+-include("jid.hrl").
 -include("mod_irc.hrl").
 -include("ejabberd_sql_pt.hrl").
 
index a927a6a9a464958e48cc199194ed8292d37182d5..b432431574cb895249aa3b0268f3f6d209e469eb 100644 (file)
@@ -41,7 +41,7 @@
          err_resource_constraint/0, err_resource_constraint/2,
          err_service_unavailable/0, err_service_unavailable/2,
          err_subscription_required/0, err_subscription_required/2,
-         err_undefined_condition/0, err_undefined_condition/2,
+         err_undefined_condition/1, err_undefined_condition/3,
          err_unexpected_request/0, err_unexpected_request/2]).
 
 %% XMPP stream errors
@@ -567,14 +567,16 @@ err_subscription_required(Text, Lang) ->
     err(auth, 'subscription-required', 407, Text, Lang).
 
 %% No error type is defined for <undefined-confition/>.
-%% We choose "modify" as it's used in RFC 6120 example.
--spec err_undefined_condition() -> error().
-err_undefined_condition() ->
-    err(modify, 'undefined-condition', 500).
-
--spec err_undefined_condition(binary(), binary() | undefined) -> error().
-err_undefined_condition(Text, Lang) ->
-    err(modify, 'undefined-condition', 500, Text, Lang).
+%% Let user provide the type.
+-spec err_undefined_condition('auth' | 'cancel' | 'continue' |
+                             'modify' | 'wait') -> error().
+err_undefined_condition(Type) ->
+    err(Type, 'undefined-condition', 500).
+
+-spec err_undefined_condition('auth' | 'cancel' | 'continue' | 'modify' | 'wait',
+                             binary(), binary() | undefined) -> error().
+err_undefined_condition(Type, Text, Lang) ->
+    err(Type, 'undefined-condition', 500, Text, Lang).
 
 %% RFC 6120 says error type SHOULD be "wait" or "modify".
 %% RFC 3920 and XEP-0082 says it SHOULD be "wait".