diff options
Diffstat (limited to 'src/mod_irc')
-rw-r--r-- | src/mod_irc/Makefile.in | 60 | ||||
-rw-r--r-- | src/mod_irc/Makefile.win32 | 42 | ||||
-rw-r--r-- | src/mod_irc/iconv.erl | 82 | ||||
-rw-r--r-- | src/mod_irc/iconv_erl.c | 177 | ||||
-rw-r--r-- | src/mod_irc/mod_irc.erl | 1289 | ||||
-rw-r--r-- | src/mod_irc/mod_irc_connection.erl | 1581 |
6 files changed, 0 insertions, 3231 deletions
diff --git a/src/mod_irc/Makefile.in b/src/mod_irc/Makefile.in deleted file mode 100644 index 9dcf9f182..000000000 --- a/src/mod_irc/Makefile.in +++ /dev/null @@ -1,60 +0,0 @@ -# $Id$ - -CC = @CC@ -CFLAGS = @CFLAGS@ -CPPFLAGS = @CPPFLAGS@ -LDFLAGS = @LDFLAGS@ -LIBS = @LIBS@ @LIBICONV@ - -ERLANG_CFLAGS = @ERLANG_CFLAGS@ -ERLANG_LIBS = @ERLANG_LIBS@ - -# Assume Linux-style dynamic library flags -DYNAMIC_LIB_CFLAGS = -fpic -shared -ifeq ($(shell uname),Darwin) - DYNAMIC_LIB_CFLAGS = -fPIC -bundle -flat_namespace -undefined suppress -endif -ifeq ($(shell uname),SunOs) - DYNAMIC_LIB_CFLAGS = -KPIC -G -z text -endif - - -EFLAGS += -I .. -EFLAGS += -pz .. - -# make debug=true to compile Erlang module with debug informations. -ifdef debug - EFLAGS+=+debug_info -endif - -ERLSHLIBS = ../iconv_erl.so -OUTDIR = .. -SOURCES = $(wildcard *.erl) -BEAMS = $(addprefix $(OUTDIR)/,$(SOURCES:.erl=.beam)) - -all: $(BEAMS) $(ERLSHLIBS) - -$(OUTDIR)/%.beam: %.erl - @ERLC@ -W $(EFLAGS) -o $(OUTDIR) $< - -#all: $(ERLSHLIBS) -# erl -s make all report "{outdir, \"..\"}" -noinput -s erlang halt - -$(ERLSHLIBS): ../%.so: %.c - $(CC) $(INCLUDES) $(CFLAGS) $(LDFLAGS) \ - $(subst ../,,$(subst .so,.c,$@)) \ - $(LIBS) \ - $(ERLANG_CFLAGS) \ - $(ERLANG_LIBS) \ - -o $@ \ - $(DYNAMIC_LIB_CFLAGS) - -clean: - rm -f $(BEAMS) $(ERLSHLIBS) - -distclean: clean - rm -f Makefile - -TAGS: - etags *.erl - diff --git a/src/mod_irc/Makefile.win32 b/src/mod_irc/Makefile.win32 deleted file mode 100644 index fb0671104..000000000 --- a/src/mod_irc/Makefile.win32 +++ /dev/null @@ -1,42 +0,0 @@ - -include ..\Makefile.inc - -EFLAGS = -I .. -pz .. - -OUTDIR = .. -BEAMS = ..\iconv.beam ..\mod_irc.beam ..\mod_irc_connection.beam - -SOURCE = iconv_erl.c -OBJECT = iconv_erl.o -DLL = $(OUTDIR)\iconv_erl.dll - -ALL : $(DLL) $(BEAMS) - -CLEAN : - -@erase $(DLL) - -@erase $(OUTDIR)\iconv_erl.exp - -@erase $(OUTDIR)\iconv_erl.lib - -@erase $(OBJECT) - -@erase $(BEAMS) - -$(OUTDIR)\iconv.beam : iconv.erl - erlc -W $(EFLAGS) -o $(OUTDIR) iconv.erl - -$(OUTDIR)\mod_irc.beam : mod_irc.erl - erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc.erl - -$(OUTDIR)\mod_irc_connection.beam : mod_irc_connection.erl - erlc -W $(EFLAGS) -o $(OUTDIR) mod_irc_connection.erl - -CC=cl.exe -CC_FLAGS=-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -MD -Ox -I"$(ERLANG_DIR)\usr\include" -I"$(EI_DIR)\include" -I"$(ICONV_DIR)\include" - -LD=link.exe -LD_FLAGS=-release -nologo -incremental:no -dll "$(EI_DIR)\lib\ei_md.lib" "$(EI_DIR)\lib\erl_interface_md.lib" "$(ICONV_LIB)" MSVCRT.LIB kernel32.lib advapi32.lib gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib - -$(DLL) : $(OBJECT) - $(LD) $(LD_FLAGS) -out:$@ $< - -$(OBJECT) : $(SOURCE) - $(CC) $(CC_FLAGS) -c -Fo$@ $< - diff --git a/src/mod_irc/iconv.erl b/src/mod_irc/iconv.erl deleted file mode 100644 index 4d8180539..000000000 --- a/src/mod_irc/iconv.erl +++ /dev/null @@ -1,82 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : iconv.erl -%%% Author : Alexey Shchepin <alexey@process-one.net> -%%% Purpose : Interface to libiconv -%%% Created : 16 Feb 2003 by Alexey Shchepin <alexey@process-one.net> -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2013 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(iconv). - --author('alexey@process-one.net'). - --behaviour(gen_server). - --export([start/0, start_link/0, convert/3]). - -%% Internal exports, call-back functions. --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, code_change/3, terminate/2]). - -start() -> - gen_server:start({local, ?MODULE}, ?MODULE, [], []). - -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], - []). - -init([]) -> - case erl_ddll:load_driver(ejabberd:get_so_path(), - iconv_erl) - of - ok -> ok; - {error, already_loaded} -> ok - end, - Port = open_port({spawn, "iconv_erl"}, []), - ets:new(iconv_table, [set, public, named_table]), - ets:insert(iconv_table, {port, Port}), - {ok, Port}. - -%%% -------------------------------------------------------- -%%% The call-back functions. -%%% -------------------------------------------------------- - -handle_call(_, _, State) -> {noreply, State}. - -handle_cast(_, State) -> {noreply, State}. - -handle_info({'EXIT', Port, Reason}, Port) -> - {stop, {port_died, Reason}, Port}; -handle_info({'EXIT', _Pid, _Reason}, Port) -> - {noreply, Port}; -handle_info(_, State) -> {noreply, State}. - -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -terminate(_Reason, Port) -> Port ! {self, close}, ok. - --spec convert(binary(), binary(), binary()) -> binary(). - -convert(From, To, String) -> - [{port, Port} | _] = ets:lookup(iconv_table, port), - Bin = term_to_binary({From, To, String}), - BRes = port_control(Port, 1, Bin), - (BRes). diff --git a/src/mod_irc/iconv_erl.c b/src/mod_irc/iconv_erl.c deleted file mode 100644 index 20d9389a8..000000000 --- a/src/mod_irc/iconv_erl.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * ejabberd, Copyright (C) 2002-2013 ProcessOne - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307 USA - * - */ - -#include <stdio.h> -#include <string.h> -#include <erl_driver.h> -#include <ei.h> -#include <iconv.h> - -/* - * R15B changed several driver callbacks to use ErlDrvSizeT and - * ErlDrvSSizeT typedefs instead of int. - * This provides missing typedefs on older OTP versions. - */ -#if ERL_DRV_EXTENDED_MAJOR_VERSION < 2 -typedef int ErlDrvSizeT; -typedef int ErlDrvSSizeT; -#endif - -typedef struct { - ErlDrvPort port; - iconv_t cd; -} iconv_data; - - -static ErlDrvData iconv_erl_start(ErlDrvPort port, char *buff) -{ - iconv_data* d = (iconv_data*)driver_alloc(sizeof(iconv_data)); - d->port = port; - d->cd = NULL; - - set_port_control_flags(port, PORT_CONTROL_FLAG_BINARY); - - return (ErlDrvData)d; -} - -static void iconv_erl_stop(ErlDrvData handle) -{ - driver_free((char*)handle); -} - -static ErlDrvSSizeT iconv_erl_control(ErlDrvData drv_data, - unsigned int command, - char *buf, ErlDrvSizeT len, - char **rbuf, ErlDrvSizeT rlen) -{ - int i; - int size; - int index = 0; - int avail; - size_t inleft, outleft; - ErlDrvBinary *b; - char *from, *to, *string, *stmp, *rstring, *rtmp; - iconv_t cd; - int invalid_utf8_as_latin1 = 0; - - ei_decode_version(buf, &index, &i); - ei_decode_tuple_header(buf, &index, &i); - ei_get_type(buf, &index, &i, &size); - from = driver_alloc(size + 1); - ei_decode_string(buf, &index, from); - - ei_get_type(buf, &index, &i, &size); - to = driver_alloc(size + 1); - ei_decode_string(buf, &index, to); - - ei_get_type(buf, &index, &i, &size); - stmp = string = driver_alloc(size + 1); - 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); - - if (cd == (iconv_t) -1) { - cd = iconv_open("ascii", "ascii"); - if (cd == (iconv_t) -1) { - *rbuf = (char*)(b = driver_alloc_binary(size)); - memcpy(b->orig_bytes, string, size); - - driver_free(from); - driver_free(to); - driver_free(string); - - return size; - } - } - - outleft = avail = 4*size; - inleft = size; - rtmp = rstring = driver_alloc(avail); - while (inleft > 0) { - 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++; - inleft--; - } - } - - size = rtmp - rstring; - - *rbuf = (char*)(b = driver_alloc_binary(size)); - memcpy(b->orig_bytes, rstring, size); - - driver_free(from); - driver_free(to); - driver_free(string); - driver_free(rstring); - iconv_close(cd); - - return size; -} - - - -ErlDrvEntry iconv_driver_entry = { - NULL, /* F_PTR init, N/A */ - iconv_erl_start, /* L_PTR start, called when port is opened */ - iconv_erl_stop, /* F_PTR stop, called when port is closed */ - NULL, /* F_PTR output, called when erlang has sent */ - NULL, /* F_PTR ready_input, called when input descriptor ready */ - NULL, /* F_PTR ready_output, called when output descriptor ready */ - "iconv_erl", /* char *driver_name, the argument to open_port */ - NULL, /* F_PTR finish, called when unloaded */ - NULL, /* handle */ - iconv_erl_control, /* F_PTR control, port_command callback */ - NULL, /* F_PTR timeout, reserved */ - NULL, /* F_PTR outputv, reserved */ - /* Added in Erlang/OTP R15B: */ - NULL, /* ready_async */ - NULL, /* flush */ - NULL, /* call */ - NULL, /* event */ - ERL_DRV_EXTENDED_MARKER, /* extended_marker */ - ERL_DRV_EXTENDED_MAJOR_VERSION, /* major_version */ - ERL_DRV_EXTENDED_MINOR_VERSION, /* minor_version */ - 0, /* driver_flags */ - NULL, /* handle2 */ - NULL, /* process_exit */ - NULL /* stop_select */ -}; - -DRIVER_INIT(iconv_erl) /* must match name in driver_entry */ -{ - return &iconv_driver_entry; -} - - diff --git a/src/mod_irc/mod_irc.erl b/src/mod_irc/mod_irc.erl deleted file mode 100644 index 53069671d..000000000 --- a/src/mod_irc/mod_irc.erl +++ /dev/null @@ -1,1289 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc.erl -%%% Author : Alexey Shchepin <alexey@process-one.net> -%%% Purpose : IRC transport -%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net> -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2013 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc). - --author('alexey@process-one.net'). - --behaviour(gen_server). - --behaviour(gen_mod). - -%% API --export([start_link/2, start/2, stop/1, export/1, - closed_connection/3, get_connection_params/3]). - -%% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3]). - --include("ejabberd.hrl"). - --include("jlib.hrl"). - --include("adhoc.hrl"). - --define(DEFAULT_IRC_ENCODING, <<"iso8859-1">>). - --define(DEFAULT_IRC_PORT, 6667). - --define(POSSIBLE_ENCODINGS, - [<<"koi8-r">>, <<"iso8859-1">>, <<"iso8859-2">>, - <<"utf-8">>, <<"utf-8+latin-1">>]). - --type conn_param() :: {binary(), binary(), inet:port_number(), binary()} | - {binary(), binary(), inet:port_number()} | - {binary(), binary()}. - --record(irc_connection, - {jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()}, - pid = self() :: pid()}). - --record(irc_custom, - {us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, - binary()}, - data = [] :: [{username, binary()} | - {connections_params, [conn_param()]}]}). - --record(state, {host = <<"">> :: binary(), - server_host = <<"">> :: binary(), - access = all :: atom()}). - --define(PROCNAME, ejabberd_mod_irc). - -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- -start_link(Host, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, - [Host, Opts], []). - -start(Host, Opts) -> - start_supervisor(Host), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, - temporary, 1000, worker, [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec). - -stop(Host) -> - stop_supervisor(Host), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:call(Proc, stop), - supervisor:delete_child(ejabberd_sup, Proc). - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -init([Host, Opts]) -> - iconv:start(), - MyHost = gen_mod:get_opt_host(Host, Opts, - <<"irc.@HOST@">>), - case gen_mod:db_type(Opts) of - mnesia -> - mnesia:create_table(irc_custom, - [{disc_copies, [node()]}, - {attributes, record_info(fields, irc_custom)}]), - update_table(); - _ -> ok - end, - Access = gen_mod:get_opt(access, Opts, - fun(A) when is_atom(A) -> A end, - all), - catch ets:new(irc_connection, - [named_table, public, - {keypos, #irc_connection.jid_server_host}]), - ejabberd_router:register_route(MyHost), - {ok, - #state{host = MyHost, server_host = Host, - access = Access}}. - -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call(stop, _From, State) -> - {stop, normal, ok, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- -handle_info({route, From, To, Packet}, - #state{host = Host, server_host = ServerHost, - access = Access} = - State) -> - case catch do_route(Host, ServerHost, Access, From, To, - Packet) - of - {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); - _ -> ok - end, - {noreply, State}; -handle_info(_Info, State) -> {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% 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. - -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> {ok, State}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- -start_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, - ejabberd_mod_irc_sup), - ChildSpec = {Proc, - {ejabberd_tmp_sup, start_link, - [Proc, mod_irc_connection]}, - permanent, infinity, supervisor, [ejabberd_tmp_sup]}, - supervisor:start_child(ejabberd_sup, ChildSpec). - -stop_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, - ejabberd_mod_irc_sup), - supervisor:terminate_child(ejabberd_sup, Proc), - supervisor:delete_child(ejabberd_sup, Proc). - -do_route(Host, ServerHost, Access, From, To, Packet) -> - case acl:match_rule(ServerHost, Access, From) of - allow -> do_route1(Host, ServerHost, From, To, Packet); - _ -> - #xmlel{attrs = Attrs} = Packet, - Lang = xml: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) - end. - -do_route1(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 = xml: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 = xml: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); - _ -> - Res = jlib:make_error_reply(Packet, - ?ERR_ITEM_NOT_FOUND) - 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; - _ -> - Err = jlib:make_error_reply(Packet, - ?ERR_ITEM_NOT_FOUND), - 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 - %% If the user tries to join a - %% chatroom, the packet for sure - %% contains the desired username. - #xmlel{name = <<"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(From, Host, - ServerHost, Server, - ConnectionUsername, - Encoding, Port, - Password, ?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; - _ -> - case str:tokens(ChanServ, <<"!">>) of - [<<_, _/binary>> = Nick, <<_, _/binary>> = Server] -> - case ets:lookup(irc_connection, {From, Server, Host}) of - [] -> - Err = jlib:make_error_reply(Packet, - ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err); - [R] -> - Pid = R#irc_connection.pid, - ?DEBUG("send to process ~p~n", [Pid]), - mod_irc_connection:route_nick(Pid, Nick, Packet), - ok - end; - _ -> - Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST), - ejabberd_router:route(To, From, Err) - end - 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, 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 = []}]; - _ -> [] - 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-2013 ProcessOne">>}]}]. - -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)). - -commands(ServerHost) -> - [{<<"join">>, <<"Join channel">>, fun adhoc_join/3}, - {<<"register">>, - <<"Configure username, encoding, port and " - "password">>, - fun (From, To, Request) -> - 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 xml: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 -> - IQ#iq{type = error, - sub_el = [SubEl, ?ERR_NOT_ACCEPTABLE]}; - #xmlel{attrs = Attrs} -> - case xml: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 -> - IQ#iq{type = error, - sub_el = [SubEl, ?ERR_BAD_REQUEST]}; - _ -> - Node = str:tokens(xml: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; - _ -> - IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]} - end - end; - get -> - Node = str:tokens(xml: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 = jlib:nameprep(ServerHost), - get_data(LServer, Host, From, - gen_mod:db_type(LServer, ?MODULE)). - -get_data(_LServer, Host, From, mnesia) -> - #jid{luser = LUser, lserver = LServer} = From, - US = {LUser, LServer}, - case catch mnesia:dirty_read({irc_custom, {US, Host}}) - of - {'EXIT', _Reason} -> error; - [] -> empty; - [#irc_custom{data = Data}] -> Data - end; -get_data(LServer, Host, From, odbc) -> - SJID = - ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(jlib:jid_remove_resource(From)))), - SHost = ejabberd_odbc:escape(Host), - case catch ejabberd_odbc:sql_query(LServer, - [<<"select data from irc_custom where jid='">>, - SJID, <<"' and host='">>, SHost, - <<"';">>]) - of - {selected, [<<"data">>], [[SData]]} -> - data_to_binary(ejabberd_odbc:decode_term(SData)); - {'EXIT', _} -> error; - {selected, _, _} -> empty - end. - -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, ?ERR_INTERNAL_SERVER_ERROR}; - 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}. - -set_data(ServerHost, Host, From, Data) -> - LServer = jlib:nameprep(ServerHost), - set_data(LServer, Host, From, data_to_binary(Data), - gen_mod:db_type(LServer, ?MODULE)). - -set_data(_LServer, Host, From, Data, mnesia) -> - {LUser, LServer, _} = jlib:jid_tolower(From), - US = {LUser, LServer}, - F = fun () -> - mnesia:write(#irc_custom{us_host = {US, Host}, - data = Data}) - end, - mnesia:transaction(F); -set_data(LServer, Host, From, Data, odbc) -> - SJID = - ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(jlib:jid_remove_resource(From)))), - SHost = ejabberd_odbc:escape(Host), - SData = ejabberd_odbc:encode_term(Data), - F = fun () -> - odbc_queries:update_t(<<"irc_custom">>, - [<<"jid">>, <<"host">>, <<"data">>], - [SJID, SHost, SData], - [<<"jid='">>, SJID, <<"' and host='">>, - SHost, <<"'">>]), - ok - end, - ejabberd_odbc:sql_transaction(LServer, F). - -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, ?ERR_NOT_ACCEPTABLE} - end; - _ -> {error, ?ERR_NOT_ACCEPTABLE} - end; - _ -> {error, ?ERR_NOT_ACCEPTABLE} - end; - _ -> {error, ?ERR_NOT_ACCEPTABLE} - end; -set_form(_ServerHost, _Host, _, _, _Lang, _XData) -> - {error, ?ERR_SERVICE_UNAVAILABLE}. - -%% Host = "irc.example.com" -%% ServerHost = "example.com" -get_connection_params(Host, From, IRCServer) -> - [_ | HostTail] = str:tokens(Host, <<".">>), - ServerHost = str:join(HostTail, <<".">>), - get_connection_params(Host, ServerHost, From, - IRCServer). - -get_default_encoding(ServerHost) -> - Result = gen_mod:get_module_opt(ServerHost, ?MODULE, default_encoding, - fun iolist_to_binary/1, - ?DEFAULT_IRC_ENCODING), - ?INFO_MSG("The default_encoding configured for " - "host ~p is: ~p~n", - [ServerHost, Result]), - Result. - -get_connection_params(Host, ServerHost, From, - IRCServer) -> - #jid{user = User, server = _Server} = From, - DefaultEncoding = get_default_encoding(ServerHost), - case get_data(ServerHost, Host, From) of - error -> - {User, DefaultEncoding, ?DEFAULT_IRC_PORT, <<"">>}; - empty -> - {User, DefaultEncoding, ?DEFAULT_IRC_PORT, <<"">>}; - Data -> - {Username, ConnParams} = get_username_and_connection_params(Data), - {NewUsername, NewEncoding, NewPort, NewPassword} = case - lists:keysearch(IRCServer, - 1, - ConnParams) - of - {value, - {_, Encoding, - Port, - Password}} -> - {Username, - Encoding, - Port, - Password}; - {value, - {_, Encoding, - Port}} -> - {Username, - Encoding, - Port, - <<"">>}; - {value, - {_, - Encoding}} -> - {Username, - Encoding, - ?DEFAULT_IRC_PORT, - <<"">>}; - _ -> - {Username, - DefaultEncoding, - ?DEFAULT_IRC_PORT, - <<"">>} - end, - {iolist_to_binary(NewUsername), - iolist_to_binary(NewEncoding), - if NewPort >= 0 andalso NewPort =< 65535 -> NewPort; - true -> ?DEFAULT_IRC_PORT - end, - 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]}); - 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/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">>, - jlib: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(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(_ServerHost, _From, _To, - #adhoc_request{action = <<"cancel">>} = Request) -> - adhoc:produce_response(Request, - #adhoc_response{status = canceled}); -adhoc_register(ServerHost, From, To, - #adhoc_request{lang = Lang, node = _Node, xdata = XData, - 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 -> - Error = {error, ?ERR_BAD_REQUEST}, - 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}); - _ -> {error, ?ERR_INTERNAL_SERVER_ERROR} - end; - true -> - Form = generate_adhoc_register_form(Lang, Username, - ConnectionsParams), - adhoc:produce_response(Request, - #adhoc_response{status = executing, - elements = [Form], - actions = - [<<"next">>, - <<"complete">>]}) - 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, [])}. - -generate_connection_params_fields(Lang, [], Number, - Acc) -> - Field = generate_connection_params_field(Lang, <<"">>, - <<"">>, -1, <<"">>, Number), - lists:reverse(Field ++ Acc); -generate_connection_params_fields(Lang, - [ConnectionParams | ConnectionsParams], - Number, Acc) -> - case ConnectionParams of - {Server, Encoding, Port, Password} -> - Field = generate_connection_params_field(Lang, Server, - Encoding, Port, Password, - Number), - generate_connection_params_fields(Lang, - ConnectionsParams, Number + 1, - Field ++ Acc); - {Server, Encoding, Port} -> - Field = generate_connection_params_field(Lang, Server, - Encoding, Port, <<"">>, Number), - generate_connection_params_fields(Lang, - ConnectionsParams, Number + 1, - Field ++ Acc); - {Server, Encoding} -> - Field = generate_connection_params_field(Lang, Server, - Encoding, -1, <<"">>, Number), - generate_connection_params_fields(Lang, - ConnectionsParams, Number + 1, - Field ++ Acc); - _ -> [] - end. - -generate_connection_params_field(Lang, Server, Encoding, - Port, Password, Number) -> - EncodingUsed = case Encoding of - <<>> -> get_default_encoding(Server); - _ -> Encoding - end, - PortUsedInt = if Port >= 0 andalso Port =< 65535 -> - Port; - true -> ?DEFAULT_IRC_PORT - end, - PortUsed = - iolist_to_binary(integer_to_list(PortUsedInt)), - PasswordUsed = case Password of - <<>> -> <<>>; - _ -> Password - 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) -> - Servers = lists:flatmap( - fun({<<"server", Var/binary>>, Value}) -> - [{Var, Value}]; - (_) -> - [] - end, Fields), - Encodings = lists:flatmap( - fun({<<"encoding", Var/binary>>, Value}) -> - [{Var, Value}]; - (_) -> - [] - end, Fields), - Ports = lists:flatmap( - fun({<<"port", Var/binary>>, Value}) -> - [{Var, Value}]; - (_) -> - [] - end, Fields), - Passwords = lists:flatmap( - fun({<<"password", Var/binary>>, Value}) -> - [{Var, Value}]; - (_) -> - [] - end, Fields), - parse_connections_params(Servers, Encodings, Ports, - Passwords). - -retrieve_connections_params(ConnectionParams, - ServerN) -> - case ConnectionParams of - [{ConnectionParamN, ConnectionParam} - | ConnectionParamsTail] -> - if ServerN == ConnectionParamN -> - {ConnectionParam, ConnectionParamsTail}; - ServerN < ConnectionParamN -> - {[], - [{ConnectionParamN, ConnectionParam} - | ConnectionParamsTail]}; - ServerN > ConnectionParamN -> {[], ConnectionParamsTail} - end; - _ -> {[], []} - end. - -parse_connections_params([], _, _, _) -> []; -parse_connections_params(_, [], [], []) -> []; -parse_connections_params([{ServerN, Server} | Servers], - Encodings, Ports, Passwords) -> - {NewEncoding, NewEncodings} = - retrieve_connections_params(Encodings, ServerN), - {NewPort, NewPorts} = retrieve_connections_params(Ports, - ServerN), - {NewPassword, NewPasswords} = - retrieve_connections_params(Passwords, ServerN), - [{Server, NewEncoding, NewPort, NewPassword} - | parse_connections_params(Servers, NewEncodings, - NewPorts, NewPasswords)]. - -get_username_and_connection_params(Data) -> - Username = case lists:keysearch(username, 1, Data) of - {value, {_, U}} when is_binary(U) -> - U; - _ -> - <<"">> - end, - ConnParams = case lists:keysearch(connections_params, 1, Data) of - {value, {_, L}} when is_list(L) -> - L; - _ -> - [] - end, - {Username, ConnParams}. - -data_to_binary(Data) -> - lists:map( - fun({username, U}) -> - {username, iolist_to_binary(U)}; - ({connections_params, Params}) -> - {connections_params, - lists:map( - fun({S, E}) -> - {iolist_to_binary(S), iolist_to_binary(E)}; - ({S, E, Port}) -> - {iolist_to_binary(S), iolist_to_binary(E), Port}; - ({S, E, Port, P}) -> - {iolist_to_binary(S), iolist_to_binary(E), - Port, iolist_to_binary(P)} - end, Params)}; - (Opt) -> - Opt - end, Data). - -conn_params_to_list(Params) -> - lists:map( - fun({S, E}) -> - {binary_to_list(S), binary_to_list(E)}; - ({S, E, Port}) -> - {binary_to_list(S), binary_to_list(E), Port}; - ({S, E, Port, P}) -> - {binary_to_list(S), binary_to_list(E), - Port, binary_to_list(P)} - end, Params). - -update_table() -> - Fields = record_info(fields, irc_custom), - case mnesia:table_info(irc_custom, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - irc_custom, Fields, set, - fun(#irc_custom{us_host = {_, H}}) -> H end, - fun(#irc_custom{us_host = {{U, S}, H}, - data = Data} = R) -> - R#irc_custom{us_host = {{iolist_to_binary(U), - iolist_to_binary(S)}, - iolist_to_binary(H)}, - data = data_to_binary(Data)} - end); - _ -> - ?INFO_MSG("Recreating irc_custom table", []), - mnesia:transform_table(irc_custom, ignore, Fields) - end. - -export(_Server) -> - [{irc_custom, - fun(Host, #irc_custom{us_host = {{U, S}, IRCHost}, - data = Data}) -> - case str:suffix(Host, IRCHost) of - true -> - SJID = ejabberd_odbc:escape( - jlib:jid_to_string( - jlib:make_jid(U, S, <<"">>))), - SIRCHost = ejabberd_odbc:escape(IRCHost), - SData = ejabberd_odbc:encode_term(Data), - [[<<"delete from irc_custom where jid='">>, SJID, - <<"' and host='">>, SIRCHost, <<"';">>], - [<<"insert into irc_custom(jid, host, " - "data) values ('">>, - SJID, <<"', '">>, SIRCHost, <<"', '">>, SData, - <<"');">>]]; - false -> - [] - end - end}]. diff --git a/src/mod_irc/mod_irc_connection.erl b/src/mod_irc/mod_irc_connection.erl deleted file mode 100644 index ba0cb4345..000000000 --- a/src/mod_irc/mod_irc_connection.erl +++ /dev/null @@ -1,1581 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc_connection.erl -%%% Author : Alexey Shchepin <alexey@process-one.net> -%%% Purpose : -%%% Created : 15 Feb 2003 by Alexey Shchepin <alexey@process-one.net> -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2013 ProcessOne -%%% -%%% This program is free software; you can redistribute it and/or -%%% modify it under the terms of the GNU General Public License as -%%% published by the Free Software Foundation; either version 2 of the -%%% License, or (at your option) any later version. -%%% -%%% This program is distributed in the hope that it will be useful, -%%% but WITHOUT ANY WARRANTY; without even the implied warranty of -%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -%%% General Public License for more details. -%%% -%%% You should have received a copy of the GNU General Public License -%%% along with this program; if not, write to the Free Software -%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA -%%% 02111-1307 USA -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc_connection). - --author('alexey@process-one.net'). - --behaviour(gen_fsm). - -%% External exports --export([start_link/8, start/9, route_chan/4, - route_nick/3]). - -%% gen_fsm callbacks --export([init/1, open_socket/2, wait_for_registration/2, - stream_established/2, handle_event/3, - handle_sync_event/4, handle_info/3, terminate/3, - code_change/4]). - --include("ejabberd.hrl"). - --include("jlib.hrl"). - --define(SETS, gb_sets). - --record(state, - {socket :: inet:socket(), - encoding = <<"">> :: binary(), - port = 0 :: inet:port_number(), - password = <<"">> :: binary(), - queue = queue:new() :: queue(), - user = #jid{} :: jid(), - host = <<"">> :: binary(), - server = <<"">> :: binary(), - nick = <<"">> :: binary(), - channels = dict:new() :: dict(), - nickchannel :: binary(), - mod = mod_irc :: atom(), - inbuf = <<"">> :: binary(), - outbuf = <<"">> :: binary()}). - -%-define(DBGFSM, true). - --ifdef(DBGFSM). - --define(FSMOPTS, [{debug, [trace]}]). - --else. - --define(FSMOPTS, []). - -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- --endif. - -start(From, Host, ServerHost, Server, Username, - Encoding, Port, Password, Mod) -> - Supervisor = gen_mod:get_module_proc(ServerHost, - ejabberd_mod_irc_sup), - supervisor:start_child(Supervisor, - [From, Host, Server, Username, Encoding, Port, - Password, Mod]). - -start_link(From, Host, Server, Username, Encoding, Port, - Password, Mod) -> - gen_fsm:start_link(?MODULE, - [From, Host, Server, Username, Encoding, Port, Password, - Mod], - ?FSMOPTS). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_fsm -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, StateName, StateData} | -%% {ok, StateName, StateData, Timeout} | -%% ignore | -%% {stop, StopReason} -%%---------------------------------------------------------------------- -init([From, Host, Server, Username, Encoding, Port, - Password, Mod]) -> - gen_fsm:send_event(self(), init), - {ok, open_socket, - #state{queue = queue:new(), mod = Mod, - encoding = Encoding, port = Port, password = Password, - user = From, nick = Username, host = Host, - server = Server}}. - -%%---------------------------------------------------------------------- -%% Func: StateName/2 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -open_socket(init, StateData) -> - Addr = StateData#state.server, - Port = StateData#state.port, - ?DEBUG("Connecting with IPv6 to ~s:~p", [Addr, Port]), - Connect6 = gen_tcp:connect(binary_to_list(Addr), Port, - [inet6, binary, {packet, 0}]), - Connect = case Connect6 of - {error, _} -> - ?DEBUG("Connection with IPv6 to ~s:~p failed. " - "Now using IPv4.", - [Addr, Port]), - gen_tcp:connect(binary_to_list(Addr), Port, - [inet, binary, {packet, 0}]); - _ -> Connect6 - end, - case Connect of - {ok, Socket} -> - NewStateData = StateData#state{socket = Socket}, - if StateData#state.password /= <<"">> -> - send_text(NewStateData, - io_lib:format("PASS ~s\r\n", - [StateData#state.password])); - true -> true - end, - send_text(NewStateData, - io_lib:format("NICK ~s\r\n", [StateData#state.nick])), - send_text(NewStateData, - io_lib:format("USER ~s ~s ~s :~s\r\n", - [StateData#state.nick, StateData#state.nick, - StateData#state.host, - StateData#state.nick])), - {next_state, wait_for_registration, NewStateData}; - {error, Reason} -> - ?DEBUG("connect return ~p~n", [Reason]), - Text = case Reason of - timeout -> <<"Server Connect Timeout">>; - _ -> <<"Server Connect Failed">> - end, - bounce_messages(Text), - {stop, normal, StateData} - end. - -wait_for_registration(closed, StateData) -> - {stop, normal, StateData}. - -stream_established({xmlstreamend, _Name}, StateData) -> - {stop, normal, StateData}; -stream_established(timeout, StateData) -> - {stop, normal, StateData}; -stream_established(closed, StateData) -> - {stop, normal, StateData}. - -%%---------------------------------------------------------------------- -%% Func: StateName/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {reply, Reply, NextStateName, NextStateData} | -%% {reply, Reply, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} | -%% {stop, Reason, Reply, NewStateData} -%%---------------------------------------------------------------------- -%state_name(Event, From, StateData) -> -% Reply = ok, -% {reply, Reply, state_name, StateData}. - -%%---------------------------------------------------------------------- -%% Func: handle_event/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -handle_event(_Event, StateName, StateData) -> - {next_state, StateName, StateData}. - -%%---------------------------------------------------------------------- -%% Func: handle_sync_event/4 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {reply, Reply, NextStateName, NextStateData} | -%% {reply, Reply, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} | -%% {stop, Reason, Reply, NewStateData} -%%---------------------------------------------------------------------- -handle_sync_event(_Event, _From, StateName, - StateData) -> - Reply = ok, {reply, Reply, StateName, StateData}. - -code_change(_OldVsn, StateName, StateData, _Extra) -> - {ok, StateName, StateData}. - --define(SEND(S), - if StateName == stream_established -> - send_text(StateData, S), StateData; - true -> - StateData#state{outbuf = <<(StateData#state.outbuf)/binary, - (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 xml:get_attr_s(<<"xmlns">>, Attrs) of - ?NS_MUC -> true; - _ -> false - end; - _ -> false - end - end, - Els) - of - [ElXMUC | _] -> - case xml:get_subtag(ElXMUC, <<"password">>) of - #xmlel{name = <<"password">>} = PasswordTag -> - {true, xml:get_tag_cdata(PasswordTag)}; - _ -> false - end; - _ -> false - end. - -%%---------------------------------------------------------------------- -%% Func: handle_info/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -handle_info({route_chan, Channel, Resource, - #xmlel{name = <<"presence">>, attrs = Attrs} = - Presence}, - StateName, StateData) -> - NewStateData = case xml: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; -handle_info({route_chan, Channel, Resource, - #xmlel{name = <<"message">>, attrs = Attrs} = El}, - StateName, StateData) -> - NewStateData = case xml:get_attr_s(<<"type">>, Attrs) of - <<"groupchat">> -> - case xml:get_path_s(El, [{elem, <<"subject">>}, cdata]) - of - <<"">> -> - ejabberd_router:route( - jlib:make_jid( - iolist_to_binary([Channel, - <<"%">>, - StateData#state.server]), - StateData#state.host, - StateData#state.nick), - StateData#state.user, El), - Body = xml: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 = xml: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}, - StateName, StateData) -> - From = StateData#state.user, - To = jlib:make_jid(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, - {next_state, StateName, StateData}; -handle_info({route_chan, _Channel, _Resource, _Packet}, - StateName, StateData) -> - {next_state, StateName, StateData}; -handle_info({route_nick, Nick, - #xmlel{name = <<"message">>, attrs = Attrs} = El}, - StateName, StateData) -> - NewStateData = case xml:get_attr_s(<<"type">>, Attrs) of - <<"chat">> -> - Body = xml: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; -handle_info({route_nick, _Nick, _Packet}, StateName, - StateData) -> - {next_state, StateName, StateData}; -handle_info({ircstring, - <<$P, $I, $N, $G, $\s, ID/binary>>}, - StateName, StateData) -> - send_text(StateData, <<"PONG ", ID/binary, "\r\n">>), - {next_state, StateName, StateData}; -handle_info({ircstring, <<$:, String/binary>>}, - wait_for_registration, StateData) -> - Words = str:tokens(String, <<" ">>), - {NewState, NewStateData} = case Words of - [_, <<"001">> | _] -> - send_text(StateData, - io_lib:format("CODEPAGE ~s\r\n", - [StateData#state.encoding])), - {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, - if NewState == error -> {stop, normal, NewStateData}; - true -> {next_state, NewState, NewStateData} - end; -handle_info({ircstring, <<$:, String/binary>>}, - _StateName, StateData) -> - Words = str:tokens(String, <<" ">>), - NewStateData = case Words of - [_, <<"353">> | Items] -> - process_channel_list(StateData, Items); - [_, <<"332">>, _Nick, <<$#, Chan/binary>> | _] -> - process_channel_topic(StateData, Chan, String), - StateData; - [_, <<"333">>, _Nick, <<$#, Chan/binary>> | _] -> - process_channel_topic_who(StateData, Chan, String), - StateData; - [_, <<"318">>, _, Nick | _] -> - process_endofwhois(StateData, String, Nick), StateData; - [_, <<"311">>, _, Nick, Ident, Irchost | _] -> - process_whois311(StateData, String, Nick, Ident, - Irchost), - StateData; - [_, <<"312">>, _, Nick, Ircserver | _] -> - process_whois312(StateData, String, Nick, Ircserver), - StateData; - [_, <<"319">>, _, Nick | _] -> - process_whois319(StateData, String, Nick), 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/binary>> | _] -> - process_chanprivmsg(StateData, Chan, From, String), - StateData; - [From, <<"NOTICE">>, <<$#, Chan/binary>> | _] -> - process_channotice(StateData, Chan, From, String), - StateData; - [From, <<"PRIVMSG">>, Nick, <<":\001VERSION\001">> - | _] -> - process_version(StateData, Nick, From), StateData; - [From, <<"PRIVMSG">>, Nick, <<":\001USERINFO\001">> - | _] -> - process_userinfo(StateData, Nick, From), StateData; - [From, <<"PRIVMSG">>, Nick | _] -> - process_privmsg(StateData, Nick, From, String), - StateData; - [From, <<"NOTICE">>, Nick | _] -> - process_notice(StateData, Nick, From, String), - StateData; - [From, <<"TOPIC">>, <<$#, Chan/binary>> | _] -> - process_topic(StateData, Chan, From, String), - StateData; - [From, <<"PART">>, <<$#, Chan/binary>> | _] -> - process_part(StateData, Chan, From, String); - [From, <<"QUIT">> | _] -> - process_quit(StateData, From, String); - [From, <<"JOIN">>, Chan | _] -> - process_join(StateData, Chan, From, String); - [From, <<"MODE">>, <<$#, Chan/binary>>, <<"+o">>, Nick - | _] -> - process_mode_o(StateData, Chan, From, Nick, - <<"admin">>, <<"moderator">>), - StateData; - [From, <<"MODE">>, <<$#, Chan/binary>>, <<"-o">>, Nick - | _] -> - process_mode_o(StateData, Chan, From, Nick, - <<"member">>, <<"participant">>), - StateData; - [From, <<"KICK">>, <<$#, Chan/binary>>, Nick | _] -> - process_kick(StateData, Chan, From, Nick, String), - StateData; - [From, <<"NICK">>, Nick | _] -> - process_nick(StateData, From, Nick); - _ -> - ?DEBUG("unknown irc command '~s'~n", [String]), - StateData - end, - NewStateData1 = case StateData#state.outbuf of - <<"">> -> NewStateData; - Data -> - send_text(NewStateData, Data), - NewStateData#state{outbuf = <<"">>} - end, - {next_state, stream_established, NewStateData1}; -handle_info({ircstring, - <<$E, $R, $R, $O, $R, _/binary>> = String}, - StateName, StateData) -> - process_error(StateData, String), - {next_state, StateName, StateData}; -handle_info({ircstring, String}, StateName, - StateData) -> - ?DEBUG("unknown irc command '~s'~n", [String]), - {next_state, StateName, StateData}; -handle_info({send_text, Text}, StateName, StateData) -> - send_text(StateData, Text), - {next_state, StateName, StateData}; -handle_info({tcp, _Socket, Data}, StateName, - StateData) -> - Buf = <<(StateData#state.inbuf)/binary, Data/binary>>, - Strings = ejabberd_regexp:split(<< <<C>> - || <<C>> <= Buf, C /= $\r >>, - <<"\n">>), - ?DEBUG("strings=~p~n", [Strings]), - NewBuf = process_lines(StateData#state.encoding, - Strings), - {next_state, StateName, - StateData#state{inbuf = NewBuf}}; -handle_info({tcp_closed, _Socket}, StateName, - StateData) -> - gen_fsm:send_event(self(), closed), - {next_state, StateName, StateData}; -handle_info({tcp_error, _Socket, _Reason}, StateName, - StateData) -> - gen_fsm:send_event(self(), closed), - {next_state, StateName, StateData}. - -%%---------------------------------------------------------------------- -%% Func: terminate/3 -%% Purpose: Shutdown the fsm -%% Returns: any -%%---------------------------------------------------------------------- -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">>}]}, - FullStateData} - end, - (FullStateData#state.mod):closed_connection(StateData#state.host, - StateData#state.user, - StateData#state.server), - bounce_messages(<<"Server Connect Failed">>), - lists:foreach(fun (Chan) -> - Stanza = #xmlel{name = <<"presence">>, - attrs = [{<<"type">>, <<"error">>}], - children = [Error]}, - send_stanza(Chan, StateData, Stanza) - end, - dict:fetch_keys(StateData#state.channels)), - case StateData#state.socket of - undefined -> ok; - Socket -> gen_tcp:close(Socket) - end, - ok. - -send_stanza(Chan, StateData, Stanza) -> - ejabberd_router:route( - jlib:make_jid( - iolist_to_binary([Chan, - <<"%">>, - StateData#state.server]), - StateData#state.host, - StateData#state.nick), - StateData#state.user, Stanza). - -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 = []}]}]}, - send_stanza(Chan, StateData, Stanza). - -%%%---------------------------------------------------------------------- -%%% Internal functions -%%%---------------------------------------------------------------------- - -send_text(#state{socket = Socket, encoding = Encoding}, - Text) -> - CText = iconv:convert(<<"utf-8">>, Encoding, iolist_to_binary(Text)), - gen_tcp:send(Socket, CText). - -%send_queue(Socket, Q) -> -% case queue:out(Q) of -% {{value, El}, Q1} -> -% send_element(Socket, El), -% send_queue(Socket, Q1); -% {empty, Q1} -> -% ok -% end. - -bounce_messages(Reason) -> - receive - {send_element, El} -> - #xmlel{attrs = Attrs} = El, - case xml:get_attr_s(<<"type">>, Attrs) of - <<"error">> -> ok; - _ -> - Err = jlib:make_error_reply(El, <<"502">>, Reason), - From = jlib:string_to_jid(xml:get_attr_s(<<"from">>, - Attrs)), - To = jlib:string_to_jid(xml:get_attr_s(<<"to">>, - Attrs)), - ejabberd_router:route(To, From, Err) - end, - bounce_messages(Reason) - after 0 -> ok - end. - -route_chan(Pid, Channel, Resource, Packet) -> - Pid ! {route_chan, Channel, Resource, Packet}. - -route_nick(Pid, Nick, Packet) -> - Pid ! {route_nick, Nick, Packet}. - -process_lines(_Encoding, [S]) -> S; -process_lines(Encoding, [S | Ss]) -> - self() ! - {ircstring, iconv:convert(Encoding, <<"utf-8">>, S)}, - process_lines(Encoding, Ss). - -process_channel_list(StateData, Items) -> - process_channel_list_find_chan(StateData, Items). - -process_channel_list_find_chan(StateData, []) -> - StateData; -process_channel_list_find_chan(StateData, - [<<$#, Chan/binary>> | Items]) -> - process_channel_list_users(StateData, Chan, Items); -process_channel_list_find_chan(StateData, - [_ | Items]) -> - process_channel_list_find_chan(StateData, Items). - -process_channel_list_users(StateData, _Chan, []) -> - StateData; -process_channel_list_users(StateData, Chan, - [User | Items]) -> - NewStateData = process_channel_list_user(StateData, - Chan, User), - process_channel_list_users(NewStateData, Chan, Items). - -process_channel_list_user(StateData, Chan, User) -> - User1 = case User of - <<$:, U1/binary>> -> U1; - _ -> User - end, - {User2, Affiliation, Role} = case User1 of - <<$@, U2/binary>> -> - {U2, <<"admin">>, <<"moderator">>}; - <<$+, U2/binary>> -> - {U2, <<"member">>, <<"participant">>}; - <<$%, U2/binary>> -> - {U2, <<"admin">>, <<"moderator">>}; - <<$&, U2/binary>> -> - {U2, <<"admin">>, <<"moderator">>}; - <<$~, U2/binary>> -> - {U2, <<"admin">>, <<"moderator">>}; - _ -> {User1, <<"member">>, <<"participant">>} - end, - ejabberd_router:route(jlib:make_jid(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 = []}]}]}), - case catch dict:update(Chan, - fun (Ps) -> (?SETS):add_element(User2, Ps) end, - 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(jlib:make_jid(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>>}]}]}). - -process_channel_topic_who(StateData, Chan, String) -> - Words = str:tokens(String, <<" ">>), - Msg1 = case Words of - [_, <<"333">>, _, _Chan, Whoset, Timeset] -> - {Unixtimeset, _Rest} = str:to_integer(Timeset), - <<"Topic for #", Chan/binary, " set by ", Whoset/binary, - " at ", (unixtime2string(Unixtimeset))/binary>>; - [_, <<"333">>, _, _Chan, Whoset | _] -> - <<"Topic for #", Chan/binary, " set by ", - Whoset/binary>>; - _ -> String - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route(jlib:make_jid(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}]}]}). - -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}]}]}. - -process_nick_in_use(StateData, String) -> - 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(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} - end. - -process_num_error(StateData, String) -> - Error = error_unknown_num(StateData, String, - <<"continue">>), - lists:foreach(fun (Chan) -> - ejabberd_router:route( - jlib:make_jid( - 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)), - StateData. - -process_endofwhois(StateData, _String, Nick) -> - ejabberd_router:route(jlib:make_jid(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">>}]}]}). - -process_whois311(StateData, String, Nick, Ident, - Irchost) -> - Fullname = ejabberd_regexp:replace(String, - <<".*311[^:]*:">>, <<"">>), - ejabberd_router:route(jlib:make_jid(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])}]}]}). - -process_whois312(StateData, String, Nick, Ircserver) -> - Ircserverdesc = ejabberd_regexp:replace(String, - <<".*312[^:]*:">>, <<"">>), - ejabberd_router:route(jlib:make_jid(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])}]}]}). - -process_whois319(StateData, String, Nick) -> - Chanlist = ejabberd_regexp:replace(String, - <<".*319[^:]*:">>, <<"">>), - ejabberd_router:route(jlib:make_jid(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])}]}]}). - -process_chanprivmsg(StateData, Chan, From, String) -> - [FromUser | _] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*PRIVMSG[^:]*:">>, <<"">>), - Msg1 = case Msg of - <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> - <<"/me ", Rest/binary>>; - _ -> Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route(jlib:make_jid(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}]}]}). - -process_channotice(StateData, Chan, From, String) -> - [FromUser | _] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*NOTICE[^:]*:">>, <<"">>), - Msg1 = case Msg of - <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> - <<"/me ", Rest/binary>>; - _ -> <<"/me NOTICE: ", Msg/binary>> - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route(jlib:make_jid(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}]}]}). - -process_privmsg(StateData, _Nick, From, String) -> - [FromUser | _] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*PRIVMSG[^:]*:">>, <<"">>), - Msg1 = case Msg of - <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> - <<"/me ", Rest/binary>>; - _ -> Msg - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route(jlib:make_jid(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}]}]}). - -process_notice(StateData, _Nick, From, String) -> - [FromUser | _] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*NOTICE[^:]*:">>, <<"">>), - Msg1 = case Msg of - <<1, $A, $C, $T, $I, $O, $N, $\s, Rest/binary>> -> - <<"/me ", Rest/binary>>; - _ -> <<"/me NOTICE: ", Msg/binary>> - end, - Msg2 = filter_message(Msg1), - ejabberd_router:route(jlib:make_jid(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}]}]}). - -process_version(StateData, _Nick, From) -> - [FromUser | _] = str:tokens(From, <<"!">>), - send_text(StateData, - io_lib:format("NOTICE ~s :\001VERSION ejabberd IRC " - "transport ~s (c) Alexey Shchepin\001\r\n", - [FromUser, ?VERSION]) - ++ - io_lib:format("NOTICE ~s :\001VERSION http://ejabberd.jabber" - "studio.org/\001\r\n", - [FromUser])). - -process_userinfo(StateData, _Nick, From) -> - [FromUser | _] = str:tokens(From, <<"!">>), - send_text(StateData, - io_lib:format("NOTICE ~s :\001USERINFO xmpp:~s\001\r\n", - [FromUser, - jlib:jid_to_string(StateData#state.user)])). - -process_topic(StateData, Chan, From, String) -> - [FromUser | _] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*TOPIC[^:]*:">>, <<"">>), - Msg1 = filter_message(Msg), - ejabberd_router:route(jlib:make_jid(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>>}]}]}). - -process_part(StateData, Chan, From, String) -> - [FromUser | FromIdent] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*PART[^:]*:">>, <<"">>), - Msg1 = filter_message(Msg), - ejabberd_router:route(jlib:make_jid(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, ")"])}]}]}), - case catch dict:update(Chan, - fun (Ps) -> remove_element(FromUser, Ps) end, - StateData#state.channels) - of - {'EXIT', _} -> StateData; - NS -> StateData#state{channels = NS} - end. - -process_quit(StateData, From, String) -> - [FromUser | FromIdent] = str:tokens(From, <<"!">>), - 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(jlib:make_jid(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), - StateData. - -process_join(StateData, Channel, From, _String) -> - [FromUser | FromIdent] = str:tokens(From, <<"!">>), - [Chan | _] = binary:split(Channel, <<":#">>), - ejabberd_router:route(jlib:make_jid(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)}]}]}), - case catch dict:update(Chan, - fun (Ps) -> (?SETS):add_element(FromUser, Ps) end, - StateData#state.channels) - of - {'EXIT', _} -> StateData; - NS -> StateData#state{channels = NS} - end. - -process_mode_o(StateData, Chan, _From, Nick, - Affiliation, Role) -> - ejabberd_router:route(jlib:make_jid(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 = []}]}]}). - -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(jlib:make_jid(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(jlib:make_jid(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 = []}]}]}). - -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(jlib:make_jid( - 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(jlib:make_jid( - 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), - if FromUser == StateData#state.nick -> - StateData#state{nick = Nick, nickchannel = undefined, - channels = NewChans}; - true -> StateData#state{channels = NewChans} - end. - -process_error(StateData, String) -> - lists:foreach(fun (Chan) -> - ejabberd_router:route(jlib:make_jid( - 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)). - -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}]}]}. - -remove_element(E, Set) -> - case (?SETS):is_element(E, Set) of - true -> (?SETS):del_element(E, Set); - _ -> 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 - end. - -process_iq_admin(StateData, Channel, set, SubEl) -> - case xml:get_subtag(SubEl, <<"item">>) of - false -> {error, ?ERR_BAD_REQUEST}; - ItemEl -> - Nick = xml:get_tag_attr_s(<<"nick">>, ItemEl), - Affiliation = xml:get_tag_attr_s(<<"affiliation">>, - ItemEl), - Role = xml:get_tag_attr_s(<<"role">>, ItemEl), - Reason = xml: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) -> - case Reason of - <<"">> -> - send_text(StateData, - io_lib:format("KICK #~s ~s\r\n", [Channel, Nick])); - _ -> - send_text(StateData, - 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}. - -filter_message(Msg) -> - list_to_binary( - lists:filter(fun (C) -> - if (C < 32) and (C /= 9) and (C /= 10) and (C /= 13) -> - false; - true -> true - end - end, - binary_to_list(filter_mirc_colors(Msg)))). - -filter_mirc_colors(Msg) -> - ejabberd_regexp:greplace(Msg, - <<"(\\003[0-9]+)(,[0-9]+)?">>, <<"">>). - -unixtime2string(Unixtime) -> - Secs = Unixtime + - calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, - {0, 0, 0}}), - {{Year, Month, Day}, {Hour, Minute, Second}} = - calendar:universal_time_to_local_time(calendar:gregorian_seconds_to_datetime(Secs)), - iolist_to_binary(io_lib:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])). |