From 3a5d2dbed8f089b112eba7998e2ad0d2508f01cb Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Wed, 20 Jun 2018 12:27:44 +0300 Subject: [PATCH] Move mod_irc to ejabberd-contrib --- README | 3 - config/ejabberd.exs | 3 - config/ejabberd.yml | 1 - configure.ac | 4 +- ejabberd.yml.example | 1 - examples/mtr/ejabberd-netbsd.sh | 2 +- examples/mtr/ejabberd.cfg | 1 - include/mod_irc.hrl | 35 - mix.exs | 2 +- sql/lite.new.sql | 10 - sql/lite.sql | 9 - sql/mssql.sql | 10 - sql/mysql.new.sql | 10 - sql/mysql.sql | 9 - sql/pg.new.sql | 13 - sql/pg.sql | 9 - src/ejabberd_admin.erl | 1 - src/ejabberd_config.erl | 1 - src/ejd2sql.erl | 1 - src/mod_admin_update_sql.erl | 3 - src/mod_irc.erl | 1006 -------------- src/mod_irc_connection.erl | 1205 ----------------- src/mod_irc_mnesia.erl | 81 -- src/mod_irc_riak.erl | 65 - src/mod_irc_sql.erl | 106 -- test/elixir-config/ejabberd_logger.exs | 4 +- .../shared/ejabberd_for_validation.exs | 2 - test/elixir-config/validation_test.exs | 3 +- win32/ejabberd.cfg | 1 - 29 files changed, 7 insertions(+), 2594 deletions(-) delete mode 100644 include/mod_irc.hrl delete mode 100644 src/mod_irc.erl delete mode 100644 src/mod_irc_connection.erl delete mode 100644 src/mod_irc_mnesia.erl delete mode 100644 src/mod_irc_riak.erl delete mode 100644 src/mod_irc_sql.erl diff --git a/README b/README index 87b684f14..fa424c279 100644 --- a/README +++ b/README @@ -93,7 +93,6 @@ Moreover, ejabberd comes with a wide range of other state-of-the-art features: - Users Directory based on users vCards. - Publish-Subscribe component with support for Personal Eventing. - Support for web clients: HTTP Polling and HTTP Binding (BOSH). - - IRC transport. - Component support: interface with networks such as AIM, ICQ and MSN. @@ -112,8 +111,6 @@ To compile ejabberd you need: - OpenSSL 1.0.0 or higher, for STARTTLS, SASL and SSL encryption. - Zlib 1.2.3 or higher, for Stream Compression support (XEP-0138). Optional. - PAM library. Optional. For Pluggable Authentication Modules (PAM). - - GNU Iconv 1.8 or higher, for the IRC Transport (mod_irc). Optional. Not - needed on systems with GNU Libc. - ImageMagick's Convert program. Optional. For CAPTCHA challenges. If your system splits packages in libraries and development headers, you must diff --git a/config/ejabberd.exs b/config/ejabberd.exs index 21bd0dc1e..85c738ff0 100644 --- a/config/ejabberd.exs +++ b/config/ejabberd.exs @@ -94,9 +94,6 @@ defmodule Ejabberd.ConfigFile do module :mod_disco do end - module :mod_irc do - end - module :mod_http_bind do end diff --git a/config/ejabberd.yml b/config/ejabberd.yml index 2a06d74b3..c72e50fe4 100644 --- a/config/ejabberd.yml +++ b/config/ejabberd.yml @@ -565,7 +565,6 @@ modules: mod_configure: {} # requires mod_adhoc mod_disco: {} ## mod_echo: {} - mod_irc: {} mod_http_bind: {} ## mod_http_fileserver: ## docroot: "/var/www" diff --git a/configure.ac b/configure.ac index 7972ffdc2..f83c08024 100644 --- a/configure.ac +++ b/configure.ac @@ -197,12 +197,12 @@ AC_ARG_ENABLE(elixir, esac],[if test "x$elixir" = "x"; then elixir=false; fi]) AC_ARG_ENABLE(iconv, -[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: yes)])], +[AC_HELP_STRING([--enable-iconv], [enable iconv support (default: no)])], [case "${enableval}" in yes) iconv=true ;; no) iconv=false ;; *) AC_MSG_ERROR(bad value ${enableval} for --enable-iconv) ;; -esac],[if test "x$iconv" = "x"; then iconv=true; fi]) +esac],[if test "x$iconv" = "x"; then iconv=false; fi]) AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], [enable debug information (default: yes)])], diff --git a/ejabberd.yml.example b/ejabberd.yml.example index c95d4f2e5..c27622bbb 100644 --- a/ejabberd.yml.example +++ b/ejabberd.yml.example @@ -734,7 +734,6 @@ modules: ## mod_delegation: {} # for xep0356 mod_disco: {} mod_echo: {} - mod_irc: {} mod_bosh: {} ## mod_http_fileserver: ## docroot: "/var/www" diff --git a/examples/mtr/ejabberd-netbsd.sh b/examples/mtr/ejabberd-netbsd.sh index 9896c9bc4..31d01b6b8 100644 --- a/examples/mtr/ejabberd-netbsd.sh +++ b/examples/mtr/ejabberd-netbsd.sh @@ -70,7 +70,7 @@ done echo '7. compile ejabberd' gmake -for A in mod_irc mod_muc mod_pubsub; do +for A in mod_muc mod_pubsub; do (cd $A; gmake) done diff --git a/examples/mtr/ejabberd.cfg b/examples/mtr/ejabberd.cfg index 6f4f4b284..b1023ee0e 100644 --- a/examples/mtr/ejabberd.cfg +++ b/examples/mtr/ejabberd.cfg @@ -51,7 +51,6 @@ override_acls. {mod_offline, []}, {mod_echo, [{host, "echo.jabber.dbc.mtview.ca.us"}]}, {mod_private, []}, -% {mod_irc, []}, {mod_muc, []}, {mod_pubsub, []}, {mod_time, []}, diff --git a/include/mod_irc.hrl b/include/mod_irc.hrl deleted file mode 100644 index e3c7f1718..000000000 --- a/include/mod_irc.hrl +++ /dev/null @@ -1,35 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --type conn_param() :: {binary(), binary(), inet:port_number(), binary()} | - {binary(), binary(), inet:port_number()} | - {binary(), binary()} | - {binary()}. - --type irc_data() :: [{username, binary()} | {connections_params, [conn_param()]}]. - --record(irc_connection, - {jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()}, - pid = self() :: pid()}). - --record(irc_custom, - {us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, - binary()}, - data = [] :: irc_data()}). diff --git a/mix.exs b/mix.exs index 2abe0f055..9f4a39a0c 100644 --- a/mix.exs +++ b/mix.exs @@ -125,7 +125,7 @@ defmodule Ejabberd.Mixfile do defp vars do case :file.consult("vars.config") do {:ok,config} -> config - _ -> [zlib: true, iconv: true] + _ -> [zlib: true, iconv: false] end end diff --git a/sql/lite.new.sql b/sql/lite.new.sql index b1ff14fd1..d58d04c77 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -329,16 +329,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - server_host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host); - CREATE TABLE motd ( username text NOT NULL, server_host text NOT NULL, diff --git a/sql/lite.sql b/sql/lite.sql index eb6b2ab4c..abb25d5b4 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -302,15 +302,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); - -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom (jid, host); - CREATE TABLE motd ( username text PRIMARY KEY, xml text, diff --git a/sql/mssql.sql b/sql/mssql.sql index 648dfb9ed..c6a89311f 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -72,16 +72,6 @@ CREATE TABLE [dbo].[caps_features] ( CREATE CLUSTERED INDEX [caps_features_node_subnode] ON [caps_features] (node, subnode) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); -CREATE TABLE [dbo].[irc_custom] ( - [jid] [varchar] (255) NOT NULL, - [host] [varchar] (255) NOT NULL, - [data] [text] NOT NULL, - [created_at] [datetime] NOT NULL DEFAULT GETDATE() -) TEXTIMAGE_ON [PRIMARY]; - -CREATE UNIQUE CLUSTERED INDEX [irc_custom_jid_host] ON [irc_custom] (jid, host) -WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON); - CREATE TABLE [dbo].[last] ( [username] [varchar] (250) NOT NULL, [seconds] [text] NOT NULL, diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 395a8aa64..544135e82 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -345,16 +345,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - server_host text NOT NULL, - data text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP -) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75)); - CREATE TABLE motd ( username varchar(191) NOT NULL, server_host text NOT NULL, diff --git a/sql/mysql.sql b/sql/mysql.sql index 1d7e4c4e7..7894fa5b7 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -318,15 +318,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - data text NOT NULL, - created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP -) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -CREATE UNIQUE INDEX i_irc_custom_jid_host USING BTREE ON irc_custom(jid(75), host(75)); - CREATE TABLE motd ( username varchar(191) PRIMARY KEY, xml text, diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 82fafa64c..928d74db0 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -144,9 +144,6 @@ -- ALTER TABLE muc_online_users ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE muc_online_users ALTER COLUMN server_host DROP DEFAULT; --- ALTER TABLE irc_custom ADD COLUMN server_host text NOT NULL DEFAULT ''; --- ALTER TABLE irc_custom ALTER COLUMN server_host DROP DEFAULT; - -- ALTER TABLE motd ADD COLUMN server_host text NOT NULL DEFAULT ''; -- ALTER TABLE motd DROP CONSTRAINT motd_pkey; -- ALTER TABLE motd ADD PRIMARY KEY (server_host, username); @@ -498,16 +495,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - server_host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now() -); - -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host); - CREATE TABLE motd ( username text NOT NULL, server_host text NOT NULL, diff --git a/sql/pg.sql b/sql/pg.sql index 4863cb721..a0cfe0ea6 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -320,15 +320,6 @@ CREATE TABLE muc_room_subscribers ( CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); -CREATE TABLE irc_custom ( - jid text NOT NULL, - host text NOT NULL, - data text NOT NULL, - created_at TIMESTAMP NOT NULL DEFAULT now() -); - -CREATE UNIQUE INDEX i_irc_custom_jid_host ON irc_custom USING btree (jid, host); - CREATE TABLE motd ( username text PRIMARY KEY, xml text, diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index ef33f0c2b..8002f7e8f 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -627,7 +627,6 @@ keep_modules_tables() -> %% module. %% Mapping between modules and their tables module_tables(mod_announce) -> [motd, motd_users]; -module_tables(mod_irc) -> [irc_custom]; module_tables(mod_last) -> [last_activity]; module_tables(mod_muc) -> [muc_room, muc_registered]; module_tables(mod_offline) -> [offline_msg]; diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index a2adde8f6..e0f3f6ade 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -1112,7 +1112,6 @@ get_copyright() -> replace_module(mod_announce_odbc) -> {mod_announce, sql}; replace_module(mod_blocking_odbc) -> {mod_blocking, sql}; replace_module(mod_caps_odbc) -> {mod_caps, sql}; -replace_module(mod_irc_odbc) -> {mod_irc, sql}; replace_module(mod_last_odbc) -> {mod_last, sql}; replace_module(mod_muc_odbc) -> {mod_muc, sql}; replace_module(mod_offline_odbc) -> {mod_offline, sql}; diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl index 7e46b8102..3f802e412 100644 --- a/src/ejd2sql.erl +++ b/src/ejd2sql.erl @@ -51,7 +51,6 @@ modules() -> [ejabberd_auth, mod_announce, mod_caps, - mod_irc, mod_last, mod_mam, mod_muc, diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 51787ff6c..3c036f341 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -241,9 +241,6 @@ update_tables(State) -> add_sh_column(State, "muc_online_users"), drop_sh_default(State, "muc_online_users"), - add_sh_column(State, "irc_custom"), - drop_sh_default(State, "irc_custom"), - add_sh_column(State, "motd"), drop_pkey(State, "motd"), add_pkey(State, "motd", ["server_host", "username"]), diff --git a/src/mod_irc.erl b/src/mod_irc.erl deleted file mode 100644 index 42e516b82..000000000 --- a/src/mod_irc.erl +++ /dev/null @@ -1,1006 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc.erl -%%% Author : Alexey Shchepin -%%% Purpose : IRC transport -%%% Created : 15 Feb 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc). - --author('alexey@process-one.net'). - --behaviour(gen_server). - --behaviour(gen_mod). - -%% API --export([start/2, stop/1, reload/3, export/1, import/1, - import/3, closed_connection/3, get_connection_params/3, - data_to_binary/2, process_disco_info/1, process_disco_items/1, - process_register/1, process_vcard/1, process_command/1]). - --export([init/1, handle_call/3, handle_cast/2, - handle_info/2, terminate/2, code_change/3, - mod_opt_type/1, mod_options/1, depends/2]). - --include("logger.hrl"). --include("xmpp.hrl"). --include("mod_irc.hrl"). --include("translate.hrl"). - --define(DEFAULT_IRC_PORT, 6667). - --define(POSSIBLE_ENCODINGS, - [<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>, - <<"utf-8">>, <<"utf-8+latin-1">>]). - --record(state, {hosts = [] :: [binary()], - server_host = <<"">> :: binary(), - access = all :: atom()}). - --callback init(binary(), gen_mod:opts()) -> any(). --callback import(binary(), #irc_custom{}) -> ok | pass. --callback get_data(binary(), binary(), jid()) -> error | empty | irc_data(). --callback set_data(binary(), binary(), jid(), irc_data()) -> {atomic, any()}. - -%%==================================================================== -%% gen_mod API -%%==================================================================== -start(Host, Opts) -> - start_supervisor(Host), - gen_mod:start_child(?MODULE, Host, Opts). - -stop(Host) -> - stop_supervisor(Host), - gen_mod:stop_child(?MODULE, Host). - -reload(Host, NewOpts, OldOpts) -> - Proc = gen_mod:get_module_proc(Host, ?MODULE), - gen_server:cast(Proc, {reload, Host, NewOpts, OldOpts}). - -depends(_Host, _Opts) -> - []. - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -init([Host, Opts]) -> - process_flag(trap_exit, true), - ejabberd:start_app(iconv), - MyHosts = gen_mod:get_opt_hosts(Host, Opts), - Mod = gen_mod:db_mod(Host, Opts, ?MODULE), - Mod:init(Host, Opts), - Access = gen_mod:get_opt(access, Opts), - catch ets:new(irc_connection, - [named_table, public, - {keypos, #irc_connection.jid_server_host}]), - lists:foreach( - fun(MyHost) -> - register_hooks(MyHost), - ejabberd_router:register_route(MyHost, Host) - end, MyHosts), - {ok, - #state{hosts = MyHosts, 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({reload, ServerHost, NewOpts, OldOpts}, State) -> - NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts), - OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts), - NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE), - OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE), - Access = gen_mod:get_opt(access, NewOpts), - if NewMod /= OldMod -> - NewMod:init(ServerHost, NewOpts); - true -> - ok - end, - lists:foreach( - fun(NewHost) -> - ejabberd_router:register_route(NewHost, ServerHost), - register_hooks(NewHost) - end, NewHosts -- OldHosts), - lists:foreach( - fun(OldHost) -> - ejabberd_router:unregister_route(OldHost), - unregister_hooks(OldHost) - end, OldHosts -- NewHosts), - Access = gen_mod:get_opt(access, NewOpts), - {noreply, State#state{hosts = NewHosts, access = Access}}; -handle_cast(Msg, State) -> - ?WARNING_MSG("unexpected cast: ~p", [Msg]), - {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, Packet}, - #state{server_host = ServerHost, access = Access} = - State) -> - To = xmpp:get_to(Packet), - Host = To#jid.lserver, - case catch do_route(Host, ServerHost, Access, 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{hosts = MyHosts}) -> - lists:foreach( - fun(MyHost) -> - ejabberd_router:unregister_route(MyHost), - unregister_hooks(MyHost) - end, MyHosts). - -%%-------------------------------------------------------------------- -%% 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 -%%-------------------------------------------------------------------- -register_hooks(Host) -> - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, - ?MODULE, process_disco_info), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, - ?MODULE, process_disco_items), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_REGISTER, - ?MODULE, process_register), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_VCARD, - ?MODULE, process_vcard), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS, - ?MODULE, process_command). - -unregister_hooks(Host) -> - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_REGISTER), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VCARD), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS). - -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_gen_mod_sup, ChildSpec). - -stop_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, - ejabberd_mod_irc_sup), - supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), - supervisor:delete_child(ejabberd_gen_mod_sup, Proc). - -do_route(Host, ServerHost, Access, Packet) -> - #jid{luser = LUser, lresource = LResource} = xmpp:get_to(Packet), - From = xmpp:get_from(Packet), - case acl:match_rule(ServerHost, Access, From) of - allow -> - case Packet of - #iq{} when LUser == <<"">>, LResource == <<"">> -> - ejabberd_router:process_iq(Packet); - #iq{} when LUser == <<"">>, LResource /= <<"">> -> - Err = xmpp:err_service_unavailable(), - ejabberd_router:route_error(Packet, Err); - _ -> - sm_route(Host, ServerHost, Packet) - end; - deny -> - Lang = xmpp:get_lang(Packet), - Err = xmpp:err_forbidden(<<"Access denied by service policy">>, Lang), - ejabberd_router:route_error(Packet, Err) - end. - -process_disco_info(#iq{type = set, lang = Lang} = IQ) -> - Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); -process_disco_info(#iq{type = get, lang = Lang, to = To, - sub_els = [#disco_info{node = Node}]} = IQ) -> - ServerHost = ejabberd_router:host_of_route(To#jid.lserver), - Info = ejabberd_hooks:run_fold(disco_info, ServerHost, - [], [ServerHost, ?MODULE, <<"">>, <<"">>]), - case iq_disco(ServerHost, Node, Lang) of - undefined -> - xmpp:make_iq_result(IQ, #disco_info{}); - DiscoInfo -> - xmpp:make_iq_result(IQ, DiscoInfo#disco_info{xdata = Info}) - end. - -process_disco_items(#iq{type = set, lang = Lang} = IQ) -> - Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); -process_disco_items(#iq{type = get, lang = Lang, to = To, - sub_els = [#disco_items{node = Node}]} = IQ) -> - case Node of - <<"">> -> - xmpp:make_iq_result(IQ, #disco_items{}); - <<"join">> -> - xmpp:make_iq_result(IQ, #disco_items{}); - <<"register">> -> - xmpp:make_iq_result(IQ, #disco_items{}); - ?NS_COMMANDS -> - Host = To#jid.lserver, - ServerHost = ejabberd_router:host_of_route(Host), - xmpp:make_iq_result( - IQ, #disco_items{node = Node, - items = command_items(ServerHost, Host, Lang)}); - _ -> - Txt = <<"Node not found">>, - xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) - end. - -process_register(#iq{type = get, to = To, from = From, lang = Lang} = IQ) -> - Host = To#jid.lserver, - ServerHost = ejabberd_router:host_of_route(Host), - case get_form(ServerHost, Host, From, Lang) of - {result, Res} -> - xmpp:make_iq_result(IQ, Res); - {error, Error} -> - xmpp:make_error(IQ, Error) - end; -process_register(#iq{type = set, lang = Lang, to = To, from = From, - sub_els = [#register{xdata = #xdata{} = X}]} = IQ) -> - case X#xdata.type of - cancel -> - xmpp:make_iq_result(IQ, #register{}); - submit -> - Host = To#jid.lserver, - ServerHost = ejabberd_router:host_of_route(Host), - case set_form(ServerHost, Host, From, Lang, X) of - {result, Res} -> - xmpp:make_iq_result(IQ, Res); - {error, Error} -> - xmpp:make_error(IQ, Error) - end; - _ -> - Txt = <<"Incorrect value of 'type' attribute">>, - xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)) - end; -process_register(#iq{type = set, lang = Lang} = IQ) -> - Txt = <<"No data form found">>, - xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)). - -process_vcard(#iq{type = set, lang = Lang} = IQ) -> - Txt = <<"Value 'set' of 'type' attribute is not allowed">>, - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); -process_vcard(#iq{type = get, lang = Lang} = IQ) -> - xmpp:make_iq_result(IQ, iq_get_vcard(Lang)). - -process_command(#iq{type = get, lang = Lang} = IQ) -> - Txt = <<"Value 'get' of 'type' attribute is not allowed">>, - xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)); -process_command(#iq{type = set, lang = Lang, to = To, from = From, - sub_els = [#adhoc_command{node = Node} = Request]} = IQ) -> - Host = To#jid.lserver, - ServerHost = ejabberd_router:host_of_route(Host), - case lists:keyfind(Node, 1, commands(ServerHost)) of - {_, _, Function} -> - try Function(From, To, Request) of - ignore -> - ignore; - {error, Error} -> - xmpp:make_error(IQ, Error); - Command -> - xmpp:make_iq_result(IQ, Command) - catch E:R -> - ?ERROR_MSG("ad-hoc handler failed: ~p", - [{E, {R, erlang:get_stacktrace()}}]), - xmpp:make_error(IQ, xmpp:err_internal_server_error()) - end; - _ -> - Txt = <<"Node not found">>, - xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang)) - end. - -sm_route(Host, ServerHost, Packet) -> - From = xmpp:get_from(Packet), - #jid{user = ChanServ, resource = Resource} = xmpp:get_to(Packet), - 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. - #presence{} -> Resource; - %% Otherwise, there is no firm - %% conclusion from the packet. - %% Better to use the configured - %% username (which defaults to the - %% username part of the JID). - _ -> Username - end, - Ident = extract_ident(Packet), - RemoteAddr = extract_ip_address(Packet), - RealName = get_realname(ServerHost), - WebircPassword = get_webirc_password(ServerHost), - {ok, Pid} = mod_irc_connection:start( - From, Host, ServerHost, Server, - ConnectionUsername, Encoding, Port, - Password, Ident, RemoteAddr, RealName, - WebircPassword, ?MODULE), - ets:insert(irc_connection, - #irc_connection{ - jid_server_host = {From, Server, Host}, - pid = Pid}), - mod_irc_connection:route_chan(Pid, Channel, Resource, Packet); - [R] -> - Pid = R#irc_connection.pid, - ?DEBUG("send to process ~p~n", [Pid]), - mod_irc_connection:route_chan(Pid, Channel, Resource, Packet) - end; - _ -> - Lang = xmpp:get_lang(Packet), - case str:tokens(ChanServ, <<"!">>) of - [<<_, _/binary>> = Nick, <<_, _/binary>> = Server] -> - case ets:lookup(irc_connection, {From, Server, Host}) of - [] -> - Txt = <<"IRC connection not found">>, - Err = xmpp:err_service_unavailable(Txt, Lang), - ejabberd_router:route_error(Packet, Err); - [R] -> - Pid = R#irc_connection.pid, - ?DEBUG("send to process ~p~n", [Pid]), - mod_irc_connection:route_nick(Pid, Nick, Packet) - end; - _ -> - Txt = <<"Failed to parse chanserv">>, - Err = xmpp:err_bad_request(Txt, Lang), - ejabberd_router:route_error(Packet, Err) - end - end. - -closed_connection(Host, From, Server) -> - ets:delete(irc_connection, {From, Server, Host}). - -iq_disco(ServerHost, <<"">>, Lang) -> - Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name), - #disco_info{ - identities = [#identity{category = <<"conference">>, - type = <<"irc">>, - name = translate:translate(Lang, Name)}], - features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC, - ?NS_REGISTER, ?NS_VCARD, ?NS_COMMANDS]}; -iq_disco(ServerHost, Node, Lang) -> - case lists:keyfind(Node, 1, commands(ServerHost)) of - {_, Name, _} -> - #disco_info{ - identities = [#identity{category = <<"automation">>, - type = <<"command-node">>, - name = translate:translate(Lang, Name)}], - features = [?NS_COMMANDS, ?NS_XDATA]}; - _ -> - undefined - end. - -iq_get_vcard(Lang) -> - #vcard_temp{fn = <<"ejabberd/mod_irc">>, - url = ejabberd_config:get_uri(), - desc = misc:get_descr(Lang, <<"ejabberd IRC module">>)}. - -command_items(ServerHost, Host, Lang) -> - lists:map(fun({Node, Name, _Function}) -> - #disco_item{jid = jid:make(Host), - node = Node, - name = translate:translate(Lang, Name)} - end, commands(ServerHost)). - -commands(ServerHost) -> - [{<<"join">>, <<"Join channel">>, fun adhoc_join/3}, - {<<"register">>, - <<"Configure username, encoding, port and " - "password">>, - fun (From, To, Request) -> - adhoc_register(ServerHost, From, To, Request) - end}]. - -get_data(ServerHost, Host, From) -> - LServer = jid:nameprep(ServerHost), - Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:get_data(LServer, Host, From). - -get_form(ServerHost, Host, From, Lang) -> - #jid{user = User, server = Server} = From, - DefaultEncoding = get_default_encoding(Host), - Customs = case get_data(ServerHost, Host, From) of - error -> - Txt1 = <<"Database failure">>, - {error, xmpp:err_internal_server_error(Txt1, Lang)}; - empty -> {User, []}; - Data -> get_username_and_connection_params(Data) - end, - case Customs of - {error, _Error} -> - Customs; - {Username, ConnectionsParams} -> - Fs = [#xdata_field{type = 'text-single', - label = translate:translate(Lang, <<"IRC Username">>), - var = <<"username">>, - values = [Username]}, - #xdata_field{type = fixed, - values = [str:format( - translate:translate( - Lang, - <<"If you want to specify" - " different ports, " - "passwords, encodings " - "for IRC servers, " - "fill this list with " - "values in format " - "'{\"irc server\", " - "\"encoding\", port, " - "\"password\"}'. " - "By default this " - "service use \"~s\" " - "encoding, port ~p, " - "empty password.">>), - [DefaultEncoding, ?DEFAULT_IRC_PORT])]}, - #xdata_field{type = fixed, - values = [translate:translate( - Lang, - <<"Example: [{\"irc.lucky.net\", \"koi8-r\", " - "6667, \"secret\"}, {\"vendetta.fef.net\", " - "\"iso8859-1\", 7000}, {\"irc.sometestserver.n" - "et\", \"utf-8\"}].">>)]}, - #xdata_field{type = 'text-multi', - label = translate:translate( - Lang, <<"Connections parameters">>), - var = <<"connections_params">>, - values = str:tokens(str:format( - "~p.", - [conn_params_to_list( - ConnectionsParams)]), - <<"\n">>)}], - X = #xdata{type = form, - title = <<(translate:translate( - Lang, <<"Registration in mod_irc for ">>))/binary, - User/binary, "@", Server/binary>>, - instructions = - [translate:translate( - Lang, - <<"Enter username, encodings, ports and " - "passwords you wish to use for connecting " - "to IRC servers">>)], - fields = Fs}, - {result, - #register{instructions = - translate:translate(Lang, - <<"You need an x:data capable client to " - "configure mod_irc settings">>), - xdata = X}} - end. - -set_data(ServerHost, Host, From, Data) -> - LServer = jid:nameprep(ServerHost), - Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:set_data(LServer, Host, From, data_to_binary(From, Data)). - -set_form(ServerHost, Host, From, Lang, XData) -> - case {xmpp_util:get_xdata_values(<<"username">>, XData), - xmpp_util:get_xdata_values(<<"connections_params">>, XData)} of - {[Username], [_|_] = Strings} -> - EncString = lists:foldl(fun (S, Res) -> - <> - end, <<"">>, Strings), - case erl_scan:string(binary_to_list(EncString)) of - {ok, Tokens, _} -> - case erl_parse:parse_term(Tokens) of - {ok, ConnectionsParams} -> - case set_data(ServerHost, Host, From, - [{username, Username}, - {connections_params, ConnectionsParams}]) of - {atomic, _} -> - {result, undefined}; - _ -> - Txt = <<"Database failure">>, - {error, xmpp:err_internal_server_error(Txt, Lang)} - end; - _ -> - Txt = <<"Parse error">>, - {error, xmpp:err_not_acceptable(Txt, Lang)} - end; - _ -> - {error, xmpp:err_not_acceptable(<<"Scan error">>, Lang)} - end; - _ -> - Txt = <<"Incorrect value in data form">>, - {error, xmpp:err_not_acceptable(Txt, Lang)} - end. - -get_connection_params(Host, From, IRCServer) -> - [_ | HostTail] = str:tokens(Host, <<".">>), - 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), - ?INFO_MSG("The default_encoding configured for " - "host ~p is: ~p~n", - [ServerHost, Result]), - Result. - -get_realname(ServerHost) -> - gen_mod:get_module_opt(ServerHost, ?MODULE, realname). - -get_webirc_password(ServerHost) -> - gen_mod:get_module_opt(ServerHost, ?MODULE, webirc_password). - -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_command{action = cancel} = Request) -> - xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled}); -adhoc_join(_From, _To, #adhoc_command{lang = Lang, xdata = undefined} = Request) -> - X = #xdata{type = form, - title = translate:translate(Lang, <<"Join IRC channel">>), - fields = [#xdata_field{var = <<"channel">>, - type = 'text-single', - label = translate:translate( - Lang, <<"IRC channel (don't put the first #)">>), - required = true}, - #xdata_field{var = <<"server">>, - type = 'text-single', - label = translate:translate(Lang, <<"IRC server">>), - required = true}]}, - xmpp_util:make_adhoc_response( - Request, #adhoc_command{status = executing, xdata = X}); -adhoc_join(From, To, #adhoc_command{lang = Lang, xdata = X} = Request) -> - Channel = case xmpp_util:get_xdata_values(<<"channel">>, X) of - [C] -> C; - _ -> false - end, - Server = case xmpp_util:get_xdata_values(<<"server">>, X) of - [S] -> S; - _ -> false - end, - if Channel /= false, Server /= false -> - RoomJID = jid:make(<>, - To#jid.server), - Reason = translate:translate(Lang, <<"Join the IRC channel here.">>), - BodyTxt = {<<"Join the IRC channel in this Jabber ID: ~s">>, - [jid:encode(RoomJID)]}, - Invite = #message{ - from = RoomJID, to = From, - body = xmpp:mk_text(BodyTxt, Lang), - sub_els = [#muc_user{ - invites = [#muc_invite{from = From, - reason = Reason}]}, - #x_conference{reason = Reason, - jid = RoomJID}]}, - ejabberd_router:route(Invite), - xmpp_util:make_adhoc_response( - Request, #adhoc_command{status = completed}); - true -> - Txt = <<"Missing 'channel' or 'server' in the data form">>, - {error, xmpp:err_bad_request(Txt, Lang)} - end. - --spec adhoc_register(binary(), jid(), jid(), adhoc_command()) -> - adhoc_command() | {error, stanza_error()}. -adhoc_register(_ServerHost, _From, _To, - #adhoc_command{action = cancel} = Request) -> - xmpp_util:make_adhoc_response(Request, #adhoc_command{status = canceled}); -adhoc_register(ServerHost, From, To, - #adhoc_command{lang = Lang, xdata = X, - action = Action} = Request) -> - #jid{user = User} = From, - #jid{lserver = Host} = To, - {Username, ConnectionsParams} = - if X == undefined -> - case get_data(ServerHost, Host, From) of - error -> {User, []}; - empty -> {User, []}; - Data -> get_username_and_connection_params(Data) - end; - true -> - {case xmpp_util:get_xdata_values(<<"username">>, X) of - [U] -> U; - _ -> User - end, parse_connections_params(X)} - end, - if Action == complete -> - case set_data(ServerHost, Host, From, - [{username, Username}, - {connections_params, ConnectionsParams}]) of - {atomic, _} -> - xmpp_util:make_adhoc_response( - Request, #adhoc_command{status = completed}); - _ -> - Txt = <<"Database failure">>, - {error, xmpp:err_internal_server_error(Txt, Lang)} - end; - true -> - Form = generate_adhoc_register_form(Lang, Username, - ConnectionsParams), - xmpp_util:make_adhoc_response( - Request, #adhoc_command{ - status = executing, - xdata = Form, - actions = #adhoc_actions{next = true, - complete = true}}) - end. - -generate_adhoc_register_form(Lang, Username, - ConnectionsParams) -> - #xdata{type = form, - title = translate:translate(Lang, <<"IRC settings">>), - instructions = [translate:translate( - Lang, - <<"Enter username and encodings you wish " - "to use for connecting to IRC servers. " - " Press 'Next' to get more fields to " - "fill in. Press 'Complete' to save settings.">>)], - fields = [#xdata_field{ - var = <<"username">>, - type = 'text-single', - label = translate:translate(Lang, <<"IRC username">>), - required = true, - values = [Username]} - | generate_connection_params_fields( - Lang, ConnectionsParams, 1, [])]}. - -generate_connection_params_fields(Lang, [], Number, - Acc) -> - 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 = integer_to_binary(PortUsedInt), - PasswordUsed = case Password of - <<>> -> <<>>; - _ -> Password - end, - NumberString = integer_to_binary(Number), - [#xdata_field{var = <<"password", NumberString/binary>>, - type = 'text-single', - label = str:format( - translate:translate(Lang, <<"Password ~b">>), - [Number]), - values = [PasswordUsed]}, - #xdata_field{var = <<"port", NumberString/binary>>, - type = 'text-single', - label = str:format( - translate:translate(Lang, <<"Port ~b">>), - [Number]), - values = [PortUsed]}, - #xdata_field{var = <<"encoding", NumberString/binary>>, - type = 'list-single', - label = str:format( - translate:translate(Lang, <<"Encoding for server ~b">>), - [Number]), - values = [EncodingUsed], - options = [#xdata_option{label = E, value = E} - || E <- ?POSSIBLE_ENCODINGS]}, - #xdata_field{var = <<"server", NumberString/binary>>, - type = 'text-single', - label = str:format( - translate:translate(Lang, <<"Server ~b">>), - [Number]), - values = [Server]}]. - -parse_connections_params(#xdata{fields = Fields}) -> - Servers = lists:flatmap( - fun(#xdata_field{var = <<"server", Var/binary>>, - values = Values}) -> - [{Var, Values}]; - (_) -> - [] - end, Fields), - Encodings = lists:flatmap( - fun(#xdata_field{var = <<"encoding", Var/binary>>, - values = Values}) -> - [{Var, Values}]; - (_) -> - [] - end, Fields), - Ports = lists:flatmap( - fun(#xdata_field{var = <<"port", Var/binary>>, - values = Values}) -> - [{Var, Values}]; - (_) -> - [] - end, Fields), - Passwords = lists:flatmap( - fun(#xdata_field{var = <<"password", Var/binary>>, - values = Values}) -> - [{Var, Values}]; - (_) -> - [] - 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(JID, Data) -> - lists:map( - fun({username, U}) -> - {username, iolist_to_binary(U)}; - ({connections_params, Params}) -> - {connections_params, - lists:flatmap( - fun(Param) -> - try - [conn_param_to_binary(Param)] - catch _:_ -> - if JID /= error -> - ?ERROR_MSG("failed to convert " - "parameter ~p for user ~s", - [Param, - jid:encode(JID)]); - true -> - ?ERROR_MSG("failed to convert " - "parameter ~p", - [Param]) - end, - [] - end - end, Params)}; - (Opt) -> - Opt - end, Data). - -conn_param_to_binary({S}) -> - {iolist_to_binary(S)}; -conn_param_to_binary({S, E}) -> - {iolist_to_binary(S), iolist_to_binary(E)}; -conn_param_to_binary({S, E, Port}) when is_integer(Port) -> - {iolist_to_binary(S), iolist_to_binary(E), Port}; -conn_param_to_binary({S, E, Port, P}) when is_integer(Port) -> - {iolist_to_binary(S), iolist_to_binary(E), Port, iolist_to_binary(P)}. - -conn_params_to_list(Params) -> - lists:map( - fun({S}) -> - {binary_to_list(S)}; - ({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). - -export(LServer) -> - Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:export(LServer). - -import(LServer) -> - Mod = gen_mod:db_mod(LServer, ?MODULE), - Mod:import(LServer). - -import(LServer, DBType, Data) -> - Mod = gen_mod:db_mod(DBType, ?MODULE), - Mod:import(LServer, Data). - -mod_opt_type(access) -> - fun acl:access_rules_validator/1; -mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end; -mod_opt_type(default_encoding) -> - fun iolist_to_binary/1; -mod_opt_type(name) -> - fun iolist_to_binary/1; -mod_opt_type(host) -> fun iolist_to_binary/1; -mod_opt_type(hosts) -> - fun (L) -> lists:map(fun iolist_to_binary/1, L) end; -mod_opt_type(realname) -> - fun iolist_to_binary/1; -mod_opt_type(webirc_password) -> - fun iolist_to_binary/1. - -mod_options(Host) -> - [{access, all}, - {db_type, ejabberd_config:default_db(Host, ?MODULE)}, - {default_encoding, <<"iso8859-15">>}, - {host, <<"irc.@HOST@">>}, - {hosts, []}, - {realname, <<"WebIRC-User">>}, - {webirc_password, <<"">>}, - {name, ?T("IRC Transport")}]. - --spec extract_ident(stanza()) -> binary(). -extract_ident(Packet) -> - Hdrs = extract_headers(Packet), - proplists:get_value(<<"X-Irc-Ident">>, Hdrs, <<"chatmovil">>). - --spec extract_ip_address(stanza()) -> binary(). -extract_ip_address(Packet) -> - Hdrs = extract_headers(Packet), - proplists:get_value(<<"X-Forwarded-For">>, Hdrs, <<"127.0.0.1">>). - --spec extract_headers(stanza()) -> [{binary(), binary()}]. -extract_headers(Packet) -> - case xmpp:get_subtag(Packet, #shim{}) of - #shim{headers = Hs} -> Hs; - false -> [] - end. diff --git a/src/mod_irc_connection.erl b/src/mod_irc_connection.erl deleted file mode 100644 index a2837f527..000000000 --- a/src/mod_irc_connection.erl +++ /dev/null @@ -1,1205 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : mod_irc_connection.erl -%%% Author : Alexey Shchepin -%%% Purpose : -%%% Created : 15 Feb 2003 by Alexey Shchepin -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc_connection). - --author('alexey@process-one.net'). - --behaviour(p1_fsm). - -%% External exports --export([start_link/12, start/13, 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("logger.hrl"). --include("xmpp.hrl"). - --define(SETS, gb_sets). - --record(state, - {socket :: inet:socket() | undefined, - encoding = <<"">> :: binary(), - port = 0 :: inet:port_number(), - password = <<"">> :: binary(), - user = #jid{} :: jid(), - host = <<"">> :: binary(), - server = <<"">> :: binary(), - remoteAddr = <<"">> :: binary(), - ident = <<"">> :: binary(), - realname = <<"">> :: binary(), - nick = <<"">> :: binary(), - channels = dict:new() :: dict:dict(), - nickchannel :: binary() | undefined, - webirc_password :: binary(), - mod = mod_irc :: atom(), - inbuf = <<"">> :: binary(), - outbuf = <<"">> :: binary()}). - --type state() :: #state{}. - -%-define(DBGFSM, true). - --ifdef(DBGFSM). - --define(FSMOPTS, [{debug, [trace]}]). - --else. - --define(FSMOPTS, []). - -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- --endif. - -start(From, Host, ServerHost, Server, Username, - Encoding, Port, Password, Ident, RemoteAddr, RealName, WebircPassword, Mod) -> - Supervisor = gen_mod:get_module_proc(ServerHost, - ejabberd_mod_irc_sup), - supervisor:start_child(Supervisor, - [From, Host, Server, Username, Encoding, Port, - Password, Ident, RemoteAddr, RealName, WebircPassword, Mod]). - -start_link(From, Host, Server, Username, Encoding, Port, - Password, Ident, RemoteAddr, RealName, WebircPassword, Mod) -> - p1_fsm:start_link(?MODULE, - [From, Host, Server, Username, Encoding, Port, Password, - Ident, RemoteAddr, RealName, WebircPassword, 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, Ident, RemoteAddr, RealName, WebircPassword, Mod]) -> - p1_fsm:send_event(self(), init), - {ok, open_socket, - #state{mod = Mod, - encoding = Encoding, port = Port, password = Password, - user = From, nick = Username, host = Host, - server = Server, ident = Ident, realname = RealName, - remoteAddr = RemoteAddr, webirc_password = WebircPassword }}. - -%%---------------------------------------------------------------------- -%% 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]), - From = StateData#state.user, - #jid{user = JidUser, server = JidServer, resource = JidResource} = From, - UserIP = ejabberd_sm:get_user_ip(JidUser, JidServer, JidResource), - UserIPStr = inet_parse:ntoa(element(1, UserIP)), - 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}, - send_text(NewStateData, - io_lib:format("WEBIRC ~s ~s ~s ~s\r\n", [StateData#state.webirc_password, JidResource, UserIPStr, UserIPStr])), - 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.ident, - StateData#state.nick, - StateData#state.host, - StateData#state.realname])), - {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). - --spec get_password_from_presence(presence()) -> {true, binary()} | false. -get_password_from_presence(#presence{} = Pres) -> - case xmpp:get_subtag(Pres, #muc{}) of - #muc{password = Password} -> - {true, Password}; - _ -> - false - end. - -%%---------------------------------------------------------------------- -%% Func: handle_info/3 -%% Returns: {next_state, NextStateName, NextStateData} | -%% {next_state, NextStateName, NextStateData, Timeout} | -%% {stop, Reason, NewStateData} -%%---------------------------------------------------------------------- -handle_info({route_chan, _, _, #presence{type = error}}, _, StateData) -> - {stop, normal, StateData}; -handle_info({route_chan, Channel, _, #presence{type = unavailable}}, - StateName, StateData) -> - send_stanza_unavailable(Channel, StateData), - S1 = (?SEND((io_lib:format("PART #~s\r\n", [Channel])))), - S2 = S1#state{channels = dict:erase(Channel, S1#state.channels)}, - {next_state, StateName, S2}; -handle_info({route_chan, Channel, Resource, - #presence{type = available} = Presence}, - StateName, StateData) -> - Nick = case Resource of - <<"">> -> StateData#state.nick; - _ -> Resource - end, - S1 = if Nick /= StateData#state.nick -> - S11 = (?SEND((io_lib:format("NICK ~s\r\n", [Nick])))), - S11#state{nickchannel = Channel}; - true -> StateData - end, - {next_state, StateName, - case dict:is_key(Channel, S1#state.channels) of - true -> S1; - _ -> - case get_password_from_presence(Presence) of - {true, Password} -> - S2 = ?SEND((io_lib:format("JOIN #~s ~s\r\n", - [Channel, Password]))); - _ -> - S2 = ?SEND((io_lib:format("JOIN #~s\r\n", [Channel]))) - end, - S2#state{channels = dict:store(Channel, ?SETS:new(), - S1#state.channels)} - end}; -handle_info({route_chan, Channel, _Resource, #message{type = groupchat} = Msg}, - StateName, StateData) -> - {next_state, StateName, - case xmpp:get_text(Msg#message.subject) of - <<"">> -> - ejabberd_router:route( - xmpp:set_from_to( - Msg, - jid:make( - iolist_to_binary([Channel, <<"%">>, StateData#state.server]), - StateData#state.host, - StateData#state.nick), - StateData#state.user)), - Body = xmpp:get_text(Msg#message.body), - case Body of - <<"/quote ", Rest/binary>> -> - ?SEND(<>); - <<"/msg ", Rest/binary>> -> - ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); - <<"/me ", Rest/binary>> -> - Strings = str:tokens(Rest, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format( - "PRIVMSG #~s :\001ACTION ~s\001\r\n", - [Channel, S]) - end, - Strings)), - ?SEND(Res); - <<"/ctcp ", Rest/binary>> -> - Words = str:tokens(Rest, <<" ">>), - case Words of - [CtcpDest | _] -> - CtcpCmd = str:to_upper( - str:substr( - Rest, str:str(Rest, <<" ">>) + 1)), - Res = io_lib:format("PRIVMSG ~s :\001~s\001\r\n", - [CtcpDest, CtcpCmd]), - ?SEND(Res); - _ -> ok - end; - _ -> - Strings = str:tokens(Body, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format("PRIVMSG #~s :~s\r\n", - [Channel, S]) - end, Strings)), - ?SEND(Res) - end; - Subject -> - Strings = str:tokens(Subject, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format("TOPIC #~s :~s\r\n", - [Channel, S]) - end, - Strings)), - ?SEND(Res) - end}; -handle_info({route_chan, _Channel, Resource, #message{type = Type} = Msg}, - StateName, StateData) when Type == chat; Type == normal -> - Body = xmpp:get_text(Msg#message.body), - {next_state, StateName, - case Body of - <<"/quote ", Rest/binary>> -> - ?SEND(<>); - <<"/msg ", Rest/binary>> -> - ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); - <<"/me ", Rest/binary>> -> - Strings = str:tokens(Rest, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format( - "PRIVMSG ~s :\001ACTION ~s\001\r\n", - [Resource, S]) - end, Strings)), - ?SEND(Res); - <<"/ctcp ", Rest/binary>> -> - Words = str:tokens(Rest, <<" ">>), - case Words of - [CtcpDest | _] -> - CtcpCmd = str:to_upper( - str:substr( - Rest, str:str(Rest, <<" ">>) + 1)), - Res = io_lib:format("PRIVMSG ~s :~s\r\n", - [CtcpDest, - <<"\001", CtcpCmd/binary, "\001">>]), - ?SEND(Res); - _ -> ok - end; - _ -> - Strings = str:tokens(Body, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format("PRIVMSG ~s :~s\r\n", - [Resource, S]) - end, Strings)), - ?SEND(Res) - end}; -handle_info({route_chan, _, _, #message{type = error}}, _, StateData) -> - {stop, normal, StateData}; -handle_info({route_chan, Channel, Resource, - #iq{type = T, sub_els = [_]} = Packet}, - StateName, StateData) when T == set; T == get -> - From = StateData#state.user, - To = jid:make(iolist_to_binary([Channel, <<"%">>, StateData#state.server]), - StateData#state.host, StateData#state.nick), - try xmpp:decode_els(Packet) of - #iq{sub_els = [SubEl]} = IQ -> - case xmpp:get_ns(SubEl) of - ?NS_MUC_ADMIN -> - iq_admin(StateData, Channel, From, To, IQ); - ?NS_VERSION -> - Res = io_lib:format("PRIVMSG ~s :\001VERSION\001\r\n", - [Resource]), - _ = (?SEND(Res)), - Err = xmpp:err_feature_not_implemented(), - ejabberd_router:route_error(Packet, Err); - ?NS_TIME -> - Res = io_lib:format("PRIVMSG ~s :\001TIME\001\r\n", - [Resource]), - _ = (?SEND(Res)), - Err = xmpp:err_feature_not_implemented(), - ejabberd_router:route_error(Packet, Err); - ?NS_VCARD -> - Res = io_lib:format("WHOIS ~s \r\n", [Resource]), - _ = (?SEND(Res)), - Err = xmpp:err_feature_not_implemented(), - ejabberd_router:route_error(Packet, Err); - _ -> - Err = xmpp:err_feature_not_implemented(), - ejabberd_router:route_error(Packet, Err) - end - catch _:{xmpp_codec, Why} -> - Err = xmpp:err_bad_request( - xmpp:io_format_error(Why), xmpp:get_lang(Packet)), - ejabberd_router:route_error(Packet, Err) - end, - {next_state, StateName, StateData}; -handle_info({route_chan, _Channel, _, #iq{} = IQ}, StateName, StateData) -> - Err = xmpp:err_feature_not_implemented(), - ejabberd_router:route_error(IQ, Err), - {next_state, StateName, StateData}; -handle_info({route_nick, Nick, #message{type = chat} = Msg}, - StateName, StateData) -> - Body = xmpp:get_text(Msg#message.body), - {next_state, StateName, - case Body of - <<"/quote ", Rest/binary>> -> - ?SEND(<>); - <<"/msg ", Rest/binary>> -> - ?SEND(<<"PRIVMSG ", Rest/binary, "\r\n">>); - <<"/me ", Rest/binary>> -> - Strings = str:tokens(Rest, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format( - "PRIVMSG ~s :\001ACTION ~s\001\r\n", - [Nick, S]) - end, - Strings)), - ?SEND(Res); - <<"/ctcp ", Rest/binary>> -> - Words = str:tokens(Rest, <<" ">>), - case Words of - [CtcpDest | _] -> - CtcpCmd = str:to_upper( - str:substr(Rest, - str:str(Rest, - <<" ">>) - + 1)), - Res = io_lib:format("PRIVMSG ~s :~s\r\n", - [CtcpDest, - <<"\001", - CtcpCmd/binary, - "\001">>]), - ?SEND(Res); - _ -> ok - end; - _ -> - Strings = str:tokens(Body, <<"\n">>), - Res = iolist_to_binary( - lists:map( - fun (S) -> - io_lib:format( - "PRIVMSG ~s :~s\r\n", - [Nick, S]) - end, - Strings)), - ?SEND(Res) - end}; -handle_info({route_nick, _, #message{type = error}}, _, StateData) -> - {stop, normal, StateData}; -handle_info({route_nick, _Nick, _Packet}, StateName, - StateData) -> - {next_state, StateName, StateData}; -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(<< <> - || <> <= 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) -> - p1_fsm:send_event(self(), closed), - {next_state, StateName, StateData}; -handle_info({tcp_error, _Socket, _Reason}, StateName, - StateData) -> - p1_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}; - _ -> - {xmpp:err_internal_server_error( - <<"Server Connect Failed">>, ejabberd_config:get_mylang()), - FullStateData} - end, - (StateData#state.mod):closed_connection(StateData#state.host, - StateData#state.user, - StateData#state.server), - bounce_messages(<<"Server Connect Failed">>), - lists:foreach(fun (Chan) -> - Stanza = xmpp:make_error(#presence{}, 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. - --spec send_stanza(binary(), state(), stanza()) -> ok. -send_stanza(Chan, StateData, Stanza) -> - ejabberd_router:route( - xmpp:set_from_to( - Stanza, - jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, - StateData#state.nick), - StateData#state.user)). - --spec send_stanza_unavailable(binary(), state()) -> ok. -send_stanza_unavailable(Chan, StateData) -> - Affiliation = member, - Role = none, - Stanza = #presence{ - type = unavailable, - sub_els = [#muc_user{ - items = [#muc_item{affiliation = Affiliation, - role = Role}], - status_codes = [110]}]}, - send_stanza(Chan, StateData, Stanza). - -%%%---------------------------------------------------------------------- -%%% 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). - -bounce_messages(Reason) -> - receive - {send_element, El} -> - Lang = xmpp:get_lang(El), - Err = xmpp:err_internal_server_error(Reason, Lang), - ejabberd_router:route_error(El, Err), - 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( - #presence{ - from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, User2), - to = StateData#state.user, - sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation, - role = Role}]}]}), - case catch dict:update(Chan, - fun (Ps) -> (?SETS):add_element(User2, Ps) end, - StateData#state.channels) of - {'EXIT', _} -> StateData; - NS -> StateData#state{channels = NS} - end. - -process_channel_topic(StateData, Chan, String) -> - Msg = ejabberd_regexp:replace(String, <<".*332[^:]*:">>, - <<"">>), - Subject = filter_message(Msg), - Body = <<"Topic for #", Chan/binary, ": ", Subject/binary>>, - ejabberd_router:route( - #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host), - to = StateData#state.user, - type = groupchat, - subject = xmpp:mk_text(Subject), - body = xmpp:mk_text(Body)}). - -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( - #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, <<"">>), - to = StateData#state.user, - type = groupchat, body = xmpp:mk_text(Msg2)}). - -error_nick_in_use(_StateData, String) -> - Msg = ejabberd_regexp:replace(String, - <<".*433 +[^ ]* +">>, <<"">>), - Msg1 = filter_message(Msg), - xmpp:err_conflict(Msg1, ejabberd_config:get_mylang()). - -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( - #presence{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, - StateData#state.nick), - to = StateData#state.user, - type = error, sub_els = [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( - #message{from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), - StateData#state.host, - StateData#state.nick), - to = StateData#state.user, - type = error, sub_els = [Error]}) - end, dict:fetch_keys(StateData#state.channels)), - StateData. - -process_endofwhois(StateData, _String, Nick) -> - ejabberd_router:route( - #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), - StateData#state.host), - to = StateData#state.user, - type = chat, body = xmpp:mk_text(<<"End of WHOIS">>)}). - -process_whois311(StateData, String, Nick, Ident, - Irchost) -> - Fullname = ejabberd_regexp:replace(String, - <<".*311[^:]*:">>, <<"">>), - ejabberd_router:route( - #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), - StateData#state.host, <<"">>), - to = StateData#state.user, - type = chat, - body = xmpp:mk_text( - iolist_to_binary( - [<<"WHOIS: ">>, Nick, <<" is ">>, Ident, - <<"@">>, Irchost, <<" : ">>, Fullname]))}). - -process_whois312(StateData, String, Nick, Ircserver) -> - Ircserverdesc = ejabberd_regexp:replace(String, - <<".*312[^:]*:">>, <<"">>), - ejabberd_router:route( - #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), - StateData#state.host, <<"">>), - to = StateData#state.user, - type = chat, - body = xmpp:mk_text( - iolist_to_binary( - [<<"WHOIS: ">>, Nick, <<" use ">>, Ircserver, - <<" : ">>, Ircserverdesc]))}). - -process_whois319(StateData, String, Nick) -> - Chanlist = ejabberd_regexp:replace(String, - <<".*319[^:]*:">>, <<"">>), - ejabberd_router:route( - #message{from = jid:make(iolist_to_binary([Nick, <<"!">>, StateData#state.server]), - StateData#state.host, <<"">>), - to = StateData#state.user, - type = chat, - body = xmpp:mk_text( - 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( - #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, FromUser), - to = StateData#state.user, - type = groupchat, body = xmpp:mk_text(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( - #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, FromUser), - to = StateData#state.user, - type = groupchat, body = xmpp:mk_text(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( - #message{from = jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]), - StateData#state.host, <<"">>), - to = StateData#state.user, - type = chat, body = xmpp:mk_text(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( - #message{from = jid:make(iolist_to_binary([FromUser, <<"!">>, StateData#state.server]), - StateData#state.host), - to = StateData#state.user, - type = chat, body = xmpp:mk_text(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, ejabberd_config:get_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, - jid:encode(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( - #message{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, FromUser), - to = StateData#state.user, - type = groupchat, - subject = xmpp:mk_text(Msg1), - body = xmpp:mk_text(<<"/me has changed the subject to: ", - Msg1/binary>>)}). - -process_part(StateData, Chan, From, String) -> - [FromUser | FromIdent] = str:tokens(From, <<"!">>), - Msg = ejabberd_regexp:replace(String, - <<".*PART[^:]*:">>, <<"">>), - Msg1 = filter_message(Msg), - ejabberd_router:route( - #presence{from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, FromUser), - to = StateData#state.user, - type = unavailable, - sub_els = [#muc_user{ - items = [#muc_item{affiliation = member, - role = none}]}], - status = xmpp:mk_text( - list_to_binary([Msg1, " (", FromIdent, ")"]))}), - case catch dict:update(Chan, - fun (Ps) -> remove_element(FromUser, Ps) end, - StateData#state.channels) - 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( - #presence{from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), - StateData#state.host, - FromUser), - to = StateData#state.user, - type = unavailable, - sub_els = [#muc_user{ - items = [#muc_item{ - affiliation = member, - role = none}]}], - status = xmpp:mk_text( - list_to_binary([Msg1, " (", FromIdent, ")"]))}), - remove_element(FromUser, Ps); - _ -> - Ps - end - end, StateData#state.channels), - StateData. - -process_join(StateData, Channel, From, _String) -> - [FromUser | FromIdent] = str:tokens(From, <<"!">>), - [Chan | _] = binary:split(Channel, <<":#">>), - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, FromUser), - to = StateData#state.user, - sub_els = [#muc_user{items = [#muc_item{affiliation = member, - role = participant}]}], - status = xmpp:mk_text(list_to_binary(FromIdent))}), - case catch dict:update(Chan, - fun (Ps) -> (?SETS):add_element(FromUser, Ps) end, - StateData#state.channels) - of - {'EXIT', _} -> StateData; - NS -> StateData#state{channels = NS} - end. - -process_mode_o(StateData, Chan, _From, Nick, - Affiliation, Role) -> - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, Nick), - to = StateData#state.user, - sub_els = [#muc_user{items = [#muc_item{affiliation = Affiliation, - role = Role}]}]}). - -process_kick(StateData, Chan, From, Nick, String) -> - Msg = lists:last(str:tokens(String, <<":">>)), - Msg2 = <>, - ejabberd_router:route( - #message{ - from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host), - to = StateData#state.user, - type = groupchat, body = xmpp:mk_text(Msg2)}), - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, <<"%">>, StateData#state.server]), - StateData#state.host, Nick), - to = StateData#state.user, - type = unavailable, - sub_els = [#muc_user{items = [#muc_item{ - affiliation = none, - role = none}], - status_codes = [307]}]}). - -process_nick(StateData, From, NewNick) -> - [FromUser | _] = str:tokens(From, <<"!">>), - [Nick | _] = binary:split(NewNick, <<":">>), - NewChans = - dict:map( - fun(Chan, Ps) -> - case (?SETS):is_member(FromUser, Ps) of - true -> - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), - StateData#state.host, - FromUser), - to = StateData#state.user, - type = unavailable, - sub_els = [#muc_user{ - items = [#muc_item{ - affiliation = member, - role = participant, - nick = Nick}], - status_codes = [303]}]}), - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), - StateData#state.host, Nick), - to = StateData#state.user, - sub_els = [#muc_user{ - items = [#muc_item{ - affiliation = member, - role = participant}]}]}), - (?SETS):add_element(Nick, remove_element(FromUser, Ps)); - _ -> Ps - end - end, StateData#state.channels), - if FromUser == StateData#state.nick -> - StateData#state{nick = Nick, nickchannel = undefined, - channels = NewChans}; - true -> StateData#state{channels = NewChans} - end. - -process_error(StateData, String) -> - lists:foreach( - fun(Chan) -> - ejabberd_router:route( - #presence{ - from = jid:make(iolist_to_binary([Chan, $%, StateData#state.server]), - StateData#state.host, - StateData#state.nick), - to = StateData#state.user, - type = error, - sub_els = [xmpp:err_internal_server_error(String, ejabberd_config:get_mylang())]}) - end, dict:fetch_keys(StateData#state.channels)). - -error_unknown_num(_StateData, String, Type) -> - Msg = ejabberd_regexp:replace(String, - <<".*[45][0-9][0-9] +[^ ]* +">>, <<"">>), - Msg1 = filter_message(Msg), - xmpp:err_undefined_condition(Type, Msg1, ejabberd_config:get_mylang()). - -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, sub_els = [SubEl]} = IQ) -> - try process_iq_admin(StateData, Channel, Type, SubEl) of - {result, Result} -> - ejabberd_router:route(xmpp:make_iq_result(IQ, Result)); - {error, Error} -> - ejabberd_router:route_error(IQ, Error) - catch E:R -> - ?ERROR_MSG("failed to process admin query from ~s: ~p", - [jid:encode(From), {E, {R, erlang:get_stacktrace()}}]), - ejabberd_router:route_error( - IQ, xmpp:err_internal_server_error()) - end. - -process_iq_admin(_StateData, _Channel, set, #muc_admin{items = []}) -> - {error, xmpp:err_bad_request()}; -process_iq_admin(StateData, Channel, set, #muc_admin{items = [Item|_]}) -> - process_admin(StateData, Channel, Item); -process_iq_admin(_StateData, _Channel, _, _SubEl) -> - {error, xmpp:err_feature_not_implemented()}. - -process_admin(_StateData, _Channel, #muc_item{nick = <<"">>}) -> - {error, xmpp:err_feature_not_implemented()}; -process_admin(StateData, Channel, #muc_item{nick = Nick, - reason = Reason, - role = none}) -> - case Reason of - <<"">> -> - send_text(StateData, - 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, undefined}; -process_admin(_StateData, _Channel, _Item) -> - {error, xmpp: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)), - (str:format("~4..0w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Minute, Second])). diff --git a/src/mod_irc_mnesia.erl b/src/mod_irc_mnesia.erl deleted file mode 100644 index f1cdf910d..000000000 --- a/src/mod_irc_mnesia.erl +++ /dev/null @@ -1,81 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : mod_irc_mnesia.erl -%%% Author : Evgeny Khramtsov -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc_mnesia). - --behaviour(mod_irc). - -%% API --export([init/2, get_data/3, set_data/4, import/2]). --export([need_transform/1, transform/1]). - --include("jid.hrl"). --include("mod_irc.hrl"). --include("logger.hrl"). - -%%%=================================================================== -%%% API -%%%=================================================================== -init(_Host, _Opts) -> - ejabberd_mnesia:create(?MODULE, irc_custom, - [{disc_copies, [node()]}, - {attributes, record_info(fields, irc_custom)}]). - -get_data(_LServer, Host, From) -> - {U, S, _} = jid:tolower(From), - case catch mnesia:dirty_read({irc_custom, {{U, S}, Host}}) of - {'EXIT', _Reason} -> error; - [] -> empty; - [#irc_custom{data = Data}] -> Data - end. - -set_data(_LServer, Host, From, Data) -> - {U, S, _} = jid:tolower(From), - F = fun () -> - mnesia:write(#irc_custom{us_host = {{U, S}, Host}, - data = Data}) - end, - mnesia:transaction(F). - -import(_LServer, #irc_custom{} = R) -> - mnesia:dirty_write(R). - -need_transform(#irc_custom{us_host = {{U, S}, H}}) - when is_list(U) orelse is_list(S) orelse is_list(H) -> - ?INFO_MSG("Mnesia table 'irc_custom' will be converted to binary", []), - true; -need_transform(_) -> - false. - -transform(#irc_custom{us_host = {{U, S}, H}, - data = Data} = R) -> - JID = jid:make(U, S), - R#irc_custom{us_host = {{iolist_to_binary(U), - iolist_to_binary(S)}, - iolist_to_binary(H)}, - data = mod_irc:data_to_binary(JID, Data)}. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== diff --git a/src/mod_irc_riak.erl b/src/mod_irc_riak.erl deleted file mode 100644 index 31d09abf1..000000000 --- a/src/mod_irc_riak.erl +++ /dev/null @@ -1,65 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : mod_irc_riak.erl -%%% Author : Evgeny Khramtsov -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc_riak). - --behaviour(mod_irc). - -%% API --export([init/2, get_data/3, set_data/4, import/2]). - --include("jid.hrl"). --include("mod_irc.hrl"). - -%%%=================================================================== -%%% API -%%%=================================================================== -init(_Host, _Opts) -> - ok. - -get_data(_LServer, Host, From) -> - {U, S, _} = jid:tolower(From), - case ejabberd_riak:get(irc_custom, irc_custom_schema(), {{U, S}, Host}) of - {ok, #irc_custom{data = Data}} -> - Data; - {error, notfound} -> - empty; - _Err -> - error - end. - -set_data(_LServer, Host, From, Data) -> - {U, S, _} = jid:tolower(From), - {atomic, ejabberd_riak:put(#irc_custom{us_host = {{U, S}, Host}, - data = Data}, - irc_custom_schema())}. - -import(_LServer, #irc_custom{} = R) -> - ejabberd_riak:put(R, irc_custom_schema()). - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== -irc_custom_schema() -> - {record_info(fields, irc_custom), #irc_custom{}}. diff --git a/src/mod_irc_sql.erl b/src/mod_irc_sql.erl deleted file mode 100644 index 9dcdceaba..000000000 --- a/src/mod_irc_sql.erl +++ /dev/null @@ -1,106 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : mod_irc_sql.erl -%%% Author : Evgeny Khramtsov -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2018 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., -%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -%%% -%%%---------------------------------------------------------------------- - --module(mod_irc_sql). - --compile([{parse_transform, ejabberd_sql_pt}]). - --behaviour(mod_irc). - -%% API --export([init/2, get_data/3, set_data/4, import/1, import/2, export/1]). - --include("jid.hrl"). --include("mod_irc.hrl"). --include("ejabberd_sql_pt.hrl"). - -%%%=================================================================== -%%% API -%%%=================================================================== -init(_Host, _Opts) -> - ok. - -get_data(LServer, Host, From) -> - SJID = jid:encode(jid:tolower(jid:remove_resource(From))), - case catch ejabberd_sql:sql_query( - LServer, - ?SQL("select @(data)s from irc_custom" - " where jid=%(SJID)s and host=%(Host)s and %(LServer)H")) of - {selected, [{SData}]} -> - mod_irc:data_to_binary(From, ejabberd_sql:decode_term(SData)); - {'EXIT', _} -> error; - {selected, _} -> empty - end. - -set_data(LServer, Host, From, Data) -> - SJID = jid:encode(jid:tolower(jid:remove_resource(From))), - SData = misc:term_to_expr(Data), - F = fun () -> - ?SQL_UPSERT_T( - "irc_custom", - ["!jid=%(SJID)s", - "!host=%(Host)s", - "server_host=%(LServer)s", - "data=%(SData)s"]), - ok - end, - ejabberd_sql:sql_transaction(LServer, F). - -export(_Server) -> - [{irc_custom, - fun(Host, #irc_custom{us_host = {{U, S}, IRCHost}, - data = Data}) -> - case str:suffix(Host, IRCHost) of - true -> - SJID = jid:encode(jid:make(U, S)), - LServer = ejabberd_router:host_of_route(IRCHost), - SData = misc:term_to_expr(Data), - [?SQL("delete from irc_custom" - " where jid=%(SJID)s and host=%(IRCHost)s and %(LServer)H;"), - ?SQL_INSERT( - "irc_custom", - ["jid=%(SJID)s", - "host=%(Host)s", - "server_host=%(LServer)s", - "data=%(SData)s"])]; - false -> - [] - end - end}]. - -import(_LServer) -> - [{<<"select jid, host, data from irc_custom;">>, - fun([SJID, IRCHost, SData]) -> - #jid{luser = U, lserver = S} = jid:decode(SJID), - Data = ejabberd_sql:decode_term(SData), - #irc_custom{us_host = {{U, S}, IRCHost}, - data = Data} - end}]. - -import(_, _) -> - pass. - -%%%=================================================================== -%%% Internal functions -%%%=================================================================== diff --git a/test/elixir-config/ejabberd_logger.exs b/test/elixir-config/ejabberd_logger.exs index d13f79aa6..594909289 100644 --- a/test/elixir-config/ejabberd_logger.exs +++ b/test/elixir-config/ejabberd_logger.exs @@ -24,7 +24,7 @@ defmodule Ejabberd.Config.EjabberdLoggerTest do test "outputs correctly when attr is not supported" do error_msg = "[ WARN ] Annotation @attr_not_supported is not supported.\n" - [_mod_irc, _mod_configure, mod_time] = Store.get(:modules) + [_mod_configure, mod_time] = Store.get(:modules) fun = fn -> mod_time |> Validation.validate @@ -37,7 +37,7 @@ defmodule Ejabberd.Config.EjabberdLoggerTest do test "outputs correctly when dependency is not found" do error_msg = "[ WARN ] Module :mod_adhoc was not found, but is required as a dependency.\n" - [_mod_irc, mod_configure, _mod_time] = Store.get(:modules) + [mod_configure, _mod_time] = Store.get(:modules) fun = fn -> mod_configure |> Validation.validate diff --git a/test/elixir-config/shared/ejabberd_for_validation.exs b/test/elixir-config/shared/ejabberd_for_validation.exs index 8c0196c7e..e47d925a9 100644 --- a/test/elixir-config/shared/ejabberd_for_validation.exs +++ b/test/elixir-config/shared/ejabberd_for_validation.exs @@ -15,6 +15,4 @@ defmodule Ejabberd.ConfigFile do @dependency [:mod_adhoc] end - module :mod_irc do - end end diff --git a/test/elixir-config/validation_test.exs b/test/elixir-config/validation_test.exs index 1df775966..ca94d2705 100644 --- a/test/elixir-config/validation_test.exs +++ b/test/elixir-config/validation_test.exs @@ -19,7 +19,7 @@ defmodule Ejabberd.Config.ValidationTest do end test "validates correctly the modules" do - [mod_irc, mod_configure, mod_time] = Store.get(:modules) + [mod_configure, mod_time] = Store.get(:modules) [{:error, _mod, errors}] = Validation.validate(mod_configure) assert %{dependency: [mod_adhoc: :not_found]} == errors @@ -27,6 +27,5 @@ defmodule Ejabberd.Config.ValidationTest do [{:error, _mod, errors}] = Validation.validate(mod_time) assert %{attribute: [{{:attr_not_supported, true}, :attr_not_supported}]} == errors - [{:ok, _mod}] = Validation.validate(mod_irc) end end diff --git a/win32/ejabberd.cfg b/win32/ejabberd.cfg index 7ba191be0..a42b849d5 100644 --- a/win32/ejabberd.cfg +++ b/win32/ejabberd.cfg @@ -146,7 +146,6 @@ {mod_offline, []}, {mod_announce, [{access, announce}]}, {mod_private, []}, - {mod_irc, []}, % Default options for mod_muc: % host: "conference." ++ ?MYNAME % access: all -- 2.40.0