);
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid);
+
+CREATE TABLE carboncopy (
+ username text NOT NULL,
+ resource text NOT NULL,
+ namespace text NOT NULL,
+ node text NOT NULL
+);
+
+CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username, resource);
+CREATE INDEX i_carboncopy_user ON carboncopy (username);
[server_host] [varchar] (255) NOT NULL,\r
[node] [varchar] (255) NOT NULL,\r
[pid] [varchar](100) NOT NULL,\r
- [local_hint] text NOT NULL\r
+ [local_hint] [text] NOT NULL\r
);\r
\r
CREATE UNIQUE CLUSTERED INDEX [route_i] ON [route] (domain, server_host, node, pid)\r
[sid] ASC\r
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)\r
) TEXTIMAGE_ON [PRIMARY];\r
+\r
+CREATE TABLE [dbo].[carboncopy] (\r
+ [username] [varchar] (255) NOT NULL,\r
+ [resource] [varchar] (255) NOT NULL,\r
+ [namespace] [varchar] (255) NOT NULL,\r
+ [node] [varchar] (255) NOT NULL\r
+);\r
+\r
+CREATE UNIQUE CLUSTERED INDEX [carboncopy_ur] ON [carboncopy] (username, resource)\r
+WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);\r
+\r
+CREATE INDEX [carboncopy_user] ON [carboncopy] (username)\r
+WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON);\r
) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75));
+
+CREATE TABLE carboncopy (
+ username text NOT NULL,
+ resource text NOT NULL,
+ namespace text NOT NULL,
+ node text NOT NULL
+) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
+
+CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy (username(75), resource(75));
+CREATE INDEX i_carboncopy_user ON carboncopy (username(75));
);
CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid);
+
+CREATE TABLE carboncopy (
+ username text NOT NULL,
+ resource text NOT NULL,
+ namespace text NOT NULL,
+ node text NOT NULL
+);
+
+CREATE UNIQUE INDEX i_carboncopy_ur ON carboncopy USING btree (username, resource);
+CREATE INDEX i_carboncopy_user ON carboncopy USING btree (username);
start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts,fun gen_iq_handler:check_type/1, one_queue),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
- Mod = gen_mod:db_mod(Host, ?MODULE),
+ Mod = gen_mod:ram_db_mod(Host, ?MODULE),
Mod:init(Host, Opts),
ejabberd_hooks:add(unset_presence_hook,Host, ?MODULE, remove_connection, 10),
%% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90)
ejabberd_hooks:delete(unset_presence_hook,Host, ?MODULE, remove_connection, 10).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:ram_db_mod(Host, NewOpts, ?MODULE),
+ OldMod = gen_mod:ram_db_mod(Host, OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
-spec enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
enable(Host, U, R, CC)->
?DEBUG("enabling for ~p", [U]),
- Mod = gen_mod:db_mod(Host, ?MODULE),
+ Mod = gen_mod:ram_db_mod(Host, ?MODULE),
Mod:enable(U, Host, R, CC).
-spec disable(binary(), binary(), binary()) -> ok | {error, any()}.
disable(Host, U, R)->
?DEBUG("disabling for ~p", [U]),
- Mod = gen_mod:db_mod(Host, ?MODULE),
+ Mod = gen_mod:ram_db_mod(Host, ?MODULE),
Mod:disable(U, Host, R).
-spec complete_packet(jid(), message(), direction()) -> message().
-spec list(binary(), binary()) -> [{binary(), binary()}].
%% list {resource, cc_version} with carbons enabled for given user and host
list(User, Server) ->
- Mod = gen_mod:db_mod(Server, ?MODULE),
+ Mod = gen_mod:ram_db_mod(Server, ?MODULE),
Mod:list(User, Server).
depends(_Host, _Opts) ->
[].
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(_) -> [db_type, iqdisc].
+mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+mod_opt_type(_) -> [ram_db_type, iqdisc].
--- /dev/null
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% Created : 29 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2017 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_carboncopy_sql).
+-behaviour(mod_carboncopy).
+
+-compile([{parse_transform, ejabberd_sql_pt}]).
+
+%% API
+-export([init/2, enable/4, disable/3, list/2]).
+
+-include("ejabberd.hrl").
+-include("logger.hrl").
+-include("ejabberd_sql_pt.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(Host, _Opts) ->
+ clean_table(Host).
+
+enable(LUser, LServer, LResource, NS) ->
+ NodeS = erlang:atom_to_binary(node(), latin1),
+ case ?SQL_UPSERT(LServer, "carboncopy",
+ ["!username=%(LUser)s",
+ "!resource=%(LResource)s",
+ "namespace=%(NS)s",
+ "node=%(NodeS)s"]) of
+ ok ->
+ ok;
+ Err ->
+ ?ERROR_MSG("failed to update 'carboncopy' table: ~p", [Err]),
+ Err
+ end.
+
+disable(LUser, LServer, LResource) ->
+ case ejabberd_sql:sql_query(
+ LServer,
+ ?SQL("delete from carboncopy where username=%(LUser)s "
+ "and resource=%(LResource)s")) of
+ {updated, _} ->
+ ok;
+ Err ->
+ ?ERROR_MSG("failed to delete from 'carboncopy' table: ~p", [Err]),
+ Err
+ end.
+
+list(LUser, LServer) ->
+ case ejabberd_sql:sql_query(
+ LServer,
+ ?SQL("select @(resource)s, @(namespace)s from carboncopy "
+ "where username=%(LUser)s")) of
+ {selected, Rows} ->
+ Rows;
+ Err ->
+ ?ERROR_MSG("failed to select from 'carboncopy' table: ~p", [Err]),
+ []
+ end.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+clean_table(LServer) ->
+ NodeS = erlang:atom_to_binary(node(), latin1),
+ ?INFO_MSG("Cleaning SQL 'carboncopy' table...", []),
+ case ejabberd_sql:sql_query(
+ LServer,
+ ?SQL("delete from carboncopy where node=%(NodeS)s")) of
+ {updated, _} ->
+ ok;
+ Err ->
+ ?ERROR_MSG("failed to clean 'carboncopy' table: ~p", [Err]),
+ Err
+ end.
carbons_tests:master_slave_cases(),
csi_tests:master_slave_cases()];
db_tests(_) ->
- %% No support for carboncopy
[{single_user, [sequence],
[test_register,
legacy_auth_tests(),
mam_tests:master_slave_cases(),
mix_tests:master_slave_cases(),
vcard_tests:master_slave_cases(),
- announce_tests:master_slave_cases()].
+ announce_tests:master_slave_cases(),
+ carbons_tests:master_slave_cases()].
ldap_tests() ->
[{ldap_tests, [sequence],
db_type: sql
mod_vcard_xupdate:
db_type: sql
+ mod_carboncopy:
+ ram_db_type: sql
mod_adhoc: []
mod_configure: []
mod_disco: []
db_type: sql
mod_vcard_xupdate:
db_type: sql
+ mod_carboncopy:
+ ram_db_type: sql
mod_adhoc: []
mod_configure: []
mod_disco: []
db_type: sql
mod_vcard_xupdate:
db_type: sql
+ mod_carboncopy:
+ ram_db_type: sql
mod_adhoc: []
mod_configure: []
mod_disco: []
db_type: internal
mod_vcard_xupdate:
db_type: internal
- mod_carboncopy: []
+ mod_carboncopy:
+ ram_db_type: internal
mod_client_state:
queue_presence: true
queue_chat_states: true
db_type: internal
mod_vcard_xupdate:
db_type: internal
- mod_carboncopy: []
+ mod_carboncopy:
+ ram_db_type: internal
mod_client_state:
queue_presence: true
queue_chat_states: true