mirror of
https://github.com/processone/ejabberd.git
synced 2024-06-18 22:15:20 +02:00
Ad-hoc commands to join IRC channel, set nickname and encoding (thanks to Magnus Henoch)(EJAB-302)
SVN Revision: 2164
This commit is contained in:
parent
e0370d89b4
commit
83ffe1989a
|
@ -2023,6 +2023,8 @@ to the IRC transport instead of the Multi-User Chat service.
|
||||||
<TT>nickserver!irc.example.org@irc.jabberserver.org</TT>.
|
<TT>nickserver!irc.example.org@irc.jabberserver.org</TT>.
|
||||||
</LI><LI CLASS="li-itemize">Entering your password is possible by sending ‘LOGIN nick password’<BR>
|
</LI><LI CLASS="li-itemize">Entering your password is possible by sending ‘LOGIN nick password’<BR>
|
||||||
to <TT>nickserver!irc.example.org@irc.jabberserver.org</TT>.
|
to <TT>nickserver!irc.example.org@irc.jabberserver.org</TT>.
|
||||||
|
</LI><LI CLASS="li-itemize">The IRC transport provides Ad-Hoc Commands (<A HREF="http://www.xmpp.org/extensions/xep-0050.html">XEP-0050</A>)
|
||||||
|
to join a channel, and to set custom IRC username and encoding.
|
||||||
</LI><LI CLASS="li-itemize">When using a popular Jabber server, it can occur that no
|
</LI><LI CLASS="li-itemize">When using a popular Jabber server, it can occur that no
|
||||||
connection can be achieved with some IRC servers because they limit the
|
connection can be achieved with some IRC servers because they limit the
|
||||||
number of conections from one IP.
|
number of conections from one IP.
|
||||||
|
|
|
@ -2687,6 +2687,8 @@ End user information:
|
||||||
\jid{nickserver!irc.example.org@irc.jabberserver.org}.
|
\jid{nickserver!irc.example.org@irc.jabberserver.org}.
|
||||||
\item Entering your password is possible by sending `LOGIN nick password' \\
|
\item Entering your password is possible by sending `LOGIN nick password' \\
|
||||||
to \jid{nickserver!irc.example.org@irc.jabberserver.org}.
|
to \jid{nickserver!irc.example.org@irc.jabberserver.org}.
|
||||||
|
\item The IRC transport provides Ad-Hoc Commands (\xepref{0050})
|
||||||
|
to join a channel, and to set custom IRC username and encoding.
|
||||||
\item When using a popular \Jabber{} server, it can occur that no
|
\item When using a popular \Jabber{} server, it can occur that no
|
||||||
connection can be achieved with some IRC servers because they limit the
|
connection can be achieved with some IRC servers because they limit the
|
||||||
number of conections from one IP.
|
number of conections from one IP.
|
||||||
|
|
|
@ -59,6 +59,7 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||||
ErlDrvBinary *b;
|
ErlDrvBinary *b;
|
||||||
char *from, *to, *string, *stmp, *rstring, *rtmp;
|
char *from, *to, *string, *stmp, *rstring, *rtmp;
|
||||||
iconv_t cd;
|
iconv_t cd;
|
||||||
|
int invalid_utf8_as_latin1 = 0;
|
||||||
|
|
||||||
ei_decode_version(buf, &index, &i);
|
ei_decode_version(buf, &index, &i);
|
||||||
ei_decode_tuple_header(buf, &index, &i);
|
ei_decode_tuple_header(buf, &index, &i);
|
||||||
|
@ -74,6 +75,15 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||||
stmp = string = malloc(size + 1);
|
stmp = string = malloc(size + 1);
|
||||||
ei_decode_string(buf, &index, string);
|
ei_decode_string(buf, &index, string);
|
||||||
|
|
||||||
|
/* Special mode: parse as UTF-8 if possible; otherwise assume it's
|
||||||
|
Latin-1. Makes no difference when encoding. */
|
||||||
|
if (strcmp(from, "utf-8+latin-1") == 0) {
|
||||||
|
from[5] = '\0';
|
||||||
|
invalid_utf8_as_latin1 = 1;
|
||||||
|
}
|
||||||
|
if (strcmp(to, "utf-8+latin-1") == 0) {
|
||||||
|
to[5] = '\0';
|
||||||
|
}
|
||||||
cd = iconv_open(to, from);
|
cd = iconv_open(to, from);
|
||||||
|
|
||||||
if (cd == (iconv_t) -1) {
|
if (cd == (iconv_t) -1) {
|
||||||
|
@ -95,6 +105,12 @@ static int iconv_erl_control(ErlDrvData drv_data,
|
||||||
rtmp = rstring = malloc(avail);
|
rtmp = rstring = malloc(avail);
|
||||||
while (inleft > 0) {
|
while (inleft > 0) {
|
||||||
if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) {
|
if (iconv(cd, &stmp, &inleft, &rtmp, &outleft) == (size_t) -1) {
|
||||||
|
if (invalid_utf8_as_latin1 && (*stmp & 0x80) && outleft >= 2) {
|
||||||
|
/* Encode one byte of (assumed) Latin-1 into two bytes of UTF-8 */
|
||||||
|
*rtmp++ = 0xc0 | ((*stmp & 0xc0) >> 6);
|
||||||
|
*rtmp++ = 0x80 | (*stmp & 0x3f);
|
||||||
|
outleft -= 2;
|
||||||
|
}
|
||||||
stmp++;
|
stmp++;
|
||||||
inleft--;
|
inleft--;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@
|
||||||
-export([start_link/2,
|
-export([start_link/2,
|
||||||
start/2,
|
start/2,
|
||||||
stop/1,
|
stop/1,
|
||||||
closed_connection/3]).
|
closed_connection/3,
|
||||||
|
get_user_and_encoding/3]).
|
||||||
|
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
||||||
|
@ -42,11 +43,15 @@
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
-include("adhoc.hrl").
|
||||||
|
|
||||||
|
-define(DEFAULT_IRC_ENCODING, "iso8859-1").
|
||||||
|
-define(POSSIBLE_ENCODINGS, ["koi8-r", "iso8859-1", "iso8859-2", "utf-8", "utf-8+latin-1"]).
|
||||||
|
|
||||||
-record(irc_connection, {jid_server_host, pid}).
|
-record(irc_connection, {jid_server_host, pid}).
|
||||||
-record(irc_custom, {us_host, data}).
|
-record(irc_custom, {us_host, data}).
|
||||||
|
|
||||||
-record(state, {host, server_host, default_encoding, access}).
|
-record(state, {host, server_host, access}).
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_mod_irc).
|
-define(PROCNAME, ejabberd_mod_irc).
|
||||||
|
|
||||||
|
@ -98,14 +103,12 @@ init([Host, Opts]) ->
|
||||||
MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
|
MyHost = gen_mod:get_opt_host(Host, Opts, "irc.@HOST@"),
|
||||||
update_table(MyHost),
|
update_table(MyHost),
|
||||||
Access = gen_mod:get_opt(access, Opts, all),
|
Access = gen_mod:get_opt(access, Opts, all),
|
||||||
DefaultEncoding = gen_mod:get_opt(default_encoding, Opts, "koi8-r"),
|
|
||||||
catch ets:new(irc_connection, [named_table,
|
catch ets:new(irc_connection, [named_table,
|
||||||
public,
|
public,
|
||||||
{keypos, #irc_connection.jid_server_host}]),
|
{keypos, #irc_connection.jid_server_host}]),
|
||||||
ejabberd_router:register_route(MyHost),
|
ejabberd_router:register_route(MyHost),
|
||||||
{ok, #state{host = MyHost,
|
{ok, #state{host = MyHost,
|
||||||
server_host = Host,
|
server_host = Host,
|
||||||
default_encoding = DefaultEncoding,
|
|
||||||
access = Access}}.
|
access = Access}}.
|
||||||
|
|
||||||
%%--------------------------------------------------------------------
|
%%--------------------------------------------------------------------
|
||||||
|
@ -138,9 +141,8 @@ handle_cast(_Msg, State) ->
|
||||||
handle_info({route, From, To, Packet},
|
handle_info({route, From, To, Packet},
|
||||||
#state{host = Host,
|
#state{host = Host,
|
||||||
server_host = ServerHost,
|
server_host = ServerHost,
|
||||||
default_encoding = DefEnc,
|
|
||||||
access = Access} = State) ->
|
access = Access} = State) ->
|
||||||
case catch do_route(Host, ServerHost, Access, From, To, Packet, DefEnc) of
|
case catch do_route(Host, ServerHost, Access, From, To, Packet) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
?ERROR_MSG("~p", [Reason]);
|
?ERROR_MSG("~p", [Reason]);
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -188,10 +190,10 @@ stop_supervisor(Host) ->
|
||||||
supervisor:terminate_child(ejabberd_sup, Proc),
|
supervisor:terminate_child(ejabberd_sup, Proc),
|
||||||
supervisor:delete_child(ejabberd_sup, Proc).
|
supervisor:delete_child(ejabberd_sup, Proc).
|
||||||
|
|
||||||
do_route(Host, ServerHost, Access, From, To, Packet, DefEnc) ->
|
do_route(Host, ServerHost, Access, From, To, Packet) ->
|
||||||
case acl:match_rule(ServerHost, Access, From) of
|
case acl:match_rule(ServerHost, Access, From) of
|
||||||
allow ->
|
allow ->
|
||||||
do_route1(Host, ServerHost, From, To, Packet, DefEnc);
|
do_route1(Host, ServerHost, From, To, Packet);
|
||||||
_ ->
|
_ ->
|
||||||
{xmlelement, _Name, Attrs, _Els} = Packet,
|
{xmlelement, _Name, Attrs, _Els} = Packet,
|
||||||
Lang = xml:get_attr_s("xml:lang", Attrs),
|
Lang = xml:get_attr_s("xml:lang", Attrs),
|
||||||
|
@ -201,7 +203,7 @@ do_route(Host, ServerHost, Access, From, To, Packet, DefEnc) ->
|
||||||
ejabberd_router:route(To, From, Err)
|
ejabberd_router:route(To, From, Err)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_route1(Host, ServerHost, From, To, Packet, DefEnc) ->
|
do_route1(Host, ServerHost, From, To, Packet) ->
|
||||||
#jid{user = ChanServ, resource = Resource} = To,
|
#jid{user = ChanServ, resource = Resource} = To,
|
||||||
{xmlelement, _Name, _Attrs, _Els} = Packet,
|
{xmlelement, _Name, _Attrs, _Els} = Packet,
|
||||||
case ChanServ of
|
case ChanServ of
|
||||||
|
@ -210,24 +212,64 @@ do_route1(Host, ServerHost, From, To, Packet, DefEnc) ->
|
||||||
"" ->
|
"" ->
|
||||||
case jlib:iq_query_info(Packet) of
|
case jlib:iq_query_info(Packet) of
|
||||||
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
|
||||||
sub_el = _SubEl, lang = Lang} = IQ ->
|
sub_el = SubEl, lang = Lang} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Node = xml:get_tag_attr_s("node", SubEl),
|
||||||
sub_el = [{xmlelement, "query",
|
case iq_disco(Node, Lang) of
|
||||||
[{"xmlns", XMLNS}],
|
[] ->
|
||||||
iq_disco(Lang)}]},
|
Res = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS}],
|
||||||
|
[]}]},
|
||||||
|
ejabberd_router:route(To,
|
||||||
|
From,
|
||||||
|
jlib:iq_to_xml(Res));
|
||||||
|
DiscoInfo ->
|
||||||
|
Res = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS}],
|
||||||
|
DiscoInfo}]},
|
||||||
|
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 = xml:get_tag_attr_s("node", SubEl),
|
||||||
|
case Node of
|
||||||
|
[] ->
|
||||||
|
ResIQ = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS}],
|
||||||
|
[]}]},
|
||||||
|
Res = jlib:iq_to_xml(ResIQ);
|
||||||
|
"join" ->
|
||||||
|
ResIQ = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS}],
|
||||||
|
[]}]},
|
||||||
|
Res = jlib:iq_to_xml(ResIQ);
|
||||||
|
"register" ->
|
||||||
|
ResIQ = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS}],
|
||||||
|
[]}]},
|
||||||
|
Res = jlib:iq_to_xml(ResIQ);
|
||||||
|
?NS_COMMANDS ->
|
||||||
|
ResIQ = IQ#iq{type = result,
|
||||||
|
sub_el = [{xmlelement, "query",
|
||||||
|
[{"xmlns", XMLNS},
|
||||||
|
{"node", Node}],
|
||||||
|
command_items(Host, Lang)}]},
|
||||||
|
Res = jlib:iq_to_xml(ResIQ);
|
||||||
|
_ ->
|
||||||
|
Res = jlib:make_error_reply(
|
||||||
|
Packet, ?ERR_ITEM_NOT_FOUND)
|
||||||
|
end,
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
From,
|
From,
|
||||||
jlib:iq_to_xml(Res));
|
Res);
|
||||||
#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS} = IQ ->
|
|
||||||
Res = IQ#iq{type = result,
|
|
||||||
sub_el = [{xmlelement, "query",
|
|
||||||
[{"xmlns", XMLNS}],
|
|
||||||
[]}]},
|
|
||||||
ejabberd_router:route(To,
|
|
||||||
From,
|
|
||||||
jlib:iq_to_xml(Res));
|
|
||||||
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
#iq{xmlns = ?NS_REGISTER} = IQ ->
|
||||||
process_register(Host, From, To, DefEnc, IQ);
|
process_register(Host, From, To, IQ);
|
||||||
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
#iq{type = get, xmlns = ?NS_VCARD = XMLNS,
|
||||||
lang = Lang} = IQ ->
|
lang = Lang} = IQ ->
|
||||||
Res = IQ#iq{type = result,
|
Res = IQ#iq{type = result,
|
||||||
|
@ -238,6 +280,34 @@ do_route1(Host, ServerHost, From, To, Packet, DefEnc) ->
|
||||||
ejabberd_router:route(To,
|
ejabberd_router:route(To,
|
||||||
From,
|
From,
|
||||||
jlib:iq_to_xml(Res));
|
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()) 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;
|
||||||
|
_ ->
|
||||||
|
Err = jlib:make_error_reply(
|
||||||
|
Packet, ?ERR_ITEM_NOT_FOUND),
|
||||||
|
ejabberd_router:route(To, From, Err)
|
||||||
|
end;
|
||||||
#iq{} = _IQ ->
|
#iq{} = _IQ ->
|
||||||
Err = jlib:make_error_reply(
|
Err = jlib:make_error_reply(
|
||||||
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
Packet, ?ERR_FEATURE_NOT_IMPLEMENTED),
|
||||||
|
@ -256,10 +326,25 @@ do_route1(Host, ServerHost, From, To, Packet, DefEnc) ->
|
||||||
[] ->
|
[] ->
|
||||||
?DEBUG("open new connection~n", []),
|
?DEBUG("open new connection~n", []),
|
||||||
{Username, Encoding} = get_user_and_encoding(
|
{Username, Encoding} = get_user_and_encoding(
|
||||||
Host, From, Server, DefEnc),
|
Host, From, Server),
|
||||||
|
ConnectionUsername =
|
||||||
|
case Packet of
|
||||||
|
%% If the user tries to join a
|
||||||
|
%% chatroom, the packet for sure
|
||||||
|
%% contains the desired username.
|
||||||
|
{xmlelement, "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,
|
||||||
{ok, Pid} = mod_irc_connection:start(
|
{ok, Pid} = mod_irc_connection:start(
|
||||||
From, Host, ServerHost, Server,
|
From, Host, ServerHost, Server,
|
||||||
Username, Encoding),
|
ConnectionUsername, Encoding),
|
||||||
ets:insert(
|
ets:insert(
|
||||||
irc_connection,
|
irc_connection,
|
||||||
#irc_connection{jid_server_host = {From, Server, Host},
|
#irc_connection{jid_server_host = {From, Server, Host},
|
||||||
|
@ -304,7 +389,7 @@ closed_connection(Host, From, Server) ->
|
||||||
ets:delete(irc_connection, {From, Server, Host}).
|
ets:delete(irc_connection, {From, Server, Host}).
|
||||||
|
|
||||||
|
|
||||||
iq_disco(Lang) ->
|
iq_disco([], Lang) ->
|
||||||
[{xmlelement, "identity",
|
[{xmlelement, "identity",
|
||||||
[{"category", "conference"},
|
[{"category", "conference"},
|
||||||
{"type", "irc"},
|
{"type", "irc"},
|
||||||
|
@ -312,7 +397,22 @@ iq_disco(Lang) ->
|
||||||
{xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
|
{xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []},
|
||||||
{xmlelement, "feature", [{"var", ?NS_MUC}], []},
|
{xmlelement, "feature", [{"var", ?NS_MUC}], []},
|
||||||
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
{xmlelement, "feature", [{"var", ?NS_REGISTER}], []},
|
||||||
{xmlelement, "feature", [{"var", ?NS_VCARD}], []}].
|
{xmlelement, "feature", [{"var", ?NS_VCARD}], []},
|
||||||
|
{xmlelement, "feature", [{"var", ?NS_COMMANDS}], []}];
|
||||||
|
iq_disco(Node, Lang) ->
|
||||||
|
case lists:keysearch(Node, 1, commands()) of
|
||||||
|
{value, {_, Name, _}} ->
|
||||||
|
[{xmlelement, "identity",
|
||||||
|
[{"category", "automation"},
|
||||||
|
{"type", "command-node"},
|
||||||
|
{"name", translate:translate(Lang, Name)}], []},
|
||||||
|
{xmlelement, "feature",
|
||||||
|
[{"var", ?NS_COMMANDS}], []},
|
||||||
|
{xmlelement, "feature",
|
||||||
|
[{"var", ?NS_XDATA}], []}];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
iq_get_vcard(Lang) ->
|
iq_get_vcard(Lang) ->
|
||||||
[{xmlelement, "FN", [],
|
[{xmlelement, "FN", [],
|
||||||
|
@ -323,8 +423,20 @@ iq_get_vcard(Lang) ->
|
||||||
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
|
[{xmlcdata, translate:translate(Lang, "ejabberd IRC module") ++
|
||||||
"\nCopyright (c) 2003-2009 Alexey Shchepin"}]}].
|
"\nCopyright (c) 2003-2009 Alexey Shchepin"}]}].
|
||||||
|
|
||||||
process_register(Host, From, To, DefEnc, #iq{} = IQ) ->
|
command_items(Host, Lang) ->
|
||||||
case catch process_irc_register(Host, From, To, DefEnc, IQ) of
|
lists:map(fun({Node, Name, _Function})
|
||||||
|
-> {xmlelement, "item",
|
||||||
|
[{"jid", Host},
|
||||||
|
{"node", Node},
|
||||||
|
{"name", translate:translate(Lang, Name)}], []}
|
||||||
|
end, commands()).
|
||||||
|
|
||||||
|
commands() ->
|
||||||
|
[{"join", "Join channel", fun adhoc_join/3},
|
||||||
|
{"register", "Configure username and encoding", fun adhoc_register/3}].
|
||||||
|
|
||||||
|
process_register(Host, From, To, #iq{} = IQ) ->
|
||||||
|
case catch process_irc_register(Host, From, To, IQ) of
|
||||||
{'EXIT', Reason} ->
|
{'EXIT', Reason} ->
|
||||||
?ERROR_MSG("~p", [Reason]);
|
?ERROR_MSG("~p", [Reason]);
|
||||||
ResIQ ->
|
ResIQ ->
|
||||||
|
@ -354,7 +466,7 @@ find_xdata_el1([{xmlelement, Name, Attrs, SubEls} | Els]) ->
|
||||||
find_xdata_el1([_ | Els]) ->
|
find_xdata_el1([_ | Els]) ->
|
||||||
find_xdata_el1(Els).
|
find_xdata_el1(Els).
|
||||||
|
|
||||||
process_irc_register(Host, From, _To, DefEnc,
|
process_irc_register(Host, From, _To,
|
||||||
#iq{type = Type, xmlns = XMLNS,
|
#iq{type = Type, xmlns = XMLNS,
|
||||||
lang = Lang, sub_el = SubEl} = IQ) ->
|
lang = Lang, sub_el = SubEl} = IQ) ->
|
||||||
case Type of
|
case Type of
|
||||||
|
@ -400,7 +512,7 @@ process_irc_register(Host, From, _To, DefEnc,
|
||||||
get ->
|
get ->
|
||||||
Node =
|
Node =
|
||||||
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
string:tokens(xml:get_tag_attr_s("node", SubEl), "/"),
|
||||||
case get_form(Host, From, Node, Lang ,DefEnc) of
|
case get_form(Host, From, Node, Lang) of
|
||||||
{result, Res} ->
|
{result, Res} ->
|
||||||
IQ#iq{type = result,
|
IQ#iq{type = result,
|
||||||
sub_el = [{xmlelement, "query",
|
sub_el = [{xmlelement, "query",
|
||||||
|
@ -415,7 +527,7 @@ process_irc_register(Host, From, _To, DefEnc,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
get_form(Host, From, [], Lang, DefEnc) ->
|
get_form(Host, From, [], Lang) ->
|
||||||
#jid{user = User, server = Server,
|
#jid{user = User, server = Server,
|
||||||
luser = LUser, lserver = LServer} = From,
|
luser = LUser, lserver = LServer} = From,
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
|
@ -469,7 +581,7 @@ get_form(Host, From, [], Lang, DefEnc) ->
|
||||||
"for IRC servers, fill this list with values "
|
"for IRC servers, fill this list with values "
|
||||||
"in format '{\"irc server\", \"encoding\"}'. "
|
"in format '{\"irc server\", \"encoding\"}'. "
|
||||||
"By default this service use \"~s\" encoding."),
|
"By default this service use \"~s\" encoding."),
|
||||||
[DefEnc]))}]}]},
|
[?DEFAULT_IRC_ENCODING]))}]}]},
|
||||||
{xmlelement, "field", [{"type", "fixed"}],
|
{xmlelement, "field", [{"type", "fixed"}],
|
||||||
[{xmlelement, "value", [],
|
[{xmlelement, "value", [],
|
||||||
[{xmlcdata,
|
[{xmlcdata,
|
||||||
|
@ -494,7 +606,7 @@ get_form(Host, From, [], Lang, DefEnc) ->
|
||||||
]}]}
|
]}]}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
get_form(_Host, _, _, _Lang, _) ->
|
get_form(_Host, _, _, _Lang) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
|
@ -544,23 +656,244 @@ set_form(_Host, _, _, _Lang, _XData) ->
|
||||||
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
{error, ?ERR_SERVICE_UNAVAILABLE}.
|
||||||
|
|
||||||
|
|
||||||
get_user_and_encoding(Host, From, IRCServer, DefEnc) ->
|
get_user_and_encoding(Host, From, IRCServer) ->
|
||||||
#jid{user = User, server = _Server,
|
#jid{user = User, server = _Server,
|
||||||
luser = LUser, lserver = LServer} = From,
|
luser = LUser, lserver = LServer} = From,
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
{User, DefEnc};
|
{User, ?DEFAULT_IRC_ENCODING};
|
||||||
[] ->
|
[] ->
|
||||||
{User, DefEnc};
|
{User, ?DEFAULT_IRC_ENCODING};
|
||||||
[#irc_custom{data = Data}] ->
|
[#irc_custom{data = Data}] ->
|
||||||
{xml:get_attr_s(username, Data),
|
{xml:get_attr_s(username, Data),
|
||||||
case xml:get_attr_s(IRCServer, xml:get_attr_s(encodings, Data)) of
|
case xml:get_attr_s(IRCServer, xml:get_attr_s(encodings, Data)) of
|
||||||
"" -> DefEnc;
|
"" -> ?DEFAULT_IRC_ENCODING;
|
||||||
E -> E
|
E -> E
|
||||||
end}
|
end}
|
||||||
end.
|
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) ->
|
||||||
|
%% Access control has already been taken care of in do_route.
|
||||||
|
if XData == false ->
|
||||||
|
Form =
|
||||||
|
{xmlelement, "x",
|
||||||
|
[{"xmlns", ?NS_XDATA},
|
||||||
|
{"type", "form"}],
|
||||||
|
[{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "Join IRC channel")}]},
|
||||||
|
{xmlelement, "field",
|
||||||
|
[{"var", "channel"},
|
||||||
|
{"type", "text-single"},
|
||||||
|
{"label", translate:translate(Lang, "Channel to join (without leading #)")}],
|
||||||
|
[{xmlelement, "required", [], []}]},
|
||||||
|
{xmlelement, "field",
|
||||||
|
[{"var", "server"},
|
||||||
|
{"type", "text-single"},
|
||||||
|
{"label", translate:translate(Lang, "Server")}],
|
||||||
|
[{xmlelement, "required", [], []}]}]},
|
||||||
|
adhoc:produce_response(Request,
|
||||||
|
#adhoc_response{status = executing,
|
||||||
|
elements = [Form]});
|
||||||
|
true ->
|
||||||
|
case jlib:parse_xdata_submit(XData) of
|
||||||
|
invalid ->
|
||||||
|
{error, ?ERR_BAD_REQUEST};
|
||||||
|
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 ++ "%" ++ Server ++ "@" ++ To#jid.server,
|
||||||
|
Invite = {xmlelement, "message", [],
|
||||||
|
[{xmlelement, "x",
|
||||||
|
[{"xmlns", ?NS_MUC_USER}],
|
||||||
|
[{xmlelement, "invite",
|
||||||
|
[{"from", jlib:jid_to_string(From)}],
|
||||||
|
[{xmlelement, "reason", [],
|
||||||
|
[{xmlcdata,
|
||||||
|
translate:translate(Lang,
|
||||||
|
"Join the IRC channel here.")}]}]}]},
|
||||||
|
{xmlelement, "x",
|
||||||
|
[{"xmlns", ?NS_XCONFERENCE}],
|
||||||
|
[{xmlcdata, translate:translate(Lang,
|
||||||
|
"Join the IRC channel here.")}]},
|
||||||
|
{xmlelement, "body", [],
|
||||||
|
[{xmlcdata, io_lib:format(
|
||||||
|
translate:translate(Lang,
|
||||||
|
"Find the IRC channel at JID ~s"),
|
||||||
|
[RoomJID])}]}]},
|
||||||
|
ejabberd_router:route(jlib:string_to_jid(RoomJID), From, Invite),
|
||||||
|
adhoc:produce_response(Request, #adhoc_response{status = completed});
|
||||||
|
true ->
|
||||||
|
{error, ?ERR_BAD_REQUEST}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
adhoc_register(_From, _To, #adhoc_request{action = "cancel"} = Request) ->
|
||||||
|
adhoc:produce_response(Request,
|
||||||
|
#adhoc_response{status = canceled});
|
||||||
|
adhoc_register(From, To, #adhoc_request{lang = Lang,
|
||||||
|
node = _Node,
|
||||||
|
xdata = XData,
|
||||||
|
action = Action} = Request) ->
|
||||||
|
#jid{user = User, luser = LUser, lserver = LServer} = From,
|
||||||
|
#jid{lserver = Host} = To,
|
||||||
|
US = {LUser, LServer},
|
||||||
|
%% Generate form for setting username and encodings. If the user
|
||||||
|
%% hasn't begun to fill out the form, generate an initial form
|
||||||
|
%% based on current values.
|
||||||
|
if XData == false ->
|
||||||
|
case catch mnesia:dirty_read({irc_custom, {US, Host}}) of
|
||||||
|
{'EXIT', _Reason} ->
|
||||||
|
Username = User,
|
||||||
|
Encodings = [];
|
||||||
|
[] ->
|
||||||
|
Username = User,
|
||||||
|
Encodings = [];
|
||||||
|
[#irc_custom{data = Data}] ->
|
||||||
|
Username = xml:get_attr_s(username, Data),
|
||||||
|
Encodings = xml:get_attr_s(encodings, Data)
|
||||||
|
end,
|
||||||
|
Error = false;
|
||||||
|
true ->
|
||||||
|
case jlib:parse_xdata_submit(XData) of
|
||||||
|
invalid ->
|
||||||
|
Error = {error, ?ERR_BAD_REQUEST},
|
||||||
|
Username = false,
|
||||||
|
Encodings = false;
|
||||||
|
Fields ->
|
||||||
|
Username = case lists:keysearch("username", 1, Fields) of
|
||||||
|
{value, {"username", U}} ->
|
||||||
|
U;
|
||||||
|
_ ->
|
||||||
|
User
|
||||||
|
end,
|
||||||
|
Encodings = parse_encodings(Fields),
|
||||||
|
Error = false
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
if Error /= false ->
|
||||||
|
Error;
|
||||||
|
Action == "complete" ->
|
||||||
|
case mnesia:transaction(
|
||||||
|
fun () ->
|
||||||
|
mnesia:write(
|
||||||
|
#irc_custom{us_host =
|
||||||
|
{US, Host},
|
||||||
|
data =
|
||||||
|
[{username,
|
||||||
|
Username},
|
||||||
|
{encodings,
|
||||||
|
Encodings}]})
|
||||||
|
end) of
|
||||||
|
{atomic, _} ->
|
||||||
|
adhoc:produce_response(Request, #adhoc_response{status = completed});
|
||||||
|
_ ->
|
||||||
|
{error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
Form = generate_adhoc_register_form(Lang, Username, Encodings),
|
||||||
|
adhoc:produce_response(Request,
|
||||||
|
#adhoc_response{status = executing,
|
||||||
|
elements = [Form],
|
||||||
|
actions = ["next", "complete"]})
|
||||||
|
end.
|
||||||
|
|
||||||
|
generate_adhoc_register_form(Lang, Username, Encodings) ->
|
||||||
|
{xmlelement, "x",
|
||||||
|
[{"xmlns", ?NS_XDATA},
|
||||||
|
{"type", "form"}],
|
||||||
|
[{xmlelement, "title", [], [{xmlcdata, translate:translate(Lang, "IRC settings")}]},
|
||||||
|
{xmlelement, "instructions", [],
|
||||||
|
[{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.")}]},
|
||||||
|
{xmlelement, "field",
|
||||||
|
[{"var", "username"},
|
||||||
|
{"type", "text-single"},
|
||||||
|
{"label", translate:translate(Lang, "IRC username")}],
|
||||||
|
[{xmlelement, "required", [], []},
|
||||||
|
{xmlelement, "value", [], [{xmlcdata, Username}]}]}] ++
|
||||||
|
generate_encoding_fields(Lang, Encodings, 1, [])}.
|
||||||
|
|
||||||
|
generate_encoding_fields(Lang, [], Number, Acc) ->
|
||||||
|
Field = generate_encoding_field(Lang, "", "", Number),
|
||||||
|
lists:reverse(Field ++ Acc);
|
||||||
|
generate_encoding_fields(Lang, [{Server, Encoding} | Encodings], Number, Acc) ->
|
||||||
|
Field = generate_encoding_field(Lang, Server, Encoding, Number),
|
||||||
|
generate_encoding_fields(Lang, Encodings, Number + 1, Field ++ Acc).
|
||||||
|
|
||||||
|
generate_encoding_field(Lang, Server, Encoding, Number) ->
|
||||||
|
EncodingUsed = case Encoding of
|
||||||
|
[] ->
|
||||||
|
?DEFAULT_IRC_ENCODING;
|
||||||
|
_ ->
|
||||||
|
Encoding
|
||||||
|
end,
|
||||||
|
%% Fields are in reverse order, as they will be reversed again later.
|
||||||
|
[{xmlelement, "field",
|
||||||
|
[{"var", "encoding" ++ io_lib:format("~b", [Number])},
|
||||||
|
{"type", "list-single"},
|
||||||
|
{"label", io_lib:format(translate:translate(Lang, "Encoding for server ~b"), [Number])}],
|
||||||
|
[{xmlelement, "value", [], [{xmlcdata, EncodingUsed}]} |
|
||||||
|
lists:map(fun(E) ->
|
||||||
|
{xmlelement, "option", [{"label", E}],
|
||||||
|
[{xmlelement, "value", [], [{xmlcdata, E}]}]}
|
||||||
|
end, ?POSSIBLE_ENCODINGS)]},
|
||||||
|
{xmlelement, "field",
|
||||||
|
[{"var", "server" ++ io_lib:format("~b", [Number])},
|
||||||
|
{"type", "text-single"},
|
||||||
|
{"label", io_lib:format(translate:translate(Lang, "Server ~b"), [Number])}],
|
||||||
|
[{xmlelement, "value", [], [{xmlcdata, Server}]}]}].
|
||||||
|
|
||||||
|
parse_encodings(Fields) ->
|
||||||
|
%% Find all fields staring with serverN and encodingN, for any values
|
||||||
|
%% of N, and generate lists of {"N", Value}.
|
||||||
|
Servers = lists:sort(
|
||||||
|
[{lists:nthtail(6, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
|
||||||
|
lists:prefix("server", Var)]),
|
||||||
|
Encodings = lists:sort(
|
||||||
|
[{lists:nthtail(8, Var), lists:flatten(Value)} || {Var, Value} <- Fields,
|
||||||
|
lists:prefix("encoding", Var)]),
|
||||||
|
|
||||||
|
%% Now sort the lists, and find the corresponding pairs.
|
||||||
|
parse_encodings(Servers, Encodings).
|
||||||
|
|
||||||
|
parse_encodings([{ServerN, Server} | Servers], [{EncodingN, Encoding} | Encodings]) ->
|
||||||
|
%% Try to match pairs of servers and encodings, no matter what fields
|
||||||
|
%% the client might have left out.
|
||||||
|
if ServerN == EncodingN ->
|
||||||
|
[{Server, Encoding} | parse_encodings(Servers, Encodings)];
|
||||||
|
ServerN < EncodingN ->
|
||||||
|
parse_encodings(Servers, [{EncodingN, Encoding} | Encodings]);
|
||||||
|
ServerN > EncodingN ->
|
||||||
|
parse_encodings([{ServerN, Server} | Servers], Encodings)
|
||||||
|
end;
|
||||||
|
parse_encodings([], _) ->
|
||||||
|
[];
|
||||||
|
parse_encodings(_, []) ->
|
||||||
|
[].
|
||||||
|
|
||||||
update_table(Host) ->
|
update_table(Host) ->
|
||||||
Fields = record_info(fields, irc_custom),
|
Fields = record_info(fields, irc_custom),
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
-record(state, {socket, encoding, queue,
|
-record(state, {socket, encoding, queue,
|
||||||
user, host, server, nick,
|
user, host, server, nick,
|
||||||
channels = dict:new(),
|
channels = dict:new(),
|
||||||
|
nickchannel,
|
||||||
inbuf = "", outbuf = ""}).
|
inbuf = "", outbuf = ""}).
|
||||||
|
|
||||||
%-define(DBGFSM, true).
|
%-define(DBGFSM, true).
|
||||||
|
@ -217,15 +218,21 @@ handle_info({route_chan, Channel, Resource,
|
||||||
_ ->
|
_ ->
|
||||||
Resource
|
Resource
|
||||||
end,
|
end,
|
||||||
S1 = ?SEND(io_lib:format("NICK ~s\r\n"
|
S1 = if
|
||||||
"JOIN #~s\r\n",
|
Nick /= StateData#state.nick ->
|
||||||
[Nick, Channel])),
|
S11 = ?SEND(io_lib:format("NICK ~s\r\n", [Nick])),
|
||||||
|
% The server reply will change the copy of the
|
||||||
|
% nick in the state (or indicate a clash).
|
||||||
|
S11#state{nickchannel = Channel};
|
||||||
|
true ->
|
||||||
|
StateData
|
||||||
|
end,
|
||||||
case dict:is_key(Channel, S1#state.channels) of
|
case dict:is_key(Channel, S1#state.channels) of
|
||||||
true ->
|
true ->
|
||||||
S1#state{nick = Nick};
|
S1;
|
||||||
_ ->
|
_ ->
|
||||||
S1#state{nick = Nick,
|
S2 = ?SEND(io_lib:format("JOIN #~s\r\n", [Channel])),
|
||||||
channels =
|
S2#state{channels =
|
||||||
dict:store(Channel, ?SETS:new(),
|
dict:store(Channel, ?SETS:new(),
|
||||||
S1#state.channels)}
|
S1#state.channels)}
|
||||||
end
|
end
|
||||||
|
@ -471,6 +478,35 @@ handle_info({ircstring, [$P, $I, $N, $G, $ | ID]}, StateName, StateData) ->
|
||||||
send_text(StateData, "PONG " ++ ID ++ "\r\n"),
|
send_text(StateData, "PONG " ++ ID ++ "\r\n"),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
|
|
||||||
|
handle_info({ircstring, [$: | String]}, wait_for_registration, StateData) ->
|
||||||
|
Words = string:tokens(String, " "),
|
||||||
|
{NewState, NewStateData} =
|
||||||
|
case Words of
|
||||||
|
[_, "001" | _] ->
|
||||||
|
{stream_established, StateData};
|
||||||
|
[_, "433" | _] ->
|
||||||
|
{error,
|
||||||
|
{error, error_nick_in_use(StateData, String), StateData}};
|
||||||
|
[_, [$4, _, _] | _] ->
|
||||||
|
{error,
|
||||||
|
{error, error_unknown_num(StateData, String, "cancel"),
|
||||||
|
StateData}};
|
||||||
|
[_, [$5, _, _] | _] ->
|
||||||
|
{error,
|
||||||
|
{error, error_unknown_num(StateData, String, "cancel"),
|
||||||
|
StateData}};
|
||||||
|
_ ->
|
||||||
|
?DEBUG("unknown irc command '~s'~n", [String]),
|
||||||
|
{wait_for_registration, StateData}
|
||||||
|
end,
|
||||||
|
% Note that we don't send any data at this stage.
|
||||||
|
if
|
||||||
|
NewState == error ->
|
||||||
|
{stop, normal, NewStateData};
|
||||||
|
true ->
|
||||||
|
{next_state, NewState, NewStateData}
|
||||||
|
end;
|
||||||
|
|
||||||
handle_info({ircstring, [$: | String]}, _StateName, StateData) ->
|
handle_info({ircstring, [$: | String]}, _StateName, StateData) ->
|
||||||
Words = string:tokens(String, " "),
|
Words = string:tokens(String, " "),
|
||||||
NewStateData =
|
NewStateData =
|
||||||
|
@ -495,6 +531,15 @@ handle_info({ircstring, [$: | String]}, _StateName, StateData) ->
|
||||||
[_, "319", _, Nick | _ ] ->
|
[_, "319", _, Nick | _ ] ->
|
||||||
process_whois319(StateData, String, Nick),
|
process_whois319(StateData, String, Nick),
|
||||||
StateData;
|
StateData;
|
||||||
|
[_, "433" | _] ->
|
||||||
|
process_nick_in_use(StateData, String);
|
||||||
|
% CODEPAGE isn't standard, so don't complain if it's not there.
|
||||||
|
[_, "421", _, "CODEPAGE" | _] ->
|
||||||
|
StateData;
|
||||||
|
[_, [$4, _, _] | _] ->
|
||||||
|
process_num_error(StateData, String);
|
||||||
|
[_, [$5, _, _] | _] ->
|
||||||
|
process_num_error(StateData, String);
|
||||||
[From, "PRIVMSG", [$# | Chan] | _] ->
|
[From, "PRIVMSG", [$# | Chan] | _] ->
|
||||||
process_chanprivmsg(StateData, Chan, From, String),
|
process_chanprivmsg(StateData, Chan, From, String),
|
||||||
StateData;
|
StateData;
|
||||||
|
@ -581,7 +626,16 @@ handle_info({tcp_error, _Socket, _Reason}, StateName, StateData) ->
|
||||||
%% Purpose: Shutdown the fsm
|
%% Purpose: Shutdown the fsm
|
||||||
%% Returns: any
|
%% Returns: any
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
terminate(_Reason, _StateName, StateData) ->
|
terminate(_Reason, _StateName, FullStateData) ->
|
||||||
|
% Extract error message if there was one.
|
||||||
|
{Error, StateData} = case FullStateData of
|
||||||
|
{error, SError, SStateData} ->
|
||||||
|
{SError, SStateData};
|
||||||
|
_ ->
|
||||||
|
{{xmlelement, "error", [{"code", "502"}],
|
||||||
|
[{xmlcdata, "Server Connect Failed"}]},
|
||||||
|
FullStateData}
|
||||||
|
end,
|
||||||
mod_irc:closed_connection(StateData#state.host,
|
mod_irc:closed_connection(StateData#state.host,
|
||||||
StateData#state.user,
|
StateData#state.user,
|
||||||
StateData#state.server),
|
StateData#state.server),
|
||||||
|
@ -594,8 +648,7 @@ terminate(_Reason, _StateName, StateData) ->
|
||||||
StateData#state.host, StateData#state.nick),
|
StateData#state.host, StateData#state.nick),
|
||||||
StateData#state.user,
|
StateData#state.user,
|
||||||
{xmlelement, "presence", [{"type", "error"}],
|
{xmlelement, "presence", [{"type", "error"}],
|
||||||
[{xmlelement, "error", [{"code", "502"}],
|
[Error]})
|
||||||
[{xmlcdata, "Server Connect Failed"}]}]})
|
|
||||||
end, dict:fetch_keys(StateData#state.channels)),
|
end, dict:fetch_keys(StateData#state.channels)),
|
||||||
case StateData#state.socket of
|
case StateData#state.socket of
|
||||||
undefined ->
|
undefined ->
|
||||||
|
@ -738,7 +791,6 @@ process_channel_topic_who(StateData, Chan, String) ->
|
||||||
String
|
String
|
||||||
end,
|
end,
|
||||||
Msg2 = filter_message(Msg1),
|
Msg2 = filter_message(Msg1),
|
||||||
|
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
jlib:make_jid(lists:concat([Chan, "%", StateData#state.server]),
|
||||||
StateData#state.host, ""),
|
StateData#state.host, ""),
|
||||||
|
@ -747,6 +799,45 @@ process_channel_topic_who(StateData, Chan, String) ->
|
||||||
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
[{xmlelement, "body", [], [{xmlcdata, Msg2}]}]}).
|
||||||
|
|
||||||
|
|
||||||
|
error_nick_in_use(_StateData, String) ->
|
||||||
|
{ok, Msg, _} = regexp:sub(String, ".*433 +[^ ]* +", ""),
|
||||||
|
Msg1 = filter_message(Msg),
|
||||||
|
{xmlelement, "error", [{"code", "409"}, {"type", "cancel"}],
|
||||||
|
[{xmlelement, "conflict", [{"xmlns", ?NS_STANZAS}], []},
|
||||||
|
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
||||||
|
[{xmlcdata, Msg1}]}]}.
|
||||||
|
|
||||||
|
process_nick_in_use(StateData, String) ->
|
||||||
|
% We can't use the jlib macro because we don't know the language of the
|
||||||
|
% message.
|
||||||
|
Error = error_nick_in_use(StateData, String),
|
||||||
|
case StateData#state.nickchannel of
|
||||||
|
undefined ->
|
||||||
|
% Shouldn't happen with a well behaved server
|
||||||
|
StateData;
|
||||||
|
Chan ->
|
||||||
|
ejabberd_router:route(
|
||||||
|
jlib:make_jid(
|
||||||
|
lists:concat([Chan, "%", StateData#state.server]),
|
||||||
|
StateData#state.host, StateData#state.nick),
|
||||||
|
StateData#state.user,
|
||||||
|
{xmlelement, "presence", [{"type", "error"}], [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(
|
||||||
|
jlib:make_jid(
|
||||||
|
lists:concat([Chan, "%", StateData#state.server]),
|
||||||
|
StateData#state.host, StateData#state.nick),
|
||||||
|
StateData#state.user,
|
||||||
|
{xmlelement, "message", [{"type", "error"}],
|
||||||
|
[Error]})
|
||||||
|
end, dict:fetch_keys(StateData#state.channels)),
|
||||||
|
StateData.
|
||||||
|
|
||||||
process_endofwhois(StateData, _String, Nick) ->
|
process_endofwhois(StateData, _String, Nick) ->
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
|
@ -876,7 +967,7 @@ process_version(StateData, _Nick, From) ->
|
||||||
"\001\r\n",
|
"\001\r\n",
|
||||||
[FromUser, ?VERSION]) ++
|
[FromUser, ?VERSION]) ++
|
||||||
io_lib:format("NOTICE ~s :\001VERSION "
|
io_lib:format("NOTICE ~s :\001VERSION "
|
||||||
?EJABBERD_URI
|
"http://ejabberd.jabberstudio.org/"
|
||||||
"\001\r\n",
|
"\001\r\n",
|
||||||
[FromUser])).
|
[FromUser])).
|
||||||
|
|
||||||
|
@ -1069,7 +1160,14 @@ process_nick(StateData, From, NewNick) ->
|
||||||
Ps
|
Ps
|
||||||
end
|
end
|
||||||
end, StateData#state.channels),
|
end, StateData#state.channels),
|
||||||
StateData#state{channels = NewChans}.
|
if
|
||||||
|
FromUser == StateData#state.nick ->
|
||||||
|
StateData#state{nick = Nick,
|
||||||
|
nickchannel = undefined,
|
||||||
|
channels = NewChans};
|
||||||
|
true ->
|
||||||
|
StateData#state{channels = NewChans}
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
process_error(StateData, String) ->
|
process_error(StateData, String) ->
|
||||||
|
@ -1085,6 +1183,13 @@ process_error(StateData, String) ->
|
||||||
[{xmlcdata, String}]}]})
|
[{xmlcdata, String}]}]})
|
||||||
end, dict:fetch_keys(StateData#state.channels)).
|
end, dict:fetch_keys(StateData#state.channels)).
|
||||||
|
|
||||||
|
error_unknown_num(_StateData, String, Type) ->
|
||||||
|
{ok, Msg, _} = regexp:sub(String, ".*[45][0-9][0-9] +[^ ]* +", ""),
|
||||||
|
Msg1 = filter_message(Msg),
|
||||||
|
{xmlelement, "error", [{"code", "500"}, {"type", Type}],
|
||||||
|
[{xmlelement, "undefined-condition", [{"xmlns", ?NS_STANZAS}], []},
|
||||||
|
{xmlelement, "text", [{"xmlns", ?NS_STANZAS}],
|
||||||
|
[{xmlcdata, Msg1}]}]}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user