aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Dockerfile2
-rw-r--r--README5
-rw-r--r--configure.ac4
-rw-r--r--mix.exs2
-rw-r--r--mix.lock20
-rw-r--r--rebar.config3
-rw-r--r--src/ejabberd_auth.erl4
-rw-r--r--src/ejabberd_auth_mnesia.erl2
-rw-r--r--src/ejabberd_http_ws.erl2
-rw-r--r--src/ejabberd_logger.erl3
-rw-r--r--src/ejabberd_s2s.erl30
-rw-r--r--src/ejabberd_sm.erl26
-rw-r--r--src/ejd2sql.erl5
-rw-r--r--src/ext_mod.erl68
-rw-r--r--src/mod_http_api.erl12
-rw-r--r--src/mod_http_fileserver.erl16
-rw-r--r--src/mod_mam.erl3
-rw-r--r--src/mod_muc.erl3
-rw-r--r--src/mod_muc_riak.erl2
-rw-r--r--src/mod_muc_room.erl2
-rw-r--r--src/mod_pubsub.erl41
-rw-r--r--src/mod_roster.erl8
-rw-r--r--src/node_pep.erl2
-rw-r--r--src/node_pep_sql.erl2
-rw-r--r--src/pubsub_db_sql.erl94
-rw-r--r--test/ejabberd_SUITE_data/ejabberd.yml8
-rwxr-xr-xtest/ejabberd_SUITE_data/extauth.py26
-rw-r--r--test/muc_tests.erl29
28 files changed, 314 insertions, 110 deletions
diff --git a/Dockerfile b/Dockerfile
index 6de6f5783..e5f3d78f3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
FROM debian:jessie-slim
MAINTAINER Rafael Römhild <rafael@roemhild.de>
-ENV EJABBERD_BRANCH=17.04 \
+ENV EJABBERD_BRANCH=17.08 \
EJABBERD_USER=ejabberd \
EJABBERD_HTTPS=true \
EJABBERD_STARTTLS=true \
diff --git a/README b/README
index 67e062267..87b684f14 100644
--- a/README
+++ b/README
@@ -116,11 +116,14 @@ To compile ejabberd you need:
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
+install the development packages also.
### 1. Compile and install on *nix systems
To compile ejabberd, execute the following commands. The first one is only
-necessary if your source tree didn't come with a `configure` script.
+necessary if your source tree didn't come with a `configure` script (In this
+case you need autoconf installed).
./autogen.sh
./configure
diff --git a/configure.ac b/configure.ac
index 884db5d4e..edf54722c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,8 +3,8 @@
AC_PREREQ(2.53)
AC_INIT(ejabberd, m4_esyscmd([echo `git describe --tags 2>/dev/null || echo 0.0` | sed 's/-g.*//;s/-/./' | tr -d '\012']), [ejabberd@process-one.net], [ejabberd])
-REQUIRE_ERLANG_MIN="6.1 (Erlang/OTP 17.1)"
-REQUIRE_ERLANG_MAX="9.0.0 (No Max)"
+REQUIRE_ERLANG_MIN="6.4 (Erlang/OTP 17.5)"
+REQUIRE_ERLANG_MAX="100.0.0 (No Max)"
AC_CONFIG_MACRO_DIR([m4])
diff --git a/mix.exs b/mix.exs
index 025552f45..24b74f87c 100644
--- a/mix.exs
+++ b/mix.exs
@@ -3,7 +3,7 @@ defmodule Ejabberd.Mixfile do
def project do
[app: :ejabberd,
- version: "17.6.0",
+ version: "17.8.0",
description: description(),
elixir: "~> 1.4",
elixirc_paths: ["lib"],
diff --git a/mix.lock b/mix.lock
index 40eda1930..4b67809e5 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,18 +1,22 @@
-%{"cache_tab": {:hex, :cache_tab, "1.0.8", "eac8923f0f20c35e630317790c4d4c2629c5bc792753fa48eb5391bd39c80245", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
- "distillery": {:hex, :distillery, "1.4.0", "d633cd322c8efa0428082b00b7f902daf8caa166d45f9022bbc19a896d2e1e56", [:mix], []},
- "earmark": {:hex, :earmark, "1.2.2", "f718159d6b65068e8daeef709ccddae5f7fdc770707d82e7d126f584cd925b74", [:mix], []},
- "esip": {:hex, :esip, "1.0.12", "e0505afe74bb362b0ea486e2a64b3c1934b1eb541a7b3e990b23045e4bdc07d4", [:rebar3], [{:fast_tls, "1.0.12", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stun, "1.0.11", [hex: :stun, optional: false]}]},
- "ex_doc": {:hex, :ex_doc, "0.16.1", "b4b8a23602b4ce0e9a5a960a81260d1f7b29635b9652c67e95b0c2f7ccee5e81", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
+%{"cache_tab": {:hex, :cache_tab, "1.0.10", "dd6aba8951ba15cab4ad483d997f8eefdb0cb00225971d0629c730d107a2bed6", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
+ "distillery": {:hex, :distillery, "1.4.1", "546d851bf27ae8fe0727e10e4fc4e146ad836eecee138263a60431e688044ed3", [:mix], []},
+ "earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [:mix], []},
+ "eredis": {:hex, :eredis, "1.0.8", "ab4fda1c4ba7fbe6c19c26c249dc13da916d762502c4b4fa2df401a8d51c5364", [:rebar], []},
+ "esip": {:hex, :esip, "1.0.15", "82c8b0178618c10b1ac9690841d94025c982d63f8cd6c8f8bf920cf33e301658", [:rebar3], [{:fast_tls, "1.0.15", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stun, "1.0.14", [hex: :stun, optional: false]}]},
+ "ex_doc": {:hex, :ex_doc, "0.16.2", "3b3e210ebcd85a7c76b4e73f85c5640c011d2a0b2f06dcdf5acdb2ae904e5084", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, optional: false]}]},
"ezlib": {:hex, :ezlib, "1.0.2", "22004ecf553a7d831404394d5642712e2aede90522e22bd6ccc089ca410ee098", [:rebar3], []},
- "fast_tls": {:hex, :fast_tls, "1.0.12", "861b591f23103142782c5b72de8898673a37acd78646c50dbda978e1e1c5b463", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
+ "fast_tls": {:hex, :fast_tls, "1.0.15", "96546e6a8b8384fbbcddf435c4c42cf2c0a3dc1858c3c9c2e62a74ae1ddd526a", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
"fast_xml": {:hex, :fast_xml, "1.1.23", "1e7b311d3353806ee832d7630fef57713987cea40a7020669cf057d537de4721", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
"fast_yaml": {:hex, :fast_yaml, "1.0.10", "ce5d52b77cb21968c8b73aa29b39f56a4ffd7e1e11f853d5597e7277858f155e", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
"goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], []},
"iconv": {:hex, :iconv, "1.0.5", "ae871aa11c854695db37e48fd5e5583b02e106126fbdf21bb53448f5a47c092b", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
"jiffy": {:hex, :jiffy, "0.14.11", "919a87d491c5a6b5e3bbc27fafedc3a0761ca0b4c405394f121f582fd4e3f0e5", [:rebar3], []},
"lager": {:hex, :lager, "3.4.2", "150b9a17b23ae6d3265cc10dc360747621cf217b7a22b8cddf03b2909dbf7aa5", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, optional: false]}]},
+ "p1_mysql": {:hex, :p1_mysql, "1.0.3", "e2cc26f2e8d17c3885a9c2fee3ff64fcac5915896f50ab6f6aa9b0da1eed341c", [:rebar3], []},
"p1_oauth2": {:hex, :p1_oauth2, "0.6.1", "4e021250cc198c538b097393671a41e7cebf463c248980320e038fe0316eb56b", [:rebar3], []},
+ "p1_pgsql": {:hex, :p1_pgsql, "1.1.3", "ce94c83e9605c88d5f541b8f4b49edff3dc2bbacd1b6409c4cad0fbf7bef2ac4", [:rebar3], []},
"p1_utils": {:hex, :p1_utils, "1.0.9", "c33c230efbeb4dcc02911161e3cb1a93231a92df15e3fc97de655a9271a26d9f", [:rebar3], []},
+ "sqlite3": {:hex, :sqlite3, "1.1.5", "794738b6d07b6d36ec6d42492cb9d629bad9cf3761617b8b8d728e765db19840", [:rebar3], []},
"stringprep": {:hex, :stringprep, "1.0.9", "9182ba39931cd1db528b8883cad0d63530abe2bf21835d26cec2f9af8bc00be0", [:rebar3], [{:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
- "stun": {:hex, :stun, "1.0.11", "386cb3e3543e17a6351028a43e047c2172225d035c826a72fcb67672da9874e5", [:rebar3], [{:fast_tls, "1.0.12", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
- "xmpp": {:hex, :xmpp, "1.1.11", "8c49964d0d48b81080d2c5700fcf6cc19950ae9dc60a71bd3ff3d4620336d052", [:rebar3], [{:fast_xml, "1.1.23", [hex: :fast_xml, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stringprep, "1.0.9", [hex: :stringprep, optional: false]}]}}
+ "stun": {:hex, :stun, "1.0.14", "6dc2080c25a72f7087301dc7333c1ea7d27ea4d88efaa379fc2b5924f3b17006", [:rebar3], [{:fast_tls, "1.0.15", [hex: :fast_tls, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}]},
+ "xmpp": {:hex, :xmpp, "1.1.14", "e186f5208e7a448a4af784a8d2cb87cefe99dd49b24623e25d38115b23a50e12", [:rebar3], [{:fast_xml, "1.1.23", [hex: :fast_xml, optional: false]}, {:p1_utils, "1.0.9", [hex: :p1_utils, optional: false]}, {:stringprep, "1.0.9", [hex: :stringprep, optional: false]}]}}
diff --git a/rebar.config b/rebar.config
index 58a8e80f4..160185511 100644
--- a/rebar.config
+++ b/rebar.config
@@ -28,7 +28,7 @@
{xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.1.14"}}},
{fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.10"}}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
- {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.1"}}},
+ {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.2"}}},
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.2"}}},
{if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.14"}}}},
{if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.15"}}}},
@@ -71,6 +71,7 @@
p1_utils,
p1_mysql,
p1_pgsql,
+ p1_oauth2,
epam,
ezlib,
iconv]}}.
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 251e36ff7..b34925ff0 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -261,12 +261,12 @@ try_register(User, Server, Password) ->
ok;
(Mod, _) ->
db_try_register(
- User, Server, Password, Mod)
+ LUser, LServer, Password, Mod)
end, {error, not_allowed},
auth_modules(LServer)) of
ok ->
ejabberd_hooks:run(
- register_user, Server, [User, Server]);
+ register_user, LServer, [LUser, LServer]);
{error, _} = Err ->
Err
end;
diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl
index 9c2152578..690152674 100644
--- a/src/ejabberd_auth_mnesia.erl
+++ b/src/ejabberd_auth_mnesia.erl
@@ -208,6 +208,8 @@ remove_user(User, Server) ->
{error, db_failure}
end.
+need_transform(#reg_users_counter{}) ->
+ false;
need_transform(#passwd{us = {U, S}, password = Pass}) ->
if is_binary(Pass) ->
case store_type(S) of
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index f4a73cc39..2c44d6552 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -241,6 +241,7 @@ handle_info(PingPong, StateName, StateData) when PingPong == ping orelse
StateData2#state{pong_expected = false}};
handle_info({timeout, Timer, _}, _StateName,
#state{timer = Timer} = StateData) ->
+ ?DEBUG("Closing websocket connection from hitting inactivity timeout", []),
{stop, normal, StateData};
handle_info({timeout, Timer, _}, StateName,
#state{ping_timer = Timer, ws = {_, WsPid}} = StateData) ->
@@ -253,6 +254,7 @@ handle_info({timeout, Timer, _}, StateName,
{next_state, StateName,
StateData#state{ping_timer = PingTimer, pong_expected = true}};
true ->
+ ?DEBUG("Closing websocket connection from missing pongs", []),
{stop, normal, StateData}
end;
handle_info(_, StateName, StateData) ->
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index 1e48732c8..eee9d3b83 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -151,6 +151,9 @@ do_start() ->
application:set_env(lager, crash_log_size, LogRotateSize),
application:set_env(lager, crash_log_count, LogRotateCount),
ejabberd:start_app(lager),
+ lists:foreach(fun(Handler) ->
+ lager:set_loghwm(Handler, LogRateLimit)
+ end, gen_event:which_handlers(lager_event)),
ok.
%% @spec () -> ok
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
index 4b74b8c4a..a0e9411cf 100644
--- a/src/ejabberd_s2s.erl
+++ b/src/ejabberd_s2s.erl
@@ -546,25 +546,23 @@ parent_domains(Domain) ->
get_commands_spec() ->
[#ejabberd_commands{
- name = incoming_s2s_number,
- tags = [stats, s2s],
+ name = incoming_s2s_number, tags = [stats, s2s],
desc = "Number of incoming s2s connections on the node",
- policy = admin,
- module = ?MODULE, function = incoming_s2s_number,
- args = [], result = {s2s_incoming, integer}},
+ policy = admin,
+ module = ?MODULE, function = incoming_s2s_number,
+ args = [], result = {s2s_incoming, integer}},
#ejabberd_commands{
- name = outgoing_s2s_number,
- tags = [stats, s2s],
+ name = outgoing_s2s_number, tags = [stats, s2s],
desc = "Number of outgoing s2s connections on the node",
- policy = admin,
- module = ?MODULE, function = outgoing_s2s_number,
- args = [], result = {s2s_outgoing, integer}},
- #ejabberd_commands{name = stop_all_connections,
- tags = [s2s],
- desc = "Stop all outgoing and incoming connections",
- policy = admin,
- module = ?MODULE, function = stop_all_connections,
- args = [], result = {res, rescode}}].
+ policy = admin,
+ module = ?MODULE, function = outgoing_s2s_number,
+ args = [], result = {s2s_outgoing, integer}},
+ #ejabberd_commands{
+ name = stop_all_connections, tags = [s2s],
+ desc = "Stop all outgoing and incoming connections",
+ policy = admin,
+ module = ?MODULE, function = stop_all_connections,
+ args = [], result = {res, rescode}}].
%% TODO Move those stats commands to ejabberd stats command ?
incoming_s2s_number() ->
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index 302cfded4..f7f7447bf 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -975,30 +975,36 @@ cache_nodes(Mod, LServer) ->
%%% ejabberd commands
get_commands_spec() ->
- [#ejabberd_commands{name = connected_users,
- tags = [session],
+ [#ejabberd_commands{name = connected_users, tags = [session],
desc = "List all established sessions",
policy = admin,
module = ?MODULE, function = connected_users, args = [],
+ result_desc = "List of users sessions",
+ result_example = [<<"user1@example.com">>, <<"user2@example.com">>],
result = {connected_users, {list, {sessions, string}}}},
- #ejabberd_commands{name = connected_users_number,
- tags = [session, stats],
+ #ejabberd_commands{name = connected_users_number, tags = [session, stats],
desc = "Get the number of established sessions",
policy = admin,
module = ?MODULE, function = connected_users_number,
+ result_example = 2,
args = [], result = {num_sessions, integer}},
- #ejabberd_commands{name = user_resources,
- tags = [session],
+ #ejabberd_commands{name = user_resources, tags = [session],
desc = "List user's connected resources",
- policy = user,
+ policy = admin,
module = ?MODULE, function = user_resources,
- args = [],
+ args = [{user, binary}, {host, binary}],
+ args_desc = ["User name", "Server name"],
+ args_example = [<<"user1">>, <<"example.com">>],
+ result_example = [<<"tka1">>, <<"Gajim">>, <<"mobile-app">>],
result = {resources, {list, {resource, string}}}},
- #ejabberd_commands{name = kick_user,
- tags = [session],
+ #ejabberd_commands{name = kick_user, tags = [session],
desc = "Disconnect user's active sessions",
module = ?MODULE, function = kick_user,
args = [{user, binary}, {host, binary}],
+ args_desc = ["User name", "Server name"],
+ args_example = [<<"user1">>, <<"example.com">>],
+ result_desc = "Number of resources that were kicked",
+ result_example = 3,
result = {num_resources, integer}}].
-spec connected_users() -> [binary()].
diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl
index c4f00a551..c801eb973 100644
--- a/src/ejd2sql.erl
+++ b/src/ejd2sql.erl
@@ -58,6 +58,7 @@ modules() ->
mod_offline,
mod_privacy,
mod_private,
+ mod_pubsub,
mod_roster,
mod_shared_roster,
mod_vcard].
@@ -80,8 +81,8 @@ export(Server, Output, Module) ->
case export(LServer, Table, IO, ConvertFun) of
{atomic, ok} -> ok;
{aborted, Reason} ->
- ?ERROR_MSG("Failed export for module ~p: ~p",
- [Module, Reason])
+ ?ERROR_MSG("Failed export for module ~p and table ~p: ~p",
+ [Module, Table, Reason])
end
end, Module:export(Server)),
close_output(Output, IO).
diff --git a/src/ext_mod.erl b/src/ext_mod.erl
index 86bd21983..ecdfa04c3 100644
--- a/src/ext_mod.erl
+++ b/src/ext_mod.erl
@@ -80,16 +80,18 @@ code_change(_OldVsn, State, _Extra) ->
get_commands_spec() ->
[#ejabberd_commands{name = modules_update_specs,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "Update the module source code from Git",
+ longdesc = "A connection to Internet is required",
module = ?MODULE, function = update,
args = [],
result = {res, rescode}},
#ejabberd_commands{name = modules_available,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "List the contributed modules available to install",
module = ?MODULE, function = available_command,
+ result_desc = "List of tuples with module name and description",
+ result_example = [{mod_cron, "Execute scheduled commands"},
+ {mod_rest, "ReST frontend"}],
args = [],
result = {modules, {list,
{module, {tuple,
@@ -97,9 +99,11 @@ get_commands_spec() ->
{summary, string}]}}}}},
#ejabberd_commands{name = modules_installed,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "List the contributed modules already installed",
module = ?MODULE, function = installed_command,
+ result_desc = "List of tuples with module name and description",
+ result_example = [{mod_cron, "Execute scheduled commands"},
+ {mod_rest, "ReST frontend"}],
args = [],
result = {modules, {list,
{module, {tuple,
@@ -107,46 +111,64 @@ get_commands_spec() ->
{summary, string}]}}}}},
#ejabberd_commands{name = module_install,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "Compile, install and start an available contributed module",
module = ?MODULE, function = install,
+ args_desc = ["Module name"],
+ args_example = [<<"mod_rest">>],
args = [{module, binary}],
result = {res, rescode}},
#ejabberd_commands{name = module_uninstall,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "Uninstall a contributed module",
module = ?MODULE, function = uninstall,
+ args_desc = ["Module name"],
+ args_example = [<<"mod_rest">>],
args = [{module, binary}],
result = {res, rescode}},
#ejabberd_commands{name = module_upgrade,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "Upgrade the running code of an installed module",
+ longdesc = "In practice, this uninstalls and installs the module",
module = ?MODULE, function = upgrade,
+ args_desc = ["Module name"],
+ args_example = [<<"mod_rest">>],
args = [{module, binary}],
result = {res, rescode}},
#ejabberd_commands{name = module_check,
tags = [admin,modules],
- desc = "",
- longdesc = "",
+ desc = "Check the contributed module repository compliance",
module = ?MODULE, function = check,
+ args_desc = ["Module name"],
+ args_example = [<<"mod_rest">>],
args = [{module, binary}],
result = {res, rescode}}
].
%% -- public modules functions
update() ->
- add_sources(?REPOS),
+ Contrib = maps:put(?REPOS, [], maps:new()),
+ Jungles = lists:foldl(fun({Package, Spec}, Acc) ->
+ Repo = proplists:get_value(url, Spec, ""),
+ Mods = maps:get(Repo, Acc, []),
+ maps:put(Repo, [Package|Mods], Acc)
+ end, Contrib, modules_spec(sources_dir(), "*/*")),
+ Repos = maps:fold(fun(Repo, _Mods, Acc) ->
+ Update = add_sources(Repo),
+ ?INFO_MSG("Update packages from repo ~s: ~p", [Repo, Update]),
+ case Update of
+ ok -> Acc;
+ Error -> [{repository, Repo, Error}|Acc]
+ end
+ end, [], Jungles),
Res = lists:foldl(fun({Package, Spec}, Acc) ->
- Path = proplists:get_value(url, Spec, ""),
- Update = add_sources(Package, Path),
+ Repo = proplists:get_value(url, Spec, ""),
+ Update = add_sources(Package, Repo),
?INFO_MSG("Update package ~s: ~p", [Package, Update]),
case Update of
ok -> Acc;
- Error -> [Error|Acc]
+ Error -> [{Package, Repo, Error}|Acc]
end
- end, [], modules_spec(sources_dir(), "*")),
+ end, Repos, modules_spec(sources_dir(), "*")),
case Res of
[] -> ok;
[Error|_] -> Error
@@ -538,7 +560,9 @@ compile_result(Results) ->
compile_options() ->
[verbose, report_errors, report_warnings]
++ [{i, filename:join(app_dir(App), "include")}
- || App <- [fast_xml, xmpp, p1_utils, ejabberd]].
+ || App <- [fast_xml, xmpp, p1_utils, ejabberd]]
+ ++ [{i, filename:join(mod_dir(Mod), "include")}
+ || Mod <- installed()].
app_dir(App) ->
case code:lib_dir(App) of
@@ -553,6 +577,10 @@ app_dir(App) ->
Dir
end.
+mod_dir({Package, Spec}) ->
+ Default = filename:join(modules_dir(), Package),
+ proplists:get_value(path, Spec, Default).
+
compile_erlang_file(Dest, File) ->
compile_erlang_file(Dest, File, compile_options()).
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl
index 7be05dbf1..ef881d14b 100644
--- a/src/mod_http_api.erl
+++ b/src/mod_http_api.erl
@@ -538,9 +538,17 @@ json_error(HTTPCode, JSONCode, Message) ->
log(Call, Args, {Addr, Port}) ->
AddrS = misc:ip_to_list({Addr, Port}),
- ?INFO_MSG("API call ~s ~p from ~s:~p", [Call, Args, AddrS, Port]);
+ ?INFO_MSG("API call ~s ~p from ~s:~p", [Call, hide_sensitive_args(Args), AddrS, Port]);
log(Call, Args, IP) ->
- ?INFO_MSG("API call ~s ~p (~p)", [Call, Args, IP]).
+ ?INFO_MSG("API call ~s ~p (~p)", [Call, hide_sensitive_args(Args), IP]).
+
+hide_sensitive_args(Args=[_H|_T]) ->
+ lists:map( fun({<<"password">>, Password}) -> {<<"password">>, ejabberd_config:may_hide_data(Password)};
+ ({<<"newpass">>,NewPassword}) -> {<<"newpass">>, ejabberd_config:may_hide_data(NewPassword)};
+ (E) -> E end,
+ Args);
+hide_sensitive_args(NonListArgs) ->
+ NonListArgs.
permission_addon() ->
Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access, none),
diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl
index c2144042e..4e3cfd08b 100644
--- a/src/mod_http_fileserver.erl
+++ b/src/mod_http_fileserver.erl
@@ -59,8 +59,13 @@
-define(HTTP_ERR_FILE_NOT_FOUND,
{-1, 404, [], <<"Not found">>}).
+-define(REQUEST_AUTH_HEADERS,
+ [{<<"WWW-Authenticate">>, <<"Basic realm=\"ejabberd\"">>}]).
+
-define(HTTP_ERR_FORBIDDEN,
{-1, 403, [], <<"Forbidden">>}).
+-define(HTTP_ERR_REQUEST_AUTH,
+ {-1, 401, ?REQUEST_AUTH_HEADERS, <<"Unauthorized">>}).
-define(DEFAULT_CONTENT_TYPE,
<<"application/octet-stream">>).
@@ -317,12 +322,17 @@ serve(LocalPath, Auth, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentT
false
end,
case CanProceed of
+ false ->
+ ?HTTP_ERR_REQUEST_AUTH;
true ->
FileName = filename:join(filename:split(DocRoot) ++ LocalPath),
case file:read_file_info(FileName) of
- {error, enoent} -> ?HTTP_ERR_FILE_NOT_FOUND;
- {error, enotdir} -> ?HTTP_ERR_FILE_NOT_FOUND;
- {error, eacces} -> ?HTTP_ERR_FORBIDDEN;
+ {error, enoent} ->
+ ?HTTP_ERR_FILE_NOT_FOUND;
+ {error, enotdir} ->
+ ?HTTP_ERR_FILE_NOT_FOUND;
+ {error, eacces} ->
+ ?HTTP_ERR_FORBIDDEN;
{ok, #file_info{type = directory}} -> serve_index(FileName,
DirectoryIndices,
CustomHeaders,
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index 30dd7dd57..674cefc05 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -999,6 +999,9 @@ get_commands_spec() ->
longdesc = "Valid message TYPEs: "
"\"chat\", \"groupchat\", \"all\".",
module = ?MODULE, function = delete_old_messages,
+ args_desc = ["Type of messages to delete (chat, groupchat, all)",
+ "Days to keep messages"],
+ args_example = [<<"all">>, 31],
args = [{type, binary}, {days, integer}],
result = {res, rescode}}].
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index 69fc2d0dc..8b6d7b8b9 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -166,7 +166,7 @@ restore_room(ServerHost, Host, Name) ->
forget_room(ServerHost, Host, Name) ->
LServer = jid:nameprep(ServerHost),
- ejabberd_hooks:run(remove_room, LServer, [LServer, Name, Host]),
+ ejabberd_hooks:run(destroy_room, LServer, [LServer, Name, Host]),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:forget_room(LServer, Host, Name).
@@ -256,6 +256,7 @@ handle_call({create, Room, From, Nick, Opts}, _From,
Nick, NewOpts, QueueType),
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
RMod:register_online_room(ServerHost, Room, Host, Pid),
+ ejabberd_hooks:run(create_room, ServerHost, [ServerHost, Room, Host]),
{reply, ok, State}.
handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{host = OldHost}) ->
diff --git a/src/mod_muc_riak.erl b/src/mod_muc_riak.erl
index 8dd1eddf8..42e644fdd 100644
--- a/src/mod_muc_riak.erl
+++ b/src/mod_muc_riak.erl
@@ -60,7 +60,7 @@ restore_room(_LServer, Host, Name) ->
forget_room(_LServer, Host, Name) ->
{atomic, ejabberd_riak:delete(muc_room, {Name, Host})}.
-can_use_nick(LServer, Host, JID, Nick) ->
+can_use_nick(_LServer, Host, JID, Nick) ->
{LUser, LServer, _} = jid:tolower(JID),
LUS = {LUser, LServer},
case ejabberd_riak:get_by_index(muc_registered,
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index ec1cffd6a..31dbbbfa7 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -4004,6 +4004,7 @@ tab_add_online_user(JID, StateData) ->
Room = StateData#state.room,
Host = StateData#state.host,
ServerHost = StateData#state.server_host,
+ ejabberd_hooks:run(join_room, ServerHost, [ServerHost, Room, Host, JID]),
mod_muc:register_online_user(ServerHost, jid:tolower(JID), Room, Host).
-spec tab_remove_online_user(jid(), state()) -> any().
@@ -4011,6 +4012,7 @@ tab_remove_online_user(JID, StateData) ->
Room = StateData#state.room,
Host = StateData#state.host,
ServerHost = StateData#state.server_host,
+ ejabberd_hooks:run(leave_room, ServerHost, [ServerHost, Room, Host, JID]),
mod_muc:unregister_online_user(ServerHost, jid:tolower(JID), Room, Host).
-spec tab_count_user(jid(), state()) -> non_neg_integer().
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 1a620cb6b..d682eb5f3 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -89,7 +89,7 @@
%% API and gen_server callbacks
-export([start/2, stop/1, init/1,
handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3, depends/2]).
+ terminate/2, code_change/3, depends/2, export/1]).
-export([send_loop/1, mod_opt_type/1]).
@@ -546,13 +546,11 @@ disco_identity(Host, Node, From) ->
case get_allowed_items_call(Host, Nidx, From, Type,
Options, Owners) of
{result, _} ->
- {result, [#identity{category = <<"pubsub">>,
- type = <<"pep">>},
- #identity{category = <<"pubsub">>,
- type = <<"leaf">>,
+ {result, [#identity{category = <<"pubsub">>, type = <<"pep">>},
+ #identity{category = <<"pubsub">>, type = <<"leaf">>,
name = case get_option(Options, title) of
false -> <<>>;
- [Title] -> Title
+ Title -> Title
end}]};
_ ->
{result, []}
@@ -586,8 +584,7 @@ disco_features(Host, Node, From) ->
Type, Options, Owners) of
{result, _} ->
{result,
- [?NS_PUBSUB |
- [feature(F) || F <- plugin_features(Host, <<"pep">>)]]};
+ [?NS_PUBSUB | [feature(F) || F <- plugin_features(Host, <<"pep">>)]]};
_ ->
{result, []}
end
@@ -620,7 +617,7 @@ disco_items(Host, <<>>, From) ->
jid = jid:make(Host),
name = case get_option(Options, title) of
false -> <<>>;
- [Title] -> Title
+ Title -> Title
end} | Acc];
_ ->
Acc
@@ -679,16 +676,14 @@ caps_update(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = Host} =
presence(Host, {presence, U, S, [R], JID}).
-spec presence_probe(jid(), jid(), pid()) -> ok.
-presence_probe(#jid{luser = U, lserver = S, lresource = R} = JID, JID, Pid) ->
- presence(S, {presence, JID, Pid}),
- presence(S, {presence, U, S, [R], JID});
presence_probe(#jid{luser = U, lserver = S}, #jid{luser = U, lserver = S}, _Pid) ->
%% ignore presence_probe from my other ressources
%% to not get duplicated last items
ok;
-presence_probe(#jid{luser = U, lserver = S, lresource = R}, #jid{lserver = S} = JID, _Pid) ->
- presence(S, {presence, U, S, [R], JID});
-presence_probe(_Host, _JID, _Pid) ->
+presence_probe(#jid{lserver = S} = From, #jid{lserver = S} = To, Pid) ->
+ presence(S, {presence, From, Pid}),
+ presence(S, {presence, From#jid.luser, S, [From#jid.lresource], To});
+presence_probe(_From, _To, _Pid) ->
%% ignore presence_probe from remote contacts,
%% those are handled via caps_add
ok.
@@ -1723,7 +1718,7 @@ delete_node(Host, Node, Owner) ->
%%<li>The node does not support subscriptions.</li>
%%<li>The node does not exist.</li>
%%</ul>
--spec subscribe_node(host(), binary(), jid(), binary(), [{binary(), [binary()]}]) ->
+-spec subscribe_node(host(), binary(), jid(), jid(), [{binary(), [binary()]}]) ->
{result, pubsub()} | {error, stanza_error()}.
subscribe_node(Host, Node, From, JID, Configuration) ->
SubModule = subscription_plugin(Host),
@@ -2152,9 +2147,14 @@ get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) ->
node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, undefined, RSM]).
get_last_items(Host, Type, Nidx, LJID, Count) ->
- case node_action(Host, Type, get_last_items, [Nidx, LJID, Count]) of
- {result, Items} -> Items;
- _ -> []
+ case get_cached_item(Host, Nidx) of
+ undefined ->
+ case node_action(Host, Type, get_last_items, [Nidx, LJID, Count]) of
+ {result, Items} -> Items;
+ _ -> []
+ end;
+ LastItem ->
+ [LastItem]
end.
%% @doc <p>Resend the items of a node to the user.</p>
@@ -3871,6 +3871,9 @@ purge_offline(Host, LJID, Node) ->
Error
end.
+export(Server) ->
+ pubsub_db_sql:export(Server).
+
mod_opt_type(access_createnode) -> fun acl:access_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(host) -> fun iolist_to_binary/1;
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 28bc06171..cb9b0f7bf 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -35,7 +35,6 @@
-module(mod_roster).
-protocol({xep, 237, '1.3'}).
--protocol({xep, 321, '0.1'}).
-author('alexey@process-one.net').
@@ -444,11 +443,10 @@ decode_item(Item, R, Managed) ->
process_iq_set(#iq{from = From, to = To,
sub_els = [#roster_query{items = [QueryItem]}]} = IQ) ->
#jid{user = User, luser = LUser, lserver = LServer} = To,
- Managed = {From#jid.luser, From#jid.lserver} /= {LUser, LServer},
LJID = jid:tolower(QueryItem#roster_item.jid),
F = fun () ->
Item = get_roster_item(LUser, LServer, LJID),
- Item2 = decode_item(QueryItem, Item, Managed),
+ Item2 = decode_item(QueryItem, Item, false),
Item3 = ejabberd_hooks:run_fold(roster_process_item,
LServer, Item2,
[LServer]),
@@ -1200,8 +1198,6 @@ 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(iqdisc) -> fun gen_iq_handler:check_type/1;
-mod_opt_type(managers) ->
- fun (B) when is_list(B) -> B end;
mod_opt_type(store_current_id) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(versioning) ->
@@ -1213,5 +1209,5 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
mod_opt_type(O) when O == use_cache; O == cache_missed ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(_) ->
- [access, db_type, iqdisc, managers, store_current_id,
+ [access, db_type, iqdisc, store_current_id,
versioning, cache_life_time, cache_size, use_cache, cache_missed].
diff --git a/src/node_pep.erl b/src/node_pep.erl
index 6dd7a68c4..cc0dd41fb 100644
--- a/src/node_pep.erl
+++ b/src/node_pep.erl
@@ -119,7 +119,7 @@ create_node(Nidx, Owner) ->
delete_node(Nodes) ->
{result, {_, _, Result}} = node_flat:delete_node(Nodes),
- {result, {[], Result}}.
+ {result, {default, Result}}.
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl
index 68a2ce946..b84c945bd 100644
--- a/src/node_pep_sql.erl
+++ b/src/node_pep_sql.erl
@@ -73,7 +73,7 @@ create_node(Nidx, Owner) ->
delete_node(Nodes) ->
{result, {_, _, Result}} = node_flat_sql:delete_node(Nodes),
- {result, {[], Result}}.
+ {result, {default, Result}}.
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl
index ae7a9fde6..ae28184db 100644
--- a/src/pubsub_db_sql.erl
+++ b/src/pubsub_db_sql.erl
@@ -25,12 +25,16 @@
-module(pubsub_db_sql).
+-compile([{parse_transform, ejabberd_sql_pt}]).
+
-author("pablo.polvorin@process-one.net").
-include("pubsub.hrl").
+-include("ejabberd_sql_pt.hrl").
-export([add_subscription/1, read_subscription/1,
delete_subscription/1, update_subscription/1]).
+-export([export/1]).
%% TODO: Those -spec lines produce errors in old Erlang versions.
%% They can be enabled again in ejabberd 3.0 because it uses R12B or higher.
@@ -139,3 +143,93 @@ sql_to_integer(N) -> binary_to_integer(N).
sql_to_boolean(B) -> B == <<"1">>.
sql_to_timestamp(T) -> xmpp_util:decode_timestamp(T).
+
+%% REVIEW:
+%% * this code takes NODEID from Itemid2, and forgets about Nodeidx
+%% * this code assumes Payload only contains one xmlelement()
+%% * PUBLISHER is taken from Creation
+export(_Server) ->
+ [{pubsub_item,
+ fun(_Host, #pubsub_item{itemid = {Itemid1, NODEID},
+ %nodeidx = _Nodeidx,
+ creation = {{C1, C2, C3}, Cusr},
+ modification = {{M1, M2, M3}, _Musr},
+ payload = Payload}) ->
+ ITEMID = ejabberd_sql:escape(Itemid1),
+ CREATION = ejabberd_sql:escape(list_to_binary(
+ string:join([string:right(integer_to_list(I),6,$0)||I<-[C1,C2,C3]],":"))),
+ MODIFICATION = ejabberd_sql:escape(list_to_binary(
+ string:join([string:right(integer_to_list(I),6,$0)||I<-[M1,M2,M3]],":"))),
+ PUBLISHER = ejabberd_sql:escape(jid:encode(Cusr)),
+ [PayloadEl] = [El || {xmlel,_,_,_} = El <- Payload],
+ PAYLOAD = ejabberd_sql:escape(fxml:element_to_binary(PayloadEl)),
+ [?SQL("delete from pubsub_item where itemid=%(ITEMID)s;"),
+ ?SQL("insert into pubsub_item(itemid,nodeid,creation,modification,publisher,payload) \n"
+ " values (%(ITEMID)s, %(NODEID)d, %(CREATION)s,
+ %(MODIFICATION)s, %(PUBLISHER)s, %(PAYLOAD)s);")];
+ (_Host, _R) ->
+ []
+ end},
+%% REVIEW:
+%% * From the mnesia table, the #pubsub_state.items is not used in ODBC
+%% * Right now AFFILIATION is the first letter of Affiliation
+%% * Right now SUBSCRIPTIONS expects only one Subscription
+%% * Right now SUBSCRIPTIONS letter is the first letter of Subscription
+ {pubsub_state,
+ fun(_Host, #pubsub_state{stateid = {Jid, Stateid},
+ %nodeidx = Nodeidx,
+ items = _Items,
+ affiliation = Affiliation,
+ subscriptions = Subscriptions}) ->
+ STATEID = list_to_binary(integer_to_list(Stateid)),
+ JID = ejabberd_sql:escape(jid:encode(Jid)),
+ NODEID = <<"unknown">>, %% TODO: integer_to_list(Nodeidx),
+ AFFILIATION = list_to_binary(string:substr(atom_to_list(Affiliation),1,1)),
+ SUBSCRIPTIONS = list_to_binary(parse_subscriptions(Subscriptions)),
+ [?SQL("delete from pubsub_state where stateid=%(STATEID)s;"),
+ ?SQL("insert into pubsub_state(stateid,jid,nodeid,affiliation,subscriptions)\n"
+ " values (%(STATEID)s, %(JID)s, %(NODEID)s, %(AFFILIATION)s, %(SUBSCRIPTIONS)s);")];
+ (_Host, _R) ->
+ []
+ end},
+
+%% REVIEW:
+%% * Parents is not migrated to PARENTs
+%% * Probably some option VALs are not correctly represented in mysql
+ {pubsub_node,
+ fun(_Host, #pubsub_node{nodeid = {Hostid, Nodeid},
+ id = Id,
+ parents = _Parents,
+ type = Type,
+ owners = Owners,
+ options = Options}) ->
+ HOST = case Hostid of
+ {U,S,R} -> ejabberd_sql:escape(jid:encode({U,S,R}));
+ _ -> ejabberd_sql:escape(Hostid)
+ end,
+ NODE = ejabberd_sql:escape(Nodeid),
+ PARENT = <<"">>,
+ IdB = integer_to_binary(Id),
+ TYPE = ejabberd_sql:escape(<<Type/binary, "_odbc">>),
+ [?SQL("delete from pubsub_node where nodeid=%(Id)d;"),
+ ?SQL("insert into pubsub_node(host,node,nodeid,parent,type) \n"
+ " values (%(HOST)s, %(NODE)s, %(Id)d, %(PARENT)s, %(TYPE)s);"),
+ ?SQL("delete from pubsub_node_option where nodeid=%(Id)d;"),
+ [["insert into pubsub_node_option(nodeid,name,val)\n"
+ " values (", IdB, ", '", atom_to_list(Name), "', '",
+ io_lib:format("~p", [Val]), "');\n"] || {Name,Val} <- Options],
+ ?SQL("delete from pubsub_node_owner where nodeid=%(Id)d;"),
+ [["insert into pubsub_node_owner(nodeid,owner)\n"
+ " values (", IdB, ", '", jid:encode(Usr), "');\n"] || Usr <- Owners],"\n"];
+ (_Host, _R) ->
+ []
+ end}].
+
+parse_subscriptions([]) ->
+ "";
+parse_subscriptions([{State, Item}]) ->
+ STATE = case State of
+ subscribed -> "s"
+ end,
+ string:join([STATE, Item],":").
+
diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml
index 74cabf584..a648cb422 100644
--- a/test/ejabberd_SUITE_data/ejabberd.yml
+++ b/test/ejabberd_SUITE_data/ejabberd.yml
@@ -458,6 +458,8 @@ listen:
port: @@web_port@@
module: ejabberd_http
captcha: true
+ request_handlers:
+ "/api": mod_http_api
-
port: @@component_port@@
module: ejabberd_service
@@ -474,6 +476,7 @@ modules:
mod_proxy65: []
mod_legacy: []
mod_muc: []
+ mod_muc_admin: []
mod_register:
welcome_message:
subject: "Welcome!"
@@ -496,3 +499,8 @@ outgoing_s2s_port: @@s2s_port@@
shaper:
fast: 50000
normal: 10000
+
+api_permissions:
+ "public commands":
+ who: all
+ what: "*"
diff --git a/test/ejabberd_SUITE_data/extauth.py b/test/ejabberd_SUITE_data/extauth.py
index fa2c9efd0..263d6464e 100755
--- a/test/ejabberd_SUITE_data/extauth.py
+++ b/test/ejabberd_SUITE_data/extauth.py
@@ -3,23 +3,27 @@ import struct
def read():
(pkt_size,) = struct.unpack('>H', sys.stdin.read(2))
- pkt = sys.stdin.read(pkt_size).split(':')
- cmd = pkt[0]
- args_num = len(pkt) - 1
- if cmd == 'auth' and args_num >= 3:
- if pkt[1] == "wrong":
+ pkt = sys.stdin.read(pkt_size)
+ cmd = pkt.split(':')[0]
+ if cmd == 'auth':
+ u, s, p = pkt.split(':', 3)[1:]
+ if u == "wrong":
write(False)
else:
write(True)
- elif cmd == 'isuser' and args_num == 2:
+ elif cmd == 'isuser':
+ u, s = pkt.split(':', 2)[1:]
+ elif cmd == 'setpass':
+ u, s, p = pkt.split(':', 3)[1:]
write(True)
- elif cmd == 'setpass' and args_num >= 3:
+ elif cmd == 'tryregister':
+ u, s, p = pkt.split(':', 3)[1:]
write(True)
- elif cmd == 'tryregister' and args_num >= 3:
+ elif cmd == 'removeuser':
+ u, s = pkt.split(':', 2)[1:]
write(True)
- elif cmd == 'removeuser' and args_num == 2:
- write(True)
- elif cmd == 'removeuser3' and args_num >= 3:
+ elif cmd == 'removeuser3':
+ u, s, p = pkt.split(':', 3)[1:]
write(True)
else:
write(False)
diff --git a/test/muc_tests.erl b/test/muc_tests.erl
index 9ded2e0fe..bcceb6938 100644
--- a/test/muc_tests.erl
+++ b/test/muc_tests.erl
@@ -53,7 +53,8 @@ single_cases() ->
single_test(service_vcard),
single_test(configure_non_existent),
single_test(cancel_configure_non_existent),
- single_test(service_subscriptions)]}.
+ single_test(service_subscriptions),
+ single_test(set_room_affiliation)]}.
service_presence_error(Config) ->
Service = muc_jid(Config),
@@ -242,6 +243,32 @@ service_subscriptions(Config) ->
end, Rooms),
disconnect(Config).
+set_room_affiliation(Config) ->
+ #jid{server = RoomService} = muc_jid(Config),
+ RoomName = <<"set_room_affiliation">>,
+ RoomJID = jid:make(RoomName, RoomService),
+ MyJID = my_jid(Config),
+ PeerJID = jid:remove_resource(?config(slave, Config)),
+
+ ct:pal("joining room ~p", [RoomJID]),
+ ok = join_new(Config, RoomJID),
+
+ ct:pal("setting affiliation in room ~p to 'member' for ~p", [RoomJID, PeerJID]),
+ ServerHost = ?config(server_host, Config),
+ WebPort = ct:get_config(web_port, 5280),
+ RequestURL = "http://" ++ ServerHost ++ ":" ++ integer_to_list(WebPort) ++ "/api/set_room_affiliation",
+ Headers = [{"X-Admin", "true"}],
+ ContentType = "application/json",
+ Body = jiffy:encode(#{name => RoomName, service => RoomService, jid => jid:encode(PeerJID), affiliation => member}),
+ {ok, {{_, 200, _}, _, _}} = httpc:request(post, {RequestURL, Headers, ContentType, Body}, [], []),
+
+ #message{id = _, from = RoomJID, to = MyJID, sub_els = [
+ #muc_user{items = [
+ #muc_item{affiliation = member, role = none, jid = PeerJID}]}]} = recv_message(Config),
+
+ ok = leave(Config, RoomJID),
+ disconnect(Config).
+
%%%===================================================================
%%% Master-slave tests
%%%===================================================================