summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-08-03 20:57:05 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2016-08-03 20:57:05 +0300
commita417fbf45b88bae2163397cc9e06a7bc22b20f7a (patch)
tree2049e6f5b8503e30208ae392527ef2a7b935275b
parentRewrite mod_configure to use XML generator (diff)
Rewrite mod_irc to use XML generator
-rw-r--r--src/mod_irc.erl1227
-rw-r--r--src/mod_irc_connection.erl1368
-rw-r--r--src/mod_irc_mnesia.erl2
-rw-r--r--src/mod_irc_riak.erl2
-rw-r--r--src/mod_irc_sql.erl2
-rw-r--r--src/xmpp.erl20
6 files changed, 974 insertions, 1647 deletions
diff --git a/src/mod_irc.erl b/src/mod_irc.erl
index 2206028b..91f43716 100644
--- a/src/mod_irc.erl
+++ b/src/mod_irc.erl
@@ -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,
@@ -42,11 +43,7 @@
-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.
diff --git a/src/mod_irc_connection.erl b/src/mod_irc_connection.erl
index 098c8c28..fb301330 100644
--- a/src/mod_irc_connection.erl
+++ b/src/mod_irc_connection.erl
@@ -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(
diff --git a/src/mod_irc_mnesia.erl b/src/mod_irc_mnesia.erl
index 9f8117ad..95cceb54 100644
--- a/src/mod_irc_mnesia.erl
+++ b/src/mod_irc_mnesia.erl
@@ -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").
diff --git a/src/mod_irc_riak.erl b/src/mod_irc_riak.erl
index 6ac7befd..a71859c5 100644
--- a/src/mod_irc_riak.erl
+++ b/src/mod_irc_riak.erl
@@ -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").
%%%===================================================================
diff --git a/src/mod_irc_sql.erl b/src/mod_irc_sql.erl
index 8aa428e5..7905db91 100644
--- a/src/mod_irc_sql.erl
+++ b/src/mod_irc_sql.erl
@@ -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").
diff --git a/src/xmpp.erl b/src/xmpp.erl
index a927a6a9..b4324315 100644
--- a/src/xmpp.erl
+++ b/src/xmpp.erl
@@ -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".