aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/acl.erl2
-rw-r--r--src/ejabberd.erl2
-rw-r--r--src/ejabberd_access_permissions.erl2
-rw-r--r--src/ejabberd_acme.erl148
-rw-r--r--src/ejabberd_admin.erl2
-rw-r--r--src/ejabberd_app.erl5
-rw-r--r--src/ejabberd_auth.erl46
-rw-r--r--src/ejabberd_auth_anonymous.erl2
-rw-r--r--src/ejabberd_auth_external.erl2
-rw-r--r--src/ejabberd_auth_ldap.erl2
-rw-r--r--src/ejabberd_auth_mnesia.erl2
-rw-r--r--src/ejabberd_auth_pam.erl2
-rw-r--r--src/ejabberd_auth_riak.erl2
-rw-r--r--src/ejabberd_auth_sql.erl30
-rw-r--r--src/ejabberd_backend_sup.erl2
-rw-r--r--src/ejabberd_bosh.erl2
-rw-r--r--src/ejabberd_c2s.erl16
-rw-r--r--src/ejabberd_c2s_config.erl2
-rw-r--r--src/ejabberd_captcha.erl9
-rw-r--r--src/ejabberd_cluster.erl2
-rw-r--r--src/ejabberd_cluster_mnesia.erl2
-rw-r--r--src/ejabberd_commands.erl3
-rw-r--r--src/ejabberd_commands_doc.erl2
-rw-r--r--src/ejabberd_config.erl10
-rw-r--r--src/ejabberd_ctl.erl9
-rw-r--r--src/ejabberd_hooks.erl17
-rw-r--r--src/ejabberd_http.erl52
-rw-r--r--src/ejabberd_http_ws.erl2
-rw-r--r--src/ejabberd_iq.erl2
-rw-r--r--src/ejabberd_listener.erl60
-rw-r--r--src/ejabberd_local.erl8
-rw-r--r--src/ejabberd_logger.erl2
-rw-r--r--src/ejabberd_mnesia.erl7
-rw-r--r--src/ejabberd_oauth.erl2
-rw-r--r--src/ejabberd_oauth_mnesia.erl2
-rw-r--r--src/ejabberd_oauth_rest.erl2
-rw-r--r--src/ejabberd_oauth_sql.erl2
-rw-r--r--src/ejabberd_piefxis.erl18
-rw-r--r--src/ejabberd_pkix.erl2
-rw-r--r--src/ejabberd_rdbms.erl2
-rw-r--r--src/ejabberd_redis.erl7
-rw-r--r--src/ejabberd_redis_sup.erl2
-rw-r--r--src/ejabberd_regexp.erl2
-rw-r--r--src/ejabberd_riak.erl2
-rw-r--r--src/ejabberd_riak_sup.erl2
-rw-r--r--src/ejabberd_router.erl8
-rw-r--r--src/ejabberd_router_mnesia.erl2
-rw-r--r--src/ejabberd_router_multicast.erl2
-rw-r--r--src/ejabberd_router_redis.erl2
-rw-r--r--src/ejabberd_router_riak.erl2
-rw-r--r--src/ejabberd_router_sql.erl8
-rw-r--r--src/ejabberd_s2s.erl11
-rw-r--r--src/ejabberd_s2s_in.erl14
-rw-r--r--src/ejabberd_s2s_out.erl2
-rw-r--r--src/ejabberd_service.erl2
-rw-r--r--src/ejabberd_shaper.erl2
-rw-r--r--src/ejabberd_sip.erl2
-rw-r--r--src/ejabberd_sm.erl212
-rw-r--r--src/ejabberd_sm_mnesia.erl2
-rw-r--r--src/ejabberd_sm_redis.erl2
-rw-r--r--src/ejabberd_sm_riak.erl2
-rw-r--r--src/ejabberd_sm_sql.erl2
-rw-r--r--src/ejabberd_sql.erl70
-rw-r--r--src/ejabberd_sql_pt.erl29
-rw-r--r--src/ejabberd_sql_sup.erl2
-rw-r--r--src/ejabberd_stun.erl2
-rw-r--r--src/ejabberd_sup.erl2
-rw-r--r--src/ejabberd_system_monitor.erl2
-rw-r--r--src/ejabberd_tmp_sup.erl2
-rw-r--r--src/ejabberd_update.erl2
-rw-r--r--src/ejabberd_web.erl2
-rw-r--r--src/ejabberd_web_admin.erl6
-rw-r--r--src/ejabberd_websocket.erl2
-rw-r--r--src/ejabberd_xmlrpc.erl2
-rw-r--r--src/ejd2sql.erl2
-rw-r--r--src/eldap_filter.erl2
-rw-r--r--src/eldap_pool.erl2
-rw-r--r--src/eldap_utils.erl2
-rw-r--r--src/elixir_logger_backend.erl2
-rw-r--r--src/ext_mod.erl2
-rw-r--r--src/extauth.erl2
-rw-r--r--src/extauth_sup.erl2
-rw-r--r--src/gen_iq_handler.erl8
-rw-r--r--src/gen_mod.erl11
-rw-r--r--src/gen_pubsub_node.erl2
-rw-r--r--src/gen_pubsub_nodetree.erl2
-rw-r--r--src/jd2ejd.erl5
-rw-r--r--src/misc.erl2
-rw-r--r--src/mod_adhoc.erl2
-rw-r--r--src/mod_admin_extra.erl12
-rw-r--r--src/mod_admin_update_sql.erl2
-rw-r--r--src/mod_announce.erl2
-rw-r--r--src/mod_announce_mnesia.erl2
-rw-r--r--src/mod_announce_riak.erl2
-rw-r--r--src/mod_announce_sql.erl2
-rw-r--r--src/mod_avatar.erl2
-rw-r--r--src/mod_block_strangers.erl24
-rw-r--r--src/mod_blocking.erl2
-rw-r--r--src/mod_bosh.erl2
-rw-r--r--src/mod_bosh_mnesia.erl2
-rw-r--r--src/mod_bosh_redis.erl2
-rw-r--r--src/mod_bosh_riak.erl2
-rw-r--r--src/mod_bosh_sql.erl2
-rw-r--r--src/mod_caps.erl2
-rw-r--r--src/mod_caps_mnesia.erl2
-rw-r--r--src/mod_caps_riak.erl2
-rw-r--r--src/mod_caps_sql.erl2
-rw-r--r--src/mod_carboncopy.erl228
-rw-r--r--src/mod_carboncopy_mnesia.erl79
-rw-r--r--src/mod_carboncopy_redis.erl176
-rw-r--r--src/mod_carboncopy_riak.erl82
-rw-r--r--src/mod_carboncopy_sql.erl91
-rw-r--r--src/mod_client_state.erl2
-rw-r--r--src/mod_configure.erl2
-rw-r--r--src/mod_delegation.erl2
-rw-r--r--src/mod_disco.erl2
-rw-r--r--src/mod_echo.erl2
-rw-r--r--src/mod_fail2ban.erl53
-rw-r--r--src/mod_http_api.erl39
-rw-r--r--src/mod_http_fileserver.erl2
-rw-r--r--src/mod_http_upload.erl2
-rw-r--r--src/mod_http_upload_quota.erl2
-rw-r--r--src/mod_last.erl2
-rw-r--r--src/mod_last_mnesia.erl2
-rw-r--r--src/mod_last_riak.erl2
-rw-r--r--src/mod_last_sql.erl2
-rw-r--r--src/mod_legacy_auth.erl2
-rw-r--r--src/mod_mam.erl221
-rw-r--r--src/mod_mam_mnesia.erl15
-rw-r--r--src/mod_mam_sql.erl23
-rw-r--r--src/mod_metrics.erl2
-rw-r--r--src/mod_mix.erl323
-rw-r--r--src/mod_muc.erl2
-rw-r--r--src/mod_muc_admin.erl27
-rw-r--r--src/mod_muc_log.erl4
-rw-r--r--src/mod_muc_mnesia.erl2
-rw-r--r--src/mod_muc_riak.erl2
-rw-r--r--src/mod_muc_room.erl835
-rw-r--r--src/mod_muc_sql.erl2
-rw-r--r--src/mod_multicast.erl2
-rw-r--r--src/mod_offline.erl43
-rw-r--r--src/mod_offline_mnesia.erl2
-rw-r--r--src/mod_offline_riak.erl2
-rw-r--r--src/mod_offline_sql.erl15
-rw-r--r--src/mod_ping.erl2
-rw-r--r--src/mod_pres_counter.erl2
-rw-r--r--src/mod_privacy.erl2
-rw-r--r--src/mod_privacy_mnesia.erl2
-rw-r--r--src/mod_privacy_riak.erl2
-rw-r--r--src/mod_privacy_sql.erl2
-rw-r--r--src/mod_private.erl193
-rw-r--r--src/mod_private_mnesia.erl2
-rw-r--r--src/mod_private_riak.erl2
-rw-r--r--src/mod_private_sql.erl2
-rw-r--r--src/mod_privilege.erl2
-rw-r--r--src/mod_proxy65.erl2
-rw-r--r--src/mod_proxy65_lib.erl2
-rw-r--r--src/mod_proxy65_mnesia.erl2
-rw-r--r--src/mod_proxy65_redis.erl2
-rw-r--r--src/mod_proxy65_riak.erl2
-rw-r--r--src/mod_proxy65_service.erl2
-rw-r--r--src/mod_proxy65_sql.erl2
-rw-r--r--src/mod_proxy65_stream.erl2
-rw-r--r--src/mod_pubsub.erl12
-rw-r--r--src/mod_push.erl2
-rw-r--r--src/mod_push_keepalive.erl2
-rw-r--r--src/mod_push_mnesia.erl2
-rw-r--r--src/mod_push_sql.erl2
-rw-r--r--src/mod_register.erl12
-rw-r--r--src/mod_register_web.erl2
-rw-r--r--src/mod_roster.erl14
-rw-r--r--src/mod_roster_mnesia.erl2
-rw-r--r--src/mod_roster_riak.erl2
-rw-r--r--src/mod_roster_sql.erl2
-rw-r--r--src/mod_s2s_dialback.erl2
-rw-r--r--src/mod_service_log.erl2
-rw-r--r--src/mod_shared_roster.erl30
-rw-r--r--src/mod_shared_roster_ldap.erl2
-rw-r--r--src/mod_shared_roster_mnesia.erl2
-rw-r--r--src/mod_shared_roster_riak.erl2
-rw-r--r--src/mod_shared_roster_sql.erl2
-rw-r--r--src/mod_sic.erl2
-rw-r--r--src/mod_sip.erl2
-rw-r--r--src/mod_sip_proxy.erl2
-rw-r--r--src/mod_sip_registrar.erl2
-rw-r--r--src/mod_stats.erl2
-rw-r--r--src/mod_stream_mgmt.erl80
-rw-r--r--src/mod_time.erl2
-rw-r--r--src/mod_vcard.erl2
-rw-r--r--src/mod_vcard_ldap.erl2
-rw-r--r--src/mod_vcard_mnesia.erl2
-rw-r--r--src/mod_vcard_riak.erl2
-rw-r--r--src/mod_vcard_sql.erl2
-rw-r--r--src/mod_vcard_xupdate.erl2
-rw-r--r--src/mod_version.erl2
-rw-r--r--src/node_buddy.erl2
-rw-r--r--src/node_club.erl2
-rw-r--r--src/node_dag.erl2
-rw-r--r--src/node_dispatch.erl2
-rw-r--r--src/node_flat.erl2
-rw-r--r--src/node_flat_sql.erl2
-rw-r--r--src/node_hometree.erl2
-rw-r--r--src/node_hometree_sql.erl2
-rw-r--r--src/node_mb.erl2
-rw-r--r--src/node_mb_sql.erl2
-rw-r--r--src/node_mix.erl2
-rw-r--r--src/node_mix_sql.erl2
-rw-r--r--src/node_online.erl2
-rw-r--r--src/node_pep.erl5
-rw-r--r--src/node_pep_sql.erl5
-rw-r--r--src/node_private.erl2
-rw-r--r--src/node_public.erl2
-rw-r--r--src/nodetree_dag.erl2
-rw-r--r--src/nodetree_tree.erl2
-rw-r--r--src/nodetree_tree_sql.erl2
-rw-r--r--src/nodetree_virtual.erl2
-rw-r--r--src/prosody2ejabberd.erl45
-rw-r--r--src/proxy_protocol.erl184
-rw-r--r--src/pubsub_db_sql.erl2
-rw-r--r--src/pubsub_index.erl2
-rw-r--r--src/pubsub_migrate.erl2
-rw-r--r--src/pubsub_subscription.erl2
-rw-r--r--src/pubsub_subscription_sql.erl2
-rw-r--r--src/rest.erl2
-rw-r--r--src/str.erl2
-rw-r--r--src/translate.erl2
-rw-r--r--src/win32_dns.erl2
-rw-r--r--src/xml_compress.erl958
228 files changed, 2848 insertions, 2151 deletions
diff --git a/src/acl.erl b/src/acl.erl
index 1bffd8fa9..959faeaf0 100644
--- a/src/acl.erl
+++ b/src/acl.erl
@@ -5,7 +5,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index 4d08fa5b3..9992d7392 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl
index 23dd9446d..4d673e753 100644
--- a/src/ejabberd_access_permissions.erl
+++ b/src/ejabberd_access_permissions.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Sep 2016 by Paweł Chmielowski <pawel@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl
index bd4f22418..9e25ed4e6 100644
--- a/src/ejabberd_acme.erl
+++ b/src/ejabberd_acme.erl
@@ -25,6 +25,7 @@
-include("ejabberd_commands.hrl").
-include("ejabberd_acme.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(DEFAULT_CONFIG_CONTACT, <<"mailto:example-admin@example.com">>).
-define(DEFAULT_CONFIG_CA_URL, "https://acme-v01.api.letsencrypt.org").
@@ -100,10 +101,10 @@ is_valid_domain_opt(DomainString) ->
end.
-spec is_valid_revoke_cert(string()) -> boolean().
-is_valid_revoke_cert(DomainOrFile) ->
+is_valid_revoke_cert(DomainOrFile) ->
lists:prefix("file:", DomainOrFile) orelse
lists:prefix("domain:", DomainOrFile).
-
+
%% Commands
get_commands_spec() ->
[#ejabberd_commands{name = get_certificates, tags = [acme],
@@ -142,7 +143,7 @@ get_commands_spec() ->
%%
-spec get_certificates(domains_opt()) -> string() | {'error', _}.
get_certificates(Domains) ->
- case is_valid_domain_opt(Domains) of
+ case is_valid_domain_opt(Domains) of
true ->
try
CAUrl = get_config_ca_url(),
@@ -150,9 +151,8 @@ get_certificates(Domains) ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, get_certificates}
end;
false ->
@@ -171,7 +171,7 @@ retrieve_or_create_account(CAUrl) ->
case read_account_persistent() of
none ->
create_save_new_account(CAUrl);
-
+
{ok, AccId, CAUrl, PrivateKey} ->
{ok, AccId, PrivateKey};
{ok, _AccId, _, _PrivateKey} ->
@@ -199,7 +199,7 @@ get_certificates2(CAUrl, PrivateKey, Hosts) ->
%% Format the result to send back to ejabberdctl
format_get_certificates_result(SavedCerts).
--spec format_get_certificates_result([{'ok', bitstring(), _} |
+-spec format_get_certificates_result([{'ok', bitstring(), _} |
{'error', bitstring(), _}]) ->
string().
format_get_certificates_result(Certs) ->
@@ -211,26 +211,26 @@ format_get_certificates_result(Certs) ->
case Cond of
true ->
Result = io_lib:format("Success:~n~s", [FormattedCerts]),
- lists:flatten(Result);
+ lists:flatten(Result);
_ ->
Result = io_lib:format("Error with one or more certificates~n~s", [FormattedCerts]),
lists:flatten(Result)
end.
--spec format_get_certificate({'ok', bitstring(), _} |
+-spec format_get_certificate({'ok', bitstring(), _} |
{'error', bitstring(), _}) ->
string().
-format_get_certificate({ok, Domain, saved}) ->
+format_get_certificate({ok, Domain, saved}) ->
io_lib:format(" Certificate for domain: \"~s\" acquired and saved", [Domain]);
-format_get_certificate({ok, Domain, not_found}) ->
+format_get_certificate({ok, Domain, not_found}) ->
io_lib:format(" Certificate for domain: \"~s\" not found, so it was not renewed", [Domain]);
format_get_certificate({ok, Domain, no_expire}) ->
io_lib:format(" Certificate for domain: \"~s\" is not close to expiring", [Domain]);
format_get_certificate({error, Domain, Reason}) ->
io_lib:format(" Error for domain: \"~s\", with reason: \'~s\'", [Domain, Reason]).
--spec get_certificate(url(), bitstring(), jose_jwk:key()) ->
- {'ok', bitstring(), pem()} |
+-spec get_certificate(url(), bitstring(), jose_jwk:key()) ->
+ {'ok', bitstring(), pem()} |
{'error', bitstring(), _}.
get_certificate(CAUrl, DomainName, PrivateKey) ->
try
@@ -243,9 +243,8 @@ get_certificate(CAUrl, DomainName, PrivateKey) ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, DomainName, get_certificate}
end.
@@ -267,23 +266,23 @@ create_save_new_account(CAUrl) ->
%% TODO:
%% Find a way to ask the user if he accepts the TOS
--spec create_new_account(url(), bitstring(), jose_jwk:key()) -> {'ok', string()} |
+-spec create_new_account(url(), bitstring(), jose_jwk:key()) -> {'ok', string()} |
no_return().
create_new_account(CAUrl, Contact, PrivateKey) ->
try
{ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl),
Req0 = [{ <<"contact">>, [Contact]}],
- {ok, {TOS, Account}, Nonce1} =
+ {ok, {TOS, Account}, Nonce1} =
ejabberd_acme_comm:new_account(Dirs, PrivateKey, Req0, Nonce0),
{<<"id">>, AccIdInt} = lists:keyfind(<<"id">>, 1, Account),
AccId = integer_to_list(AccIdInt),
Req1 = [{ <<"agreement">>, list_to_bitstring(TOS)}],
- {ok, _Account2, _Nonce2} =
+ {ok, _Account2, _Nonce2} =
ejabberd_acme_comm:update_account({CAUrl, AccId}, PrivateKey, Req1, Nonce1),
{ok, AccId}
catch
E:R ->
- ?ERROR_MSG("Error: ~p creating an account for contact: ~p",
+ ?ERROR_MSG("Error: ~p creating an account for contact: ~p",
[{E,R}, Contact]),
throw({error,create_new_account})
end.
@@ -298,7 +297,7 @@ create_new_authorization(CAUrl, DomainName, PrivateKey) ->
{[{<<"type">>, <<"dns">>},
{<<"value">>, DomainName}]}},
{<<"existing">>, <<"accept">>}],
- {ok, {AuthzUrl, Authz}, Nonce1} =
+ {ok, {AuthzUrl, Authz}, Nonce1} =
ejabberd_acme_comm:new_authz(Dirs, PrivateKey, Req0, Nonce0),
{ok, AuthzId} = location_to_id(AuthzUrl),
@@ -321,7 +320,7 @@ create_new_authorization(CAUrl, DomainName, PrivateKey) ->
acme_challenge:unregister_hooks(DomainName)
end.
--spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) ->
+-spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) ->
{ok, bitstring(), pem()}.
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
try
@@ -338,7 +337,7 @@ create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
{ok, {IssuerCertLink, Certificate}, _Nonce1} =
ejabberd_acme_comm:new_cert(Dirs, PrivateKey, Req, Nonce0),
- DecodedCert = public_key:pkix_decode_cert(list_to_binary(Certificate), plain),
+ DecodedCert = public_key:pkix_decode_cert(list_to_binary(Certificate), plain),
PemEntryCert = public_key:pem_entry_encode('Certificate', DecodedCert),
{ok, IssuerCert, _Nonce2} = ejabberd_acme_comm:get_issuer_cert(IssuerCertLink),
@@ -351,7 +350,7 @@ create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
PemCertKey = public_key:pem_encode([PemEntryKey, PemEntryCert, PemEntryIssuerCert]),
{ok, DomainName, PemCertKey}
- catch
+ catch
E:R ->
?ERROR_MSG("Error: ~p getting an authorization for domain: ~p~n",
[{E,R}, DomainName]),
@@ -383,9 +382,8 @@ renew_certificates() ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, get_certificates}
end.
@@ -406,8 +404,8 @@ renew_certificates0(CAUrl) ->
%% Format the result to send back to ejabberdctl
format_get_certificates_result(SavedCerts).
--spec renew_certificate(url(), {bitstring(), data_cert()}, jose_jwk:key()) ->
- {'ok', bitstring(), _} |
+-spec renew_certificate(url(), {bitstring(), data_cert()}, jose_jwk:key()) ->
+ {'ok', bitstring(), _} |
{'error', bitstring(), _}.
renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
case cert_to_expire(Cert) of
@@ -449,9 +447,8 @@ list_certificates(Verbose) ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, list_certificates}
end;
false ->
@@ -492,34 +489,33 @@ format_certificate(DataCert, Verbose) ->
format_certificate_verbose(DomainName, SANs, NotAfter, PemCert)
end
catch
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
fail_format_certificate(DomainName)
end.
--spec format_certificate_plain(bitstring(), [string()], {expired | ok, string()}, string())
+-spec format_certificate_plain(bitstring(), [string()], {expired | ok, string()}, string())
-> string().
format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
Result = lists:flatten(io_lib:format(
- " Domain: ~s~n"
+ " Domain: ~s~n"
"~s"
- " ~s~n"
- " Path: ~s",
+ " ~s~n"
+ " Path: ~s",
[DomainName,
lists:flatten([io_lib:format(" SAN: ~s~n", [SAN]) || SAN <- SANs]),
format_validity(NotAfter), Path])),
Result.
--spec format_certificate_verbose(bitstring(), [string()], {expired | ok, string()}, bitstring())
+-spec format_certificate_verbose(bitstring(), [string()], {expired | ok, string()}, bitstring())
-> string().
format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) ->
Result = lists:flatten(io_lib:format(
" Domain: ~s~n"
- "~s"
- " ~s~n"
- " Certificate In PEM format: ~n~s",
- [DomainName,
+ "~s"
+ " ~s~n"
+ " Certificate In PEM format: ~n~s",
+ [DomainName,
lists:flatten([io_lib:format(" SAN: ~s~n", [SAN]) || SAN <- SANs]),
format_validity(NotAfter), PemCert])),
Result.
@@ -533,8 +529,8 @@ format_validity({ok, NotAfter}) ->
-spec fail_format_certificate(bitstring()) -> string().
fail_format_certificate(DomainName) ->
Result = lists:flatten(io_lib:format(
- " Domain: ~s~n"
- " Failed to format Certificate",
+ " Domain: ~s~n"
+ " Failed to format Certificate",
[DomainName])),
Result.
@@ -542,7 +538,7 @@ fail_format_certificate(DomainName) ->
get_commonName(#'Certificate'{tbsCertificate = TbsCertificate}) ->
#'TBSCertificate'{
subject = {rdnSequence, SubjectList}
- } = TbsCertificate,
+ } = TbsCertificate,
%% TODO: Not the best way to find the commonName
ShallowSubjectList = [Attribute || [Attribute] <- SubjectList],
@@ -560,9 +556,9 @@ get_notAfter(Certificate) ->
true -> "19" ++ [Y1,Y2];
_ -> "20" ++ [Y1,Y2]
end,
- NotAfter = lists:flatten(io_lib:format("~s-~s-~s ~s:~s:~s",
+ NotAfter = lists:flatten(io_lib:format("~s-~s-~s ~s:~s:~s",
[YEAR, [MO1,MO2], [D1,D2],
- [H1,H2], [MI1,MI2], [S1,S2]])),
+ [H1,H2], [MI1,MI2], [S1,S2]])),
case close_to_expire(UtcTime, 0) of
true ->
@@ -577,7 +573,7 @@ get_subjectAltNames(#'Certificate'{tbsCertificate = TbsCertificate}) ->
extensions = Exts
} = TbsCertificate,
- EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts,
+ EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts,
Oid =:= attribute_oid(subjectAltName)],
lists:flatmap(
@@ -586,7 +582,7 @@ get_subjectAltNames(#'Certificate'{tbsCertificate = TbsCertificate}) ->
[Name || {dNSName, Name} <- SANs0]
end, EncodedSANs).
-
+
-spec get_utc_validity(#'Certificate'{}) -> string().
get_utc_validity(#'Certificate'{tbsCertificate = TbsCertificate}) ->
@@ -618,18 +614,17 @@ revoke_certificates(DomainOrFile) ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, revoke_certificate}
- end.
+ end.
-spec revoke_certificate0(url(), string()) -> {ok, deleted}.
revoke_certificate0(CAUrl, DomainOrFile) ->
ParsedCert = parse_revoke_cert_argument(DomainOrFile),
revoke_certificate1(CAUrl, ParsedCert).
--spec revoke_certificate1(url(), {domain, bitstring()} | {file, file:filename()}) ->
+-spec revoke_certificate1(url(), {domain, bitstring()} | {file, file:filename()}) ->
{ok, deleted}.
revoke_certificate1(CAUrl, {domain, Domain}) ->
case domain_certificate_exists(Domain) of
@@ -650,7 +645,7 @@ revoke_certificate1(CAUrl, {file, File}) ->
?ERROR_MSG("Error: ~p reading pem certificate-key file: ~p", [Reason, File]),
throw({error, Reason})
end.
-
+
-spec revoke_certificate2(url(), pem()) -> ok.
revoke_certificate2(CAUrl, PemEncodedCert) ->
@@ -676,10 +671,10 @@ prepare_certificate_revoke(PemEncodedCert) ->
DerCert = public_key:der_encode('Certificate', PemCert),
Base64Cert = base64url:encode(DerCert),
- {ok, Key} = find_private_key_in_pem(PemEncodedCert),
+ {ok, Key} = find_private_key_in_pem(PemEncodedCert),
{Base64Cert, Key}.
--spec domain_certificate_exists(bitstring()) -> {bitstring(), data_cert()} | false.
+-spec domain_certificate_exists(bitstring()) -> {bitstring(), data_cert()} | false.
domain_certificate_exists(Domain) ->
Certs = read_certificates_persistent(),
lists:keyfind(Domain, 1, Certs).
@@ -693,7 +688,7 @@ domain_certificate_exists(Domain) ->
%% For now we accept only generating a key of
%% specific type for signing the csr
--spec make_csr(proplist(), [{dNSName, bitstring()}])
+-spec make_csr(proplist(), [{dNSName, bitstring()}])
-> {binary(), jose_jwk:key()}.
make_csr(Attributes, SANs) ->
Key = generate_key(),
@@ -749,9 +744,9 @@ extension(SANs) ->
extension_request(SANs) ->
#'AttributePKCS-10'{
type = ?'pkcs-9-at-extensionRequest',
- values = [{'asn1_OPENTYPE',
+ values = [{'asn1_OPENTYPE',
public_key:der_encode(
- 'ExtensionRequest',
+ 'ExtensionRequest',
[extension(SANs)])}]
}.
@@ -918,7 +913,7 @@ find_private_key_in_pem(Pem) ->
JoseKey = jose_jwk:from_key(Key),
{ok, JoseKey}
end.
-
+
-spec find_private_key_in_pem1([public_key:pki_asn1_type()],
[public_key:pem_entry()]) ->
@@ -948,10 +943,10 @@ private_key_types() ->
find_all_sub_domains(DomainName) ->
AllRoutes = ejabberd_router:get_all_routes(),
DomainLen = size(DomainName),
- [Route || Route <- AllRoutes,
- binary:longest_common_suffix([DomainName, Route])
+ [Route || Route <- AllRoutes,
+ binary:longest_common_suffix([DomainName, Route])
=:= DomainLen].
-
+
-spec is_error(_) -> boolean().
is_error({error, _}) -> true;
@@ -981,13 +976,13 @@ data_get_account(Data) ->
end.
-spec data_set_account(acme_data(), {list(), url(), jose_jwk:key()}) -> acme_data().
-data_set_account(Data, {AccId, CAUrl, PrivateKey}) ->
+data_set_account(Data, {AccId, CAUrl, PrivateKey}) ->
NewAcc = {account, #data_acc{id = AccId, ca_url = CAUrl, key = PrivateKey}},
lists:keystore(account, 1, Data, NewAcc).
%%
%% Certificates
-%%
+%%
-spec data_get_certificates(acme_data()) -> data_certs().
data_get_certificates(Data) ->
@@ -999,7 +994,7 @@ data_get_certificates(Data) ->
end.
-spec data_set_certificates(acme_data(), data_certs()) -> acme_data().
-data_set_certificates(Data, NewCerts) ->
+data_set_certificates(Data, NewCerts) ->
lists:keystore(certs, 1, Data, {certs, NewCerts}).
%% ATM we preserve one certificate for each domain
@@ -1053,7 +1048,7 @@ write_persistent(Data) ->
{error, Reason} ->
?ERROR_MSG("Error: ~p writing acme data file", [Reason]),
throw({error, Reason})
- end.
+ end.
-spec create_persistent() -> ok | no_return().
create_persistent() ->
@@ -1069,7 +1064,7 @@ create_persistent() ->
{error, Reason} ->
?ERROR_MSG("Error: ~p creating acme data file", [Reason]),
throw({error, Reason})
- end.
+ end.
-spec write_account_persistent({list(), url(), jose_jwk:key()}) -> ok | no_return().
write_account_persistent({AccId, CAUrl, PrivateKey}) ->
@@ -1099,7 +1094,7 @@ remove_certificate_persistent(DataCert) ->
NewData = data_remove_certificate(Data, DataCert),
ok = write_persistent(NewData).
--spec save_certificate({ok, bitstring(), binary()} | {error, _, _}) ->
+-spec save_certificate({ok, bitstring(), binary()} | {error, _, _}) ->
{ok, bitstring(), saved} | {error, bitstring(), _}.
save_certificate({error, _, _} = Error) ->
Error;
@@ -1123,13 +1118,12 @@ save_certificate({ok, DomainName, Cert}) ->
catch
throw:Throw ->
Throw;
- E:R ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, St]),
+ ?EX_RULE(E, R, St) ->
+ ?ERROR_MSG("Unknown ~p:~p, ~p", [E, R, ?EX_STACK(St)]),
{error, DomainName, saving}
end.
--spec save_renewed_certificate({ok, bitstring(), _} | {error, _, _}) ->
+-spec save_renewed_certificate({ok, bitstring(), _} | {error, _, _}) ->
{ok, bitstring(), _} | {error, bitstring(), _}.
save_renewed_certificate({error, _, _} = Error) ->
Error;
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index beaaaf562..4dbd0a0be 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 7 May 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 3cdab80fe..20e9a7df7 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -77,6 +77,7 @@ start(_, _) ->
%% This function is called when an application is about to be stopped,
%% before shutting down the processes of the application.
prep_stop(State) ->
+ ejabberd_hooks:run(ejabberd_stopping, []),
ejabberd_listener:stop_listeners(),
ejabberd_sm:stop(),
gen_mod:stop_modules(),
@@ -151,10 +152,10 @@ start_apps() ->
crypto:start(),
ejabberd:start_app(sasl),
ejabberd:start_app(ssl),
- ejabberd:start_app(pkix),
ejabberd:start_app(p1_utils),
ejabberd:start_app(fast_yaml),
ejabberd:start_app(fast_tls),
+ ejabberd:start_app(pkix),
ejabberd:start_app(xmpp),
ejabberd:start_app(cache_tab),
ejabberd:start_app(eimp).
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index 5659ee389..c27930140 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -41,7 +41,8 @@
get_password_s/2, get_password_with_authmodule/2,
user_exists/2, user_exists_in_other_modules/3,
remove_user/2, remove_user/3, plain_password_required/1,
- store_type/1, entropy/1, backend_type/1, password_format/1]).
+ store_type/1, entropy/1, backend_type/1, password_format/1,
+ which_users_exists/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -411,6 +412,47 @@ user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) ->
maybe
end.
+-spec which_users_exists(list({binary(), binary()})) -> list({binary(), binary()}).
+which_users_exists(USPairs) ->
+ ByServer = lists:foldl(
+ fun({User, Server}, Dict) ->
+ LServer = jid:nameprep(Server),
+ LUser = jid:nodeprep(User),
+ case gb_trees:lookup(LServer, Dict) of
+ none ->
+ gb_trees:insert(LServer, gb_sets:singleton(LUser), Dict);
+ {value, Set} ->
+ gb_trees:update(LServer, gb_sets:add(LUser, Set), Dict)
+ end
+ end, gb_trees:empty(), USPairs),
+ Set = lists:foldl(
+ fun({LServer, UsersSet}, Results) ->
+ UsersList = gb_sets:to_list(UsersSet),
+ lists:foldl(
+ fun(M, Results2) ->
+ try M:which_users_exists(LServer, UsersList) of
+ {error, _} ->
+ Results2;
+ Res ->
+ gb_sets:union(
+ gb_sets:from_list([{U, LServer} || U <- Res]),
+ Results2)
+ catch
+ _:undef ->
+ lists:foldl(
+ fun(U, R2) ->
+ case user_exists(U, LServer) of
+ true ->
+ gb_sets:add({U, LServer}, R2);
+ _ ->
+ R2
+ end
+ end, Results2, UsersList)
+ end
+ end, Results, auth_modules(LServer))
+ end, gb_sets:empty(), gb_trees:to_list(ByServer)),
+ gb_sets:to_list(Set).
+
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
case validate_credentials(User, Server) of
diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl
index 3c4993cb0..21aecc341 100644
--- a/src/ejabberd_auth_anonymous.erl
+++ b/src/ejabberd_auth_anonymous.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index 3e300e3a0..6b2e2852e 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl
index b23b1c340..06000c7f1 100644
--- a/src/ejabberd_auth_ldap.erl
+++ b/src/ejabberd_auth_ldap.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl
index 004e74996..1f75f686d 100644
--- a/src/ejabberd_auth_mnesia.erl
+++ b/src/ejabberd_auth_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl
index 71b745df0..ffbab1f1f 100644
--- a/src/ejabberd_auth_pam.erl
+++ b/src/ejabberd_auth_pam.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Jul 2007 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_riak.erl b/src/ejabberd_auth_riak.erl
index 33c25424a..29da504c9 100644
--- a/src/ejabberd_auth_riak.erl
+++ b/src/ejabberd_auth_riak.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Nov 2012 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl
index 4b774642a..f6f4f9efb 100644
--- a/src/ejabberd_auth_sql.erl
+++ b/src/ejabberd_auth_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -35,7 +35,7 @@
-export([start/1, stop/1, set_password/3, try_register/3,
get_users/2, count_users/2, get_password/2,
remove_user/2, store_type/1, plain_password_required/1,
- convert_to_scram/1, opt_type/1, export/1]).
+ convert_to_scram/1, opt_type/1, export/1, which_users_exists/2]).
-include("scram.hrl").
-include("logger.hrl").
@@ -247,6 +247,32 @@ users_number(LServer, [{prefix, Prefix}])
users_number(LServer, []) ->
users_number(LServer).
+which_users_exists(LServer, LUsers) when length(LUsers) =< 100 ->
+ try ejabberd_sql:sql_query(
+ LServer,
+ ?SQL("select @(username)s from users where username in %(LUsers)ls")) of
+ {selected, Matching} ->
+ [U || {U} <- Matching];
+ {error, _} = E ->
+ E
+ catch _:B ->
+ {error, B}
+ end;
+which_users_exists(LServer, LUsers) ->
+ {First, Rest} = lists:split(100, LUsers),
+ case which_users_exists(LServer, First) of
+ {error, _} = E ->
+ E;
+ V ->
+ case which_users_exists(LServer, Rest) of
+ {error, _} = E2 ->
+ E2;
+ V2 ->
+ V ++ V2
+ end
+ end.
+
+
convert_to_scram(Server) ->
LServer = jid:nameprep(Server),
if
diff --git a/src/ejabberd_backend_sup.erl b/src/ejabberd_backend_sup.erl
index dadb31f12..832c4e3e6 100644
--- a/src/ejabberd_backend_sup.erl
+++ b/src/ejabberd_backend_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 24 Feb 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl
index e39a67132..cdfd59b8a 100644
--- a/src/ejabberd_bosh.erl
+++ b/src/ejabberd_bosh.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index d491ae9d3..61284e3e6 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -2,7 +2,7 @@
%%% Created : 8 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -709,13 +709,11 @@ process_presence_out(#{lserver := LServer, jid := JID,
end.
-spec process_self_presence(state(), presence()) -> state().
-process_self_presence(#{ip := IP, conn := Conn, lserver := LServer,
- auth_module := AuthMod, sid := SID,
+process_self_presence(#{lserver := LServer, sid := SID,
user := U, server := S, resource := R} = State,
#presence{type = unavailable} = Pres) ->
Status = xmpp:get_text(Pres#presence.status),
- Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthMod}],
- ejabberd_sm:unset_presence(SID, U, S, R, Status, Info),
+ ejabberd_sm:unset_presence(SID, U, S, R, Status),
{Pres1, State1} = ejabberd_hooks:run_fold(
c2s_self_presence, LServer, {Pres, State}, []),
State2 = broadcast_presence_unavailable(State1, Pres1),
@@ -733,13 +731,11 @@ process_self_presence(#{lserver := LServer} = State,
process_self_presence(State, _Pres) ->
State.
--spec update_priority(state(), presence()) -> ok.
-update_priority(#{ip := IP, conn := Conn, auth_module := AuthMod,
- sid := SID, user := U, server := S, resource := R},
+-spec update_priority(state(), presence()) -> ok | {error, notfound}.
+update_priority(#{sid := SID, user := U, server := S, resource := R},
Pres) ->
Priority = get_priority_from_presence(Pres),
- Info = [{ip, IP}, {conn, Conn}, {auth_module, AuthMod}],
- ejabberd_sm:set_presence(SID, U, S, R, Priority, Pres, Info).
+ ejabberd_sm:set_presence(SID, U, S, R, Priority, Pres).
-spec broadcast_presence_unavailable(state(), presence()) -> state().
broadcast_presence_unavailable(#{jid := JID, pres_a := PresA} = State, Pres) ->
diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl
index 0a183783d..d6782de4f 100644
--- a/src/ejabberd_c2s_config.erl
+++ b/src/ejabberd_c2s_config.erl
@@ -6,7 +6,7 @@
%%% Created : 2 Nov 2007 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl
index 62cecb9b6..91f0cb2da 100644
--- a/src/ejabberd_captcha.erl
+++ b/src/ejabberd_captcha.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Apr 2008 by Evgeniy Khramtsov <xramtsov@gmail.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -105,12 +105,13 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
"To unblock them, visit ~s">>, [JID, get_url(Id)]},
Body = xmpp:mk_text(BodyString, Lang),
OOB = #oob_x{url = get_url(Id)},
+ Hint = #hint{type = 'no-store'},
Tref = erlang:send_after(?CAPTCHA_LIFETIME, ?MODULE,
{remove_id, Id}),
ets:insert(captcha,
#captcha{id = Id, pid = self(), key = Key, tref = Tref,
args = Args}),
- {ok, Id, Body, [OOB, Captcha, Data]};
+ {ok, Id, Body, [Hint, OOB, Captcha, Data]};
Err -> Err
end.
@@ -128,10 +129,10 @@ create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
"visit the web page.">>),
Imageurl = get_url(<<Id/binary, "/image">>),
NewFs = [mk_field(hidden, <<"FORM_TYPE">>, ?NS_CAPTCHA)|Fs] ++
- [#xdata_field{type = fixed, values = [HelpTxt]},
+ [#xdata_field{type = fixed, var = <<"captcha-fallback-text">>, values = [HelpTxt]},
#xdata_field{type = hidden, var = <<"captchahidden">>,
values = [<<"workaround-for-psi">>]},
- #xdata_field{type = 'text-single', var = <<"url">>,
+ #xdata_field{type = 'text-single', var = <<"captcha-fallback-url">>,
label = translate:translate(
Lang, <<"CAPTCHA web page">>),
values = [Imageurl]},
diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl
index 8fb9dde8e..10f945fe7 100644
--- a/src/ejabberd_cluster.erl
+++ b/src/ejabberd_cluster.erl
@@ -3,7 +3,7 @@
%%% Created : 5 Jul 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_cluster_mnesia.erl b/src/ejabberd_cluster_mnesia.erl
index 4889abd66..fb793cda8 100644
--- a/src/ejabberd_cluster_mnesia.erl
+++ b/src/ejabberd_cluster_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl
index 56a1518e4..921ed25c7 100644
--- a/src/ejabberd_commands.erl
+++ b/src/ejabberd_commands.erl
@@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -492,6 +492,7 @@ do_execute_command(Command, Arguments) ->
Module = Command#ejabberd_commands.module,
Function = Command#ejabberd_commands.function,
?DEBUG("Executing command ~p:~p with Args=~p", [Module, Function, Arguments]),
+ ejabberd_hooks:run(api_call, [Module, Function, Arguments]),
apply(Module, Function, Arguments).
-spec get_tags_commands() -> [{string(), [string()]}].
diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl
index 590e06ea9..b62c4ca5b 100644
--- a/src/ejabberd_commands_doc.erl
+++ b/src/ejabberd_commands_doc.erl
@@ -5,7 +5,7 @@
%%% Created : 20 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 48fdcefbc..a7eea52d5 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -5,7 +5,7 @@
%%% Created : 14 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -500,7 +500,8 @@ get_config_option_key(Name, Val) ->
maps_to_lists(IMap) ->
maps:fold(fun(Name, Map, Res) when Name == host_config orelse Name == append_host_config ->
- [{Name, [{Host, maps_to_lists(SMap)} || {Host,SMap} <- maps:values(Map)]} | Res];
+ [{Name, [{jid:nameprep(Host), maps_to_lists(SMap)} ||
+ {Host,SMap} <- maps:values(Map)]} | Res];
(Name, Map, Res) when is_map(Map) ->
[{Name, maps:values(Map)} | Res];
(Name, Val, Res) ->
@@ -513,8 +514,9 @@ merge_configs(Terms, ResMap) ->
New = lists:foldl(fun(SVal, OMap) ->
NVal = if Name == host_config orelse Name == append_host_config ->
{Host, Opts} = SVal,
- {_, SubMap} = maps:get(Host, OMap, {Host, #{}}),
- {Host, merge_configs(Opts, SubMap)};
+ HostNP = jid:nameprep(Host),
+ {_, SubMap} = maps:get(HostNP, OMap, {HostNP, #{}}),
+ {HostNP, merge_configs(Opts, SubMap)};
true ->
SVal
end,
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index c71649562..73a98827c 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jan 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -59,6 +59,7 @@
-include("ejabberd_ctl.hrl").
-include("ejabberd_commands.hrl").
-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(DEFAULT_VERSION, 1000000).
@@ -327,9 +328,9 @@ try_call_command(Args, Auth, AccessCommands, Version) ->
catch
throw:Error ->
{io_lib:format("~p", [Error]), ?STATUS_ERROR};
- A:Why ->
- Stack = erlang:get_stacktrace(),
- {io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR}
+ ?EX_RULE(A, Why, Stack) ->
+ {io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p",
+ [A, Why, ?EX_STACK(Stack)]), ?STATUS_ERROR}
end.
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()} | {error, ErrorType}
diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl
index bc67b4c67..28d994c4a 100644
--- a/src/ejabberd_hooks.erl
+++ b/src/ejabberd_hooks.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Aug 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -57,6 +57,7 @@
terminate/2]).
-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(state, {}).
-type local_hook() :: { Seq :: integer(), Module :: atom(), Function :: atom()}.
@@ -129,14 +130,14 @@ delete_dist(Hook, Node, Module, Function, Seq) ->
delete_dist(Hook, Host, Node, Module, Function, Seq) ->
gen_server:call(ejabberd_hooks, {delete, Hook, Host, Node, Module, Function, Seq}).
--spec delete_all_hooks() -> true.
+-spec delete_all_hooks() -> true.
%% @doc Primarily for testing / instrumentation
delete_all_hooks() ->
gen_server:call(ejabberd_hooks, {delete_all}).
-spec get_handlers(atom(), binary() | global) -> [local_hook() | distributed_hook()].
-%% @doc Returns currently set handler for hook name
+%% @doc Returns currently set handler for hook name
get_handlers(Hookname, Host) ->
gen_server:call(ejabberd_hooks, {get_handlers, Hookname, Host}).
@@ -264,7 +265,7 @@ handle_delete(Hook, Host, El) ->
ok;
[] ->
ok
- end.
+ end.
%%----------------------------------------------------------------------
%% Func: handle_cast/2
@@ -379,15 +380,11 @@ safe_apply(Hook, Module, Function, Args) ->
true ->
apply(Module, Function, Args)
end
- catch E:R when E /= exit; R /= normal ->
- St = get_stacktrace(),
+ catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n"
"** Reason = ~p~n"
"** Arguments = ~p",
[Hook, Module, Function, length(Args),
- {E, R, St}, Args]),
+ {E, R, ?EX_STACK(St)}, Args]),
'EXIT'
end.
-
-get_stacktrace() ->
- [{Mod, Fun, Loc, Args} || {Mod, Fun, Args, Loc} <- erlang:get_stacktrace()].
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index 65a0c2f5d..f2928a02e 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -69,7 +69,8 @@
default_host,
custom_headers,
trail = <<>>,
- addr_re
+ addr_re,
+ sock_peer_name = none
}).
-define(XHTML_DOCTYPE,
@@ -143,6 +144,7 @@ init({SockMod, Socket}, Opts) ->
true -> [{[], ejabberd_xmlrpc}];
false -> []
end,
+ SockPeer = proplists:get_value(sock_peer_name, Opts, none),
DefinedHandlers = proplists:get_value(request_handlers, Opts, []),
RequestHandlers = DefinedHandlers ++ Captcha ++ Register ++
Admin ++ Bind ++ XMLRPC,
@@ -159,6 +161,7 @@ init({SockMod, Socket}, Opts) ->
custom_headers = CustomHeaders,
options = Opts,
request_handlers = RequestHandlers,
+ sock_peer_name = SockPeer,
addr_re = RE},
try receive_headers(State) of
V -> V
@@ -411,11 +414,11 @@ extract_path_query(#state{request_method = Method,
when Method =:= 'GET' orelse
Method =:= 'HEAD' orelse
Method =:= 'DELETE' orelse Method =:= 'OPTIONS' ->
- case catch url_decode_q_split(Path) of
- {'EXIT', _} -> {State, false};
- {NPath, Query} ->
- LPath = normalize_path([NPE
- || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
+ case catch url_decode_q_split_normalize(Path) of
+ {'EXIT', Error} ->
+ ?DEBUG("Error decoding URL '~p': ~p", [Path, Error]),
+ {State, false};
+ {LPath, Query} ->
LQuery = case catch parse_urlencoded(Query) of
{'EXIT', _Reason} -> [];
LQ -> LQ
@@ -429,11 +432,11 @@ extract_path_query(#state{request_method = Method,
sockmod = _SockMod,
socket = _Socket} = State)
when (Method =:= 'POST' orelse Method =:= 'PUT') andalso Len>0 ->
- case catch url_decode_q_split(Path) of
- {'EXIT', _} -> {State, false};
- {NPath, _Query} ->
- LPath = normalize_path(
- [NPE || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
+ case catch url_decode_q_split_normalize(Path) of
+ {'EXIT', Error} ->
+ ?DEBUG("Error decoding URL '~p': ~p", [Path, Error]),
+ {State, false};
+ {LPath, _Query} ->
case Method of
'PUT' ->
{State, {LPath, [], Trail}};
@@ -463,6 +466,7 @@ process_request(#state{request_method = Method,
request_version = Version,
sockmod = SockMod,
socket = Socket,
+ sock_peer_name = SockPeer,
options = Options,
request_host = Host,
request_port = Port,
@@ -481,13 +485,17 @@ process_request(#state{request_method = Method,
{State2, false} ->
{State2, make_bad_request(State)};
{State2, {LPath, LQuery, Data}} ->
- PeerName =
- case SockMod of
- gen_tcp ->
- inet:peername(Socket);
- _ ->
- SockMod:peername(Socket)
- end,
+ PeerName = case SockPeer of
+ none ->
+ case SockMod of
+ gen_tcp ->
+ inet:peername(Socket);
+ _ ->
+ SockMod:peername(Socket)
+ end;
+ {_, Peer} ->
+ {ok, Peer}
+ end,
IPHere = case PeerName of
{ok, V} -> V;
{error, _} = E -> throw(E)
@@ -724,6 +732,12 @@ file_format_error(Reason) ->
Text -> Text
end.
+url_decode_q_split_normalize(Path) ->
+ {NPath, Query} = url_decode_q_split(Path),
+ LPath = normalize_path([NPE
+ || NPE <- str:tokens(path_decode(NPath), <<"/">>)]),
+ {LPath, Query}.
+
% Code below is taken (with some modifications) from the yaws webserver, which
% is distributed under the following license:
%
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index d10dbd108..4b54e67ec 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -5,7 +5,7 @@
%%% Created : 09-10-2010 by Eric Cestari <ecestari@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl
index adee25dbe..8c731e0a1 100644
--- a/src/ejabberd_iq.erl
+++ b/src/ejabberd_iq.erl
@@ -5,7 +5,7 @@
%%% Created : 10 Nov 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl
index 3a1448c0b..c32f5be86 100644
--- a/src/ejabberd_listener.erl
+++ b/src/ejabberd_listener.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -204,26 +204,49 @@ accept(ListenSocket, Module, Opts, Sup, Interval) ->
NewInterval = check_rate_limit(Interval),
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
- case {inet:sockname(Socket), inet:peername(Socket)} of
- {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
- Receiver = case start_connection(Module, Socket, Opts, Sup) of
- {ok, RecvPid} ->
- RecvPid;
- _ ->
- gen_tcp:close(Socket),
- none
- end,
- ?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p",
- [Receiver,
- ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)),
- PPort, inet_parse:ntoa(Addr), Port]);
+ case proplists:get_value(use_proxy_protocol, Opts, false) of
+ true ->
+ case proxy_protocol:decode(gen_tcp, Socket, 10000) of
+ {error, Err} ->
+ ?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s",
+ [ListenSocket, inet:format_error(Err)]),
+ gen_tcp:close(Socket);
+ {{Addr, Port}, {PAddr, PPort}} = SP ->
+ Opts2 = [{sock_peer_name, SP} | Opts],
+ Receiver = case start_connection(Module, Socket, Opts2, Sup) of
+ {ok, RecvPid} ->
+ RecvPid;
+ _ ->
+ gen_tcp:close(Socket),
+ none
+ end,
+ ?INFO_MSG("(~p) Accepted proxied connection ~s:~p -> ~s:~p",
+ [Receiver,
+ ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)),
+ PPort, inet_parse:ntoa(Addr), Port])
+ end;
_ ->
- gen_tcp:close(Socket)
+ case {inet:sockname(Socket), inet:peername(Socket)} of
+ {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
+ Receiver = case start_connection(Module, Socket, Opts, Sup) of
+ {ok, RecvPid} ->
+ RecvPid;
+ _ ->
+ gen_tcp:close(Socket),
+ none
+ end,
+ ?INFO_MSG("(~p) Accepted connection ~s:~p -> ~s:~p",
+ [Receiver,
+ ejabberd_config:may_hide_data(inet_parse:ntoa(PAddr)),
+ PPort, inet_parse:ntoa(Addr), Port]);
+ _ ->
+ gen_tcp:close(Socket)
+ end
end,
accept(ListenSocket, Module, Opts, Sup, NewInterval);
{error, Reason} ->
?ERROR_MSG("(~w) Failed TCP accept: ~s",
- [ListenSocket, inet:format_error(Reason)]),
+ [ListenSocket, inet:format_error(Reason)]),
accept(ListenSocket, Module, Opts, Sup, NewInterval)
end.
@@ -665,7 +688,9 @@ listen_opt_type(max_fsm_queue) ->
listen_opt_type(shaper) ->
fun acl:shaper_rules_validator/1;
listen_opt_type(access) ->
- fun acl:access_rules_validator/1.
+ fun acl:access_rules_validator/1;
+listen_opt_type(use_proxy_protocol) ->
+ fun(B) when is_boolean(B) -> B end.
listen_options() ->
[module, port,
@@ -675,6 +700,7 @@ listen_options() ->
{inet6, false},
{accept_interval, 0},
{backlog, 5},
+ {use_proxy_protocol, false},
{supervisor, true}].
opt_type(listen) -> fun validate_cfg/1;
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index d9ef97129..1384a2359 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -48,6 +48,7 @@
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
-include("xmpp.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(state, {}).
@@ -70,10 +71,9 @@ start_link() ->
-spec route(stanza()) -> any().
route(Packet) ->
try do_route(Packet)
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
- [xmpp:pp(Packet), {E, {R, St}}])
+ [xmpp:pp(Packet), {E, {R, ?EX_STACK(St)}}])
end.
-spec route_iq(iq(), function()) -> ok.
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index c7845be2f..e35a769e5 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -5,7 +5,7 @@
%%% Created : 12 May 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_mnesia.erl b/src/ejabberd_mnesia.erl
index 48bc6db5c..3ccf6af89 100644
--- a/src/ejabberd_mnesia.erl
+++ b/src/ejabberd_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Nov 2016 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -43,6 +43,7 @@
-define(NEED_RESET, [local_content, type]).
-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(state, {tables = #{} :: map(),
schema = [] :: [{atom(), [{atom(), any()}]}]}).
@@ -385,8 +386,8 @@ do_transform(OldAttrs, Attrs, Old) ->
transform_fun(Module, Name) ->
fun(Obj) ->
try Module:transform(Obj)
- catch E:R ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
+ StackTrace = ?EX_STACK(St),
?ERROR_MSG("Failed to transform Mnesia table ~s:~n"
"** Record: ~p~n"
"** Reason: ~p~n"
diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl
index fbd311335..2913c8ef9 100644
--- a/src/ejabberd_oauth.erl
+++ b/src/ejabberd_oauth.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Mar 2015 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl
index 04d5c9958..1c55877eb 100644
--- a/src/ejabberd_oauth_mnesia.erl
+++ b/src/ejabberd_oauth_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl
index 151946d30..215766d00 100644
--- a/src/ejabberd_oauth_rest.erl
+++ b/src/ejabberd_oauth_rest.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl
index f8daa51bd..e86e5be9f 100644
--- a/src/ejabberd_oauth_sql.erl
+++ b/src/ejabberd_oauth_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Jul 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 541fcad8b..02b79ed4d 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -5,7 +5,7 @@
%%% Created : 17 Jul 2008 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -166,15 +166,10 @@ export_users([], _Server, _Fd) ->
export_user(User, Server, Fd) ->
Password = ejabberd_auth:get_password_s(User, Server),
LServer = jid:nameprep(Server),
- PasswordFormat = ejabberd_auth:password_format(LServer),
- Pass = case Password of
- {_,_,_,_} ->
- case PasswordFormat of
- scram -> format_scram_password(Password);
- _ -> <<"">>
- end;
- _ -> Password
- end,
+ Pass = case ejabberd_auth:password_format(LServer) of
+ scram -> format_scram_password(Password);
+ _ -> Password
+ end,
Els = get_offline(User, Server) ++
get_vcard(User, Server) ++
get_privacy(User, Server) ++
@@ -186,7 +181,8 @@ export_user(User, Server, Fd) ->
{<<"password">>, Pass}],
children = Els})).
-format_scram_password({StoredKey, ServerKey, Salt, IterationCount}) ->
+format_scram_password(#scram{storedkey = StoredKey, serverkey = ServerKey,
+ salt = Salt, iterationcount = IterationCount}) ->
StoredKeyB64 = base64:encode(StoredKey),
ServerKeyB64 = base64:encode(ServerKey),
SaltB64 = base64:encode(Salt),
diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl
index 2005ffdfa..39b69d033 100644
--- a/src/ejabberd_pkix.erl
+++ b/src/ejabberd_pkix.erl
@@ -3,7 +3,7 @@
%%% Created : 4 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl
index 2b69258e5..92eacdd0b 100644
--- a/src/ejabberd_rdbms.erl
+++ b/src/ejabberd_rdbms.erl
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl
index 857bece0e..3fa3f37d2 100644
--- a/src/ejabberd_redis.erl
+++ b/src/ejabberd_redis.erl
@@ -4,7 +4,7 @@
%%% Created : 8 May 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -50,6 +50,7 @@
-define(CALL_TIMEOUT, 60*1000). %% 60 seconds
-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(state, {connection :: pid() | undefined,
num :: pos_integer(),
@@ -106,9 +107,9 @@ multi(F) ->
{error, _} = Err -> Err;
Result -> get_result(Result)
end
- catch E:R ->
+ catch ?EX_RULE(E, R, St) ->
erlang:erase(?TR_STACK),
- erlang:raise(E, R, erlang:get_stacktrace())
+ erlang:raise(E, R, ?EX_STACK(St))
end;
_ ->
erlang:error(nested_transaction)
diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl
index bbcd11449..f34a96655 100644
--- a/src/ejabberd_redis_sup.erl
+++ b/src/ejabberd_redis_sup.erl
@@ -3,7 +3,7 @@
%%% Created : 6 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl
index 284797529..c454835a9 100644
--- a/src/ejabberd_regexp.erl
+++ b/src/ejabberd_regexp.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2011 by Badlop
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_riak.erl b/src/ejabberd_riak.erl
index 54c84d543..a86ac06d3 100644
--- a/src/ejabberd_riak.erl
+++ b/src/ejabberd_riak.erl
@@ -5,7 +5,7 @@
%%% Created : 29 Dec 2011 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_riak_sup.erl b/src/ejabberd_riak_sup.erl
index 950acbd9e..2598297b9 100644
--- a/src/ejabberd_riak_sup.erl
+++ b/src/ejabberd_riak_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 29 Dec 2011 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index bd9a87ce2..12baed5ee 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -71,6 +71,7 @@
-include("logger.hrl").
-include("ejabberd_router.hrl").
-include("xmpp.hrl").
+-include("ejabberd_stacktrace.hrl").
-callback init() -> any().
-callback register_route(binary(), binary(), local_hint(),
@@ -90,10 +91,9 @@ start_link() ->
-spec route(stanza()) -> ok.
route(Packet) ->
try do_route(Packet)
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
- [xmpp:pp(Packet), {E, {R, St}}])
+ [xmpp:pp(Packet), {E, {R, ?EX_STACK(St)}}])
end.
-spec route(jid(), jid(), xmlel() | stanza()) -> ok.
diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl
index a97874bcd..03fbe495f 100644
--- a/src/ejabberd_router_mnesia.erl
+++ b/src/ejabberd_router_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl
index c95c18882..6dbaeb41a 100644
--- a/src/ejabberd_router_multicast.erl
+++ b/src/ejabberd_router_multicast.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Aug 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_redis.erl b/src/ejabberd_router_redis.erl
index 706a6b4fe..cafb05b13 100644
--- a/src/ejabberd_router_redis.erl
+++ b/src/ejabberd_router_redis.erl
@@ -3,7 +3,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_riak.erl b/src/ejabberd_router_riak.erl
index b330f201d..20346c369 100644
--- a/src/ejabberd_router_riak.erl
+++ b/src/ejabberd_router_riak.erl
@@ -3,7 +3,7 @@
%%% Created : 15 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl
index edf06dfe0..bc3ef52ef 100644
--- a/src/ejabberd_router_sql.erl
+++ b/src/ejabberd_router_sql.erl
@@ -3,7 +3,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -32,6 +32,7 @@
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
-include("ejabberd_router.hrl").
+-include("ejabberd_stacktrace.hrl").
%%%===================================================================
%%% API
@@ -121,12 +122,11 @@ row_to_route(Domain, {ServerHost, NodeS, PidS, LocalHintS} = Row) ->
local_hint = dec_local_hint(LocalHintS)}]
catch _:{bad_node, _} ->
[];
- E:R ->
- St = erlang:get_stacktrace(),
+ ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to decode row from 'route' table:~n"
"Row = ~p~n"
"Domain = ~s~n"
"Reason = ~p",
- [Row, Domain, {E, {R, St}}]),
+ [Row, Domain, {E, {R, ?EX_STACK(St)}}]),
[]
end.
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
index a33d477e5..4b1881199 100644
--- a/src/ejabberd_s2s.erl
+++ b/src/ejabberd_s2s.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -55,12 +55,10 @@
transform_options/1, opt_type/1]).
-include("logger.hrl").
-
-include("xmpp.hrl").
-
-include("ejabberd_commands.hrl").
-
-include_lib("public_key/include/public_key.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(PKIXEXPLICIT, 'OTP-PUB-KEY').
@@ -94,10 +92,9 @@ start_link() ->
route(Packet) ->
try do_route(Packet)
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
- [xmpp:pp(Packet), {E, {R, St}}])
+ [xmpp:pp(Packet), {E, {R, ?EX_STACK(St)}}])
end.
clean_temporarily_blocked_table() ->
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 91fadb696..db7655ef8 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -2,7 +2,7 @@
%%% Created : 12 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -135,16 +135,16 @@ process_closed(#{server := LServer} = State, Reason) ->
%%%===================================================================
%%% xmpp_stream_in callbacks
%%%===================================================================
-tls_options(#{tls_options := TLSOpts, server_host := LServer}) ->
+tls_options(#{tls_options := TLSOpts, lserver := LServer}) ->
ejabberd_s2s:tls_options(LServer, TLSOpts).
-tls_required(#{server_host := LServer}) ->
+tls_required(#{lserver := LServer}) ->
ejabberd_s2s:tls_required(LServer).
-tls_enabled(#{server_host := LServer}) ->
+tls_enabled(#{lserver := LServer}) ->
ejabberd_s2s:tls_enabled(LServer).
-compress_methods(#{server_host := LServer}) ->
+compress_methods(#{lserver := LServer}) ->
case ejabberd_s2s:zlib_enabled(LServer) of
true -> [<<"zlib">>];
false -> []
@@ -181,7 +181,7 @@ handle_auth_success(RServer, Mech, _AuthModule,
?INFO_MSG("(~s) Accepted inbound s2s ~s authentication ~s -> ~s (~s)",
[xmpp_socket:pp(Socket), Mech, RServer, LServer,
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
- State1 = case ejabberd_s2s:allow_host(ServerHost, RServer) of
+ State1 = case ejabberd_s2s:allow_host(LServer, RServer) of
true ->
AuthDomains1 = sets:add_element(RServer, AuthDomains),
State0 = change_shaper(State, RServer),
@@ -327,7 +327,7 @@ check_to(#jid{lserver = LServer}, _State) ->
ejabberd_router:is_my_route(LServer).
-spec set_idle_timeout(state()) -> state().
-set_idle_timeout(#{server_host := LServer,
+set_idle_timeout(#{lserver := LServer,
established := true} = State) ->
Timeout = ejabberd_s2s:get_idle_timeout(LServer),
xmpp_stream_in:set_timeout(State, Timeout);
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index db8f3d0a3..d940284ef 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 0f40822b7..c2fa2b8c0 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl
index e42cb4121..cad04986c 100644
--- a/src/ejabberd_shaper.erl
+++ b/src/ejabberd_shaper.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sip.erl b/src/ejabberd_sip.erl
index f9061edbe..8f6aed55c 100644
--- a/src/ejabberd_sip.erl
+++ b/src/ejabberd_sip.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index d9e211656..2c7135365 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -47,11 +47,9 @@
disconnect_removed_user/2,
get_user_resources/2,
get_user_present_resources/2,
- set_presence/7,
- unset_presence/6,
+ set_presence/6,
+ unset_presence/5,
close_session_unset_presence/5,
- set_offline_info/5,
- get_offline_info/4,
dirty_get_sessions_list/0,
dirty_get_my_sessions_list/0,
get_vh_session_list/1,
@@ -68,6 +66,8 @@
get_session_sids/2,
get_user_info/2,
get_user_info/3,
+ set_user_info/5,
+ del_user_info/4,
get_user_ip/3,
get_max_user_sessions/2,
get_all_pids/0,
@@ -78,8 +78,7 @@
host_down/1,
make_sid/0,
clean_cache/1,
- config_reloaded/0,
- is_online/1
+ config_reloaded/0
]).
-export([init/1, handle_call/3, handle_cast/2,
@@ -91,6 +90,7 @@
-include("ejabberd_commands.hrl").
-include("ejabberd_sm.hrl").
+-include("ejabberd_stacktrace.hrl").
-callback init() -> ok | {error, any()}.
-callback set_session(#session{}) -> ok | {error, any()}.
@@ -141,11 +141,10 @@ route(Packet) ->
?DEBUG("hook dropped stanza:~n~s", [xmpp:pp(Packet)]);
Packet1 ->
try do_route(Packet1), ok
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to route packet:~n~s~nReason = ~p",
[xmpp:pp(Packet1),
- {E, {R, St}}])
+ {E, {R, ?EX_STACK(St)}}])
end
end.
@@ -211,14 +210,14 @@ get_user_resources(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Mod = get_sm_backend(LServer),
- Ss = online(get_sessions(Mod, LUser, LServer)),
+ Ss = get_sessions(Mod, LUser, LServer),
[element(3, S#session.usr) || S <- clean_session_list(Ss)].
-spec get_user_present_resources(binary(), binary()) -> [tuple()].
get_user_present_resources(LUser, LServer) ->
Mod = get_sm_backend(LServer),
- Ss = online(get_sessions(Mod, LUser, LServer)),
+ Ss = get_sessions(Mod, LUser, LServer),
[{S#session.priority, element(3, S#session.usr)}
|| S <- clean_session_list(Ss), is_integer(S#session.priority)].
@@ -229,7 +228,7 @@ get_user_ip(User, Server, Resource) ->
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
Mod = get_sm_backend(LServer),
- case online(get_sessions(Mod, LUser, LServer, LResource)) of
+ case get_sessions(Mod, LUser, LServer, LResource) of
[] ->
undefined;
Ss ->
@@ -242,7 +241,7 @@ get_user_info(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Mod = get_sm_backend(LServer),
- Ss = online(get_sessions(Mod, LUser, LServer)),
+ Ss = get_sessions(Mod, LUser, LServer),
[{LResource, [{node, node(Pid)}, {ts, Ts}, {pid, Pid},
{priority, Priority} | Info]}
|| #session{usr = {_, _, LResource},
@@ -257,7 +256,7 @@ get_user_info(User, Server, Resource) ->
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
Mod = get_sm_backend(LServer),
- case online(get_sessions(Mod, LUser, LServer, LResource)) of
+ case get_sessions(Mod, LUser, LServer, LResource) of
[] ->
offline;
Ss ->
@@ -269,27 +268,87 @@ get_user_info(User, Server, Resource) ->
|Session#session.info]
end.
+-spec set_user_info(binary(), binary(), binary(), atom(), term()) -> ok | {error, any()}.
+set_user_info(User, Server, Resource, Key, Val) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Server),
+ LResource = jid:resourceprep(Resource),
+ Mod = get_sm_backend(LServer),
+ case get_sessions(Mod, LUser, LServer, LResource) of
+ [] -> {error, notfound};
+ Ss ->
+ lists:foldl(
+ fun(#session{sid = {_, Pid},
+ info = Info} = Session, _) when Pid == self() ->
+ Info1 = lists:keystore(Key, 1, Info, {Key, Val}),
+ set_session(Session#session{info = Info1});
+ (_, Acc) ->
+ Acc
+ end, {error, not_owner}, Ss)
+ end.
+
+-spec del_user_info(binary(), binary(), binary(), atom()) -> ok | {error, any()}.
+del_user_info(User, Server, Resource, Key) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Server),
+ LResource = jid:resourceprep(Resource),
+ Mod = get_sm_backend(LServer),
+ case get_sessions(Mod, LUser, LServer, LResource) of
+ [] -> {error, notfound};
+ Ss ->
+ lists:foldl(
+ fun(#session{sid = {_, Pid},
+ info = Info} = Session, _) when Pid == self() ->
+ Info1 = lists:keydelete(Key, 1, Info),
+ set_session(Session#session{info = Info1});
+ (_, Acc) ->
+ Acc
+ end, {error, not_owner}, Ss)
+ end.
+
-spec set_presence(sid(), binary(), binary(), binary(),
- prio(), presence(), info()) -> ok.
+ prio(), presence()) -> ok | {error, notfound}.
-set_presence(SID, User, Server, Resource, Priority,
- Presence, Info) ->
- set_session(SID, User, Server, Resource, Priority,
- Info),
- ejabberd_hooks:run(set_presence_hook,
- jid:nameprep(Server),
- [User, Server, Resource, Presence]).
+set_presence(SID, User, Server, Resource, Priority, Presence) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Server),
+ LResource = jid:resourceprep(Resource),
+ Mod = get_sm_backend(LServer),
+ case get_sessions(Mod, LUser, LServer, LResource) of
+ [] -> {error, notfound};
+ Ss ->
+ case lists:keyfind(SID, #session.sid, Ss) of
+ #session{info = Info} ->
+ set_session(SID, User, Server, Resource, Priority, Info),
+ ejabberd_hooks:run(set_presence_hook,
+ LServer,
+ [User, Server, Resource, Presence]);
+ false ->
+ {error, notfound}
+ end
+ end.
-spec unset_presence(sid(), binary(), binary(),
- binary(), binary(), info()) -> ok.
+ binary(), binary()) -> ok | {error, notfound}.
-unset_presence(SID, User, Server, Resource, Status,
- Info) ->
- set_session(SID, User, Server, Resource, undefined,
- Info),
- ejabberd_hooks:run(unset_presence_hook,
- jid:nameprep(Server),
- [User, Server, Resource, Status]).
+unset_presence(SID, User, Server, Resource, Status) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Server),
+ LResource = jid:resourceprep(Resource),
+ Mod = get_sm_backend(LServer),
+ case get_sessions(Mod, LUser, LServer, LResource) of
+ [] -> {error, notfound};
+ Ss ->
+ case lists:keyfind(SID, #session.sid, Ss) of
+ #session{info = Info} ->
+ set_session(SID, User, Server, Resource, undefined, Info),
+ ejabberd_hooks:run(unset_presence_hook,
+ LServer,
+ [User, Server, Resource, Status]);
+ false ->
+ {error, notfound}
+ end
+ end.
-spec close_session_unset_presence(sid(), binary(), binary(),
binary(), binary()) -> ok.
@@ -316,7 +375,7 @@ get_session_sid(User, Server, Resource) ->
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
Mod = get_sm_backend(LServer),
- case online(get_sessions(Mod, LUser, LServer, LResource)) of
+ case get_sessions(Mod, LUser, LServer, LResource) of
[] ->
none;
Ss ->
@@ -330,43 +389,15 @@ get_session_sids(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
Mod = get_sm_backend(LServer),
- Sessions = online(get_sessions(Mod, LUser, LServer)),
+ Sessions = get_sessions(Mod, LUser, LServer),
[SID || #session{sid = SID} <- Sessions].
--spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok.
-
-set_offline_info(SID, User, Server, Resource, Info) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Server),
- LResource = jid:resourceprep(Resource),
- set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]).
-
--spec get_offline_info(erlang:timestamp(), binary(), binary(),
- binary()) -> none | info().
-
-get_offline_info(Time, User, Server, Resource) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Server),
- LResource = jid:resourceprep(Resource),
- Mod = get_sm_backend(LServer),
- case get_sessions(Mod, LUser, LServer, LResource) of
- [#session{sid = {Time, _}, info = Info}] ->
- case proplists:get_bool(offline, Info) of
- true ->
- Info;
- false ->
- none
- end;
- _ ->
- none
- end.
-
-spec dirty_get_sessions_list() -> [ljid()].
dirty_get_sessions_list() ->
lists:flatmap(
fun(Mod) ->
- [S#session.usr || S <- online(get_sessions(Mod))]
+ [S#session.usr || S <- get_sessions(Mod)]
end, get_sm_backends()).
-spec dirty_get_my_sessions_list() -> [#session{}].
@@ -374,7 +405,7 @@ dirty_get_sessions_list() ->
dirty_get_my_sessions_list() ->
lists:flatmap(
fun(Mod) ->
- [S || S <- online(get_sessions(Mod)),
+ [S || S <- get_sessions(Mod),
node(element(2, S#session.sid)) == node()]
end, get_sm_backends()).
@@ -383,14 +414,14 @@ dirty_get_my_sessions_list() ->
get_vh_session_list(Server) ->
LServer = jid:nameprep(Server),
Mod = get_sm_backend(LServer),
- [S#session.usr || S <- online(get_sessions(Mod, LServer))].
+ [S#session.usr || S <- get_sessions(Mod, LServer)].
-spec get_all_pids() -> [pid()].
get_all_pids() ->
lists:flatmap(
fun(Mod) ->
- [element(2, S#session.sid) || S <- online(get_sessions(Mod))]
+ [element(2, S#session.sid) || S <- get_sessions(Mod)]
end, get_sm_backends()).
-spec get_vh_session_number(binary()) -> non_neg_integer().
@@ -398,7 +429,7 @@ get_all_pids() ->
get_vh_session_number(Server) ->
LServer = jid:nameprep(Server),
Mod = get_sm_backend(LServer),
- length(online(get_sessions(Mod, LServer))).
+ length(get_sessions(Mod, LServer)).
%% Why the hell do we have so many similar kicks?
c2s_handle_info(#{lang := Lang} = State, replaced) ->
@@ -514,9 +545,13 @@ set_session(SID, User, Server, Resource, Priority, Info) ->
LResource = jid:resourceprep(Resource),
US = {LUser, LServer},
USR = {LUser, LServer, LResource},
+ set_session(#session{sid = SID, usr = USR, us = US,
+ priority = Priority, info = Info}).
+
+-spec set_session(#session{}) -> ok | {error, any()}.
+set_session(#session{us = {LUser, LServer}} = Session) ->
Mod = get_sm_backend(LServer),
- case Mod:set_session(#session{sid = SID, usr = USR, us = US,
- priority = Priority, info = Info}) of
+ case Mod:set_session(Session) of
ok ->
case use_cache(Mod, LServer) of
true ->
@@ -579,16 +614,6 @@ delete_session(Mod, #session{usr = {LUser, LServer, _}} = Session) ->
ok
end.
--spec online([#session{}]) -> [#session{}].
-
-online(Sessions) ->
- lists:filter(fun is_online/1, Sessions).
-
--spec is_online(#session{}) -> boolean().
-
-is_online(#session{info = Info}) ->
- not proplists:get_bool(offline, Info).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec do_route(jid(), term()) -> any().
do_route(#jid{lresource = <<"">>} = To, Term) ->
@@ -600,7 +625,7 @@ do_route(To, Term) ->
?DEBUG("broadcasting ~p to ~s", [Term, jid:encode(To)]),
{U, S, R} = jid:tolower(To),
Mod = get_sm_backend(S),
- case online(get_sessions(Mod, U, S, R)) of
+ case get_sessions(Mod, U, S, R) of
[] ->
?DEBUG("dropping broadcast to unavailable resourse: ~p", [Term]);
Ss ->
@@ -631,7 +656,7 @@ do_route(#presence{to = To, type = T} = Packet)
ejabberd_c2s:route(Pid, {route, Packet1});
(_) ->
ok
- end, online(get_sessions(Mod, LUser, LServer)));
+ end, get_sessions(Mod, LUser, LServer));
false ->
ok
end;
@@ -660,7 +685,7 @@ do_route(Packet) ->
To = xmpp:get_to(Packet),
{LUser, LServer, LResource} = jid:tolower(To),
Mod = get_sm_backend(LServer),
- case online(get_sessions(Mod, LUser, LServer, LResource)) of
+ case get_sessions(Mod, LUser, LServer, LResource) of
[] ->
case Packet of
#message{type = T} when T == chat; T == normal ->
@@ -708,8 +733,8 @@ route_message(#message{to = To, type = Type} = Packet) ->
(P >= 0) and (Type == headline) ->
LResource = jid:resourceprep(R),
Mod = get_sm_backend(LServer),
- case online(get_sessions(Mod, LUser, LServer,
- LResource)) of
+ case get_sessions(Mod, LUser, LServer,
+ LResource) of
[] ->
ok; % Race condition
Ss ->
@@ -780,13 +805,9 @@ check_for_sessions_to_replace(User, Server, Resource) ->
check_existing_resources(LUser, LServer, LResource) ->
Mod = get_sm_backend(LServer),
Ss = get_sessions(Mod, LUser, LServer, LResource),
- {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss),
- lists:foreach(fun(S) ->
- delete_session(Mod, S)
- end, OfflineSs),
- if OnlineSs == [] -> ok;
+ if Ss == [] -> ok;
true ->
- SIDs = [SID || #session{sid = SID} <- OnlineSs],
+ SIDs = [SID || #session{sid = SID} <- Ss],
MaxSID = lists:max(SIDs),
lists:foreach(fun ({_, Pid} = S) when S /= MaxSID ->
ejabberd_c2s:route(Pid, replaced);
@@ -806,22 +827,17 @@ get_resource_sessions(User, Server, Resource) ->
LServer = jid:nameprep(Server),
LResource = jid:resourceprep(Resource),
Mod = get_sm_backend(LServer),
- [S#session.sid || S <- online(get_sessions(Mod, LUser, LServer, LResource))].
+ [S#session.sid || S <- get_sessions(Mod, LUser, LServer, LResource)].
-spec check_max_sessions(binary(), binary()) -> ok | replaced.
check_max_sessions(LUser, LServer) ->
Mod = get_sm_backend(LServer),
Ss = get_sessions(Mod, LUser, LServer),
- {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss),
MaxSessions = get_max_user_sessions(LUser, LServer),
- if length(OnlineSs) =< MaxSessions -> ok;
+ if length(Ss) =< MaxSessions -> ok;
true ->
- #session{sid = {_, Pid}} = lists:min(OnlineSs),
+ #session{sid = {_, Pid}} = lists:min(Ss),
ejabberd_c2s:route(Pid, replaced)
- end,
- if length(OfflineSs) =< MaxSessions -> ok;
- true ->
- delete_session(Mod, lists:min(OfflineSs))
end.
%% Get the user_max_session setting
@@ -843,7 +859,7 @@ get_max_user_sessions(LUser, Host) ->
force_update_presence({LUser, LServer}) ->
Mod = get_sm_backend(LServer),
- Ss = online(get_sessions(Mod, LUser, LServer)),
+ Ss = get_sessions(Mod, LUser, LServer),
lists:foreach(fun (#session{sid = {_, Pid}}) ->
ejabberd_c2s:resend_presence(Pid)
end,
diff --git a/src/ejabberd_sm_mnesia.erl b/src/ejabberd_sm_mnesia.erl
index 43b49202e..5e36a3d8a 100644
--- a/src/ejabberd_sm_mnesia.erl
+++ b/src/ejabberd_sm_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 9 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_redis.erl b/src/ejabberd_sm_redis.erl
index a93be86bb..e4bab3902 100644
--- a/src/ejabberd_sm_redis.erl
+++ b/src/ejabberd_sm_redis.erl
@@ -4,7 +4,7 @@
%%% Created : 11 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_riak.erl b/src/ejabberd_sm_riak.erl
index a8150d938..36c936976 100644
--- a/src/ejabberd_sm_riak.erl
+++ b/src/ejabberd_sm_riak.erl
@@ -3,7 +3,7 @@
%%% Created : 15 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl
index bdc32a27c..8c3efc9b3 100644
--- a/src/ejabberd_sm_sql.erl
+++ b/src/ejabberd_sm_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 9 Mar 2015 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl
index 8b28f0735..153479026 100644
--- a/src/ejabberd_sql.erl
+++ b/src/ejabberd_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -37,12 +37,12 @@
sql_query_t/1,
sql_transaction/2,
sql_bloc/2,
- abort/1,
- restart/1,
- use_new_schema/0,
- sql_query_to_iolist/1,
+ abort/1,
+ restart/1,
+ use_new_schema/0,
+ sql_query_to_iolist/1,
escape/1,
- standard_escape/1,
+ standard_escape/1,
escape_like/1,
escape_like_arg/1,
escape_like_arg_circumflex/1,
@@ -55,7 +55,8 @@
freetds_config/0,
odbcinst_config/0,
init_mssql/1,
- keep_alive/2]).
+ keep_alive/2,
+ to_list/2]).
%% gen_fsm callbacks
-export([init/1, handle_event/3, handle_sync_event/4,
@@ -68,6 +69,7 @@
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(state,
{db_ref = self() :: pid(),
@@ -176,7 +178,7 @@ keep_alive(Host, PID) ->
{sql_cmd, {sql_query, ?KEEPALIVE_QUERY},
p1_time_compat:monotonic_time(milli_seconds)},
query_timeout(Host)) of
- {selected,[<<"1">>],[[<<"1">>]]} ->
+ {selected,_,[[<<"1">>]]} ->
ok;
_Err ->
?ERROR_MSG("keep alive query failed, closing connection: ~p", [_Err]),
@@ -258,6 +260,10 @@ to_bool(true) -> true;
to_bool(1) -> true;
to_bool(_) -> false.
+to_list(EscapeFun, Val) ->
+ Escaped = lists:join(<<",">>, lists:map(EscapeFun, Val)),
+ [<<"(">>, Escaped, <<")">>].
+
encode_term(Term) ->
escape(list_to_binary(
erl_prettypr:format(erl_syntax:abstract(Term),
@@ -511,24 +517,26 @@ outer_transaction(F, NRestarts, _Reason) ->
end,
sql_query_internal([<<"begin;">>]),
put(?NESTING_KEY, PreviousNestingLevel + 1),
- Result = (catch F()),
- put(?NESTING_KEY, PreviousNestingLevel),
- case Result of
- {aborted, Reason} when NRestarts > 0 ->
- sql_query_internal([<<"rollback;">>]),
- outer_transaction(F, NRestarts - 1, Reason);
- {aborted, Reason} when NRestarts =:= 0 ->
- ?ERROR_MSG("SQL transaction restarts exceeded~n** "
- "Restarts: ~p~n** Last abort reason: "
- "~p~n** Stacktrace: ~p~n** When State "
- "== ~p",
- [?MAX_TRANSACTION_RESTARTS, Reason,
- erlang:get_stacktrace(), get(?STATE_KEY)]),
- sql_query_internal([<<"rollback;">>]),
- {aborted, Reason};
- {'EXIT', Reason} ->
- sql_query_internal([<<"rollback;">>]), {aborted, Reason};
- Res -> sql_query_internal([<<"commit;">>]), {atomic, Res}
+ try F() of
+ Res ->
+ sql_query_internal([<<"commit;">>]),
+ {atomic, Res}
+ catch
+ ?EX_RULE(throw, {aborted, Reason}, _) when NRestarts > 0 ->
+ sql_query_internal([<<"rollback;">>]),
+ outer_transaction(F, NRestarts - 1, Reason);
+ ?EX_RULE(throw, {aborted, Reason}, Stack) when NRestarts =:= 0 ->
+ ?ERROR_MSG("SQL transaction restarts exceeded~n** "
+ "Restarts: ~p~n** Last abort reason: "
+ "~p~n** Stacktrace: ~p~n** When State "
+ "== ~p",
+ [?MAX_TRANSACTION_RESTARTS, Reason,
+ ?EX_STACK(Stack), get(?STATE_KEY)]),
+ sql_query_internal([<<"rollback;">>]),
+ {aborted, Reason};
+ ?EX_RULE(exit, Reason, _) ->
+ sql_query_internal([<<"rollback;">>]),
+ {aborted, Reason}
end.
execute_bloc(F) ->
@@ -594,10 +602,9 @@ sql_query_internal(#sql_query{} = Query) ->
{error, <<"killed">>};
exit:{normal, _} ->
{error, <<"terminated unexpectedly">>};
- Class:Reason ->
- ST = erlang:get_stacktrace(),
+ ?EX_RULE(Class, Reason, Stack) ->
?ERROR_MSG("Internal error while processing SQL query: ~p",
- [{Class, Reason, ST}]),
+ [{Class, Reason, ?EX_STACK(Stack)}]),
{error, <<"internal error">>}
end,
check_error(Res, Query);
@@ -732,12 +739,11 @@ sql_query_format_res({selected, _, Rows}, SQLQuery) ->
try
[(SQLQuery#sql_query.format_res)(Row)]
catch
- Class:Reason ->
- ST = erlang:get_stacktrace(),
+ ?EX_RULE(Class, Reason, Stack) ->
?ERROR_MSG("Error while processing "
"SQL query result: ~p~n"
"row: ~p",
- [{Class, Reason, ST}, Row]),
+ [{Class, Reason, ?EX_STACK(Stack)}, Row]),
[]
end
end, Rows),
diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl
index eb7905bf0..0ae04c64d 100644
--- a/src/ejabberd_sql_pt.erl
+++ b/src/ejabberd_sql_pt.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Jan 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -306,6 +306,20 @@ parse1([$%, $( | S], Acc, State) ->
false ->
append_string("0=0", State3)
end;
+ {list, InternalType} ->
+ Convert = erl_syntax:application(
+ erl_syntax:atom(ejabberd_sql),
+ erl_syntax:atom(to_list),
+ [erl_syntax:record_access(
+ erl_syntax:variable(?ESCAPE_VAR),
+ erl_syntax:atom(?ESCAPE_RECORD),
+ erl_syntax:atom(InternalType)),
+ erl_syntax:variable(Name)]),
+ State2#state{'query' = [{var, Var} | State2#state.'query'],
+ args = [Convert | State2#state.args],
+ params = [Var | State2#state.params],
+ param_pos = State2#state.param_pos + 1,
+ used_vars = [Name | State2#state.used_vars]};
_ ->
Convert =
erl_syntax:application(
@@ -335,6 +349,19 @@ parse_name(S, IsArg, State) ->
parse_name([], _Acc, _Depth, _IsArg, State) ->
throw({error, State#state.loc,
"expected ')', found end of string"});
+parse_name([$), $l, T | S], Acc, 0, true, State) ->
+ Type = case T of
+ $d -> {list, integer};
+ $s -> {list, string};
+ $b -> {list, boolean};
+ _ ->
+ throw({error, State#state.loc,
+ ["unknown type specifier 'l", T, "'"]})
+ end,
+ {lists:reverse(Acc), Type, S, State};
+parse_name([$), $l, T | _], _Acc, 0, false, State) ->
+ throw({error, State#state.loc,
+ ["list type 'l", T, "' is not allowed for outputs"]});
parse_name([$), T | S], Acc, 0, IsArg, State) ->
Type =
case T of
diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl
index ee889bd21..f16c23a00 100644
--- a/src/ejabberd_sql_sup.erl
+++ b/src/ejabberd_sql_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Dec 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl
index 1e00c85f5..24f3e696b 100644
--- a/src/ejabberd_stun.erl
+++ b/src/ejabberd_stun.erl
@@ -5,7 +5,7 @@
%%% Created : 8 May 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index 86a14c78a..edf15e438 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl
index cf50209fb..59ba6f806 100644
--- a/src/ejabberd_system_monitor.erl
+++ b/src/ejabberd_system_monitor.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Mar 2007 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_tmp_sup.erl b/src/ejabberd_tmp_sup.erl
index aa8a7e0f2..8f3e1f06c 100644
--- a/src/ejabberd_tmp_sup.erl
+++ b/src/ejabberd_tmp_sup.erl
@@ -5,7 +5,7 @@
%%% Created : 18 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_update.erl b/src/ejabberd_update.erl
index 4c360011f..c7aa652c1 100644
--- a/src/ejabberd_update.erl
+++ b/src/ejabberd_update.erl
@@ -5,7 +5,7 @@
%%% Created : 27 Jan 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_web.erl b/src/ejabberd_web.erl
index a0291fc6f..3025dc146 100644
--- a/src/ejabberd_web.erl
+++ b/src/ejabberd_web.erl
@@ -6,7 +6,7 @@
%%% Created : 28 Feb 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl
index 03b11c9f6..0bcb87153 100644
--- a/src/ejabberd_web_admin.erl
+++ b/src/ejabberd_web_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Apr 2004 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -305,7 +305,7 @@ make_xhtml(Els, Host, Node, Lang, JID) ->
#xmlel{name = <<"script">>,
attrs =
[{<<"src">>,
- <<Base/binary, "/additions.js">>},
+ <<Base/binary, "additions.js">>},
{<<"type">>, <<"text/javascript">>}],
children = [?C(<<" ">>)]},
#xmlel{name = <<"link">>,
@@ -337,7 +337,7 @@ make_xhtml(Els, Host, Node, Lang, JID) ->
[?XAE(<<"div">>, [{<<"id">>, <<"copyright">>}],
[?XE(<<"p">>,
[?AC(<<"https://www.ejabberd.im/">>, <<"ejabberd">>),
- ?C(<<" (c) 2002-2018 ">>),
+ ?C(<<" (c) 2002-2019 ">>),
?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)]
)])])])]}}.
diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl
index 5a329797b..506ff142b 100644
--- a/src/ejabberd_websocket.erl
+++ b/src/ejabberd_websocket.erl
@@ -33,7 +33,7 @@
%%% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%%% ==========================================================================================================
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%----------------------------------------------------------------------
-module(ejabberd_websocket).
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index 11da7f369..cda2864df 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Aug 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl
index 40793f405..546b86879 100644
--- a/src/ejd2sql.erl
+++ b/src/ejd2sql.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Aug 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_filter.erl b/src/eldap_filter.erl
index 32cb85fd9..7ab634d95 100644
--- a/src/eldap_filter.erl
+++ b/src/eldap_filter.erl
@@ -6,7 +6,7 @@
%%% Author: Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_pool.erl b/src/eldap_pool.erl
index 3e8e35b84..b6421c230 100644
--- a/src/eldap_pool.erl
+++ b/src/eldap_pool.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Nov 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl
index d757aa31b..47e18aac3 100644
--- a/src/eldap_utils.erl
+++ b/src/eldap_utils.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl
index bbe43a2d3..16d7f954c 100644
--- a/src/elixir_logger_backend.erl
+++ b/src/elixir_logger_backend.erl
@@ -5,7 +5,7 @@
%%% Created : 9 March 2016 by Mickael Remond <mremond@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/ext_mod.erl b/src/ext_mod.erl
index 02165bea8..0d734e08c 100644
--- a/src/ext_mod.erl
+++ b/src/ext_mod.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Feb 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2006-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2006-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/extauth.erl b/src/extauth.erl
index f77d3d2cc..ace340f72 100644
--- a/src/extauth.erl
+++ b/src/extauth.erl
@@ -2,7 +2,7 @@
%%% Created : 7 May 2018 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/extauth_sup.erl b/src/extauth_sup.erl
index c9dc0cdff..f4220fa81 100644
--- a/src/extauth_sup.erl
+++ b/src/extauth_sup.erl
@@ -2,7 +2,7 @@
%%% Created : 7 May 2018 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl
index f6a6744fd..38aa32e36 100644
--- a/src/gen_iq_handler.erl
+++ b/src/gen_iq_handler.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -40,6 +40,7 @@
-include("logger.hrl").
-include("xmpp.hrl").
-include("translate.hrl").
+-include("ejabberd_stacktrace.hrl").
-type component() :: ejabberd_sm | ejabberd_local.
@@ -113,10 +114,9 @@ process_iq(_Host, Module, Function, IQ) ->
ejabberd_router:route(ResIQ);
ignore ->
ok
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to process iq:~n~s~nReason = ~p",
- [xmpp:pp(IQ), {E, {R, St}}]),
+ [xmpp:pp(IQ), {E, {R, ?EX_STACK(St)}}]),
Txt = <<"Module failed to handle the query">>,
Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang),
ejabberd_router:route_error(IQ, Err)
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index cf107f7b0..4d815073c 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -58,6 +58,7 @@
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
+-include("ejabberd_stacktrace.hrl").
-record(ejabberd_module,
{module_host = {undefined, <<"">>} :: {atom(), binary()},
@@ -217,8 +218,8 @@ start_module(Host, Module, Opts0, Order, NeedValidation) ->
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
Err -> erlang:error({bad_return, Module, Err})
end
- catch Class:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?EX_RULE(Class, Reason, Stack) ->
+ StackTrace = ?EX_STACK(Stack),
ets:delete(ejabberd_modules, {Module, Host}),
ErrorText = format_module_error(
Module, start, 2,
@@ -282,8 +283,8 @@ reload_module(Host, Module, NewOpts, OldOpts, Order) ->
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
Err -> erlang:error({bad_return, Module, Err})
end
- catch Class:Reason ->
- StackTrace = erlang:get_stacktrace(),
+ catch ?EX_RULE(Class, Reason, Stack) ->
+ StackTrace = ?EX_STACK(Stack),
ErrorText = format_module_error(
Module, reload, 3,
NewOpts, Class, Reason,
diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl
index b54cc569b..624b2fd07 100644
--- a/src/gen_pubsub_node.erl
+++ b/src/gen_pubsub_node.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl
index bcf52855c..aba78a89a 100644
--- a/src/gen_pubsub_nodetree.erl
+++ b/src/gen_pubsub_nodetree.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/jd2ejd.erl b/src/jd2ejd.erl
index 35ebeab8d..0122a7f2d 100644
--- a/src/jd2ejd.erl
+++ b/src/jd2ejd.erl
@@ -5,7 +5,7 @@
%%% Created : 2 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -111,7 +111,6 @@ process_xdb(User, Server,
xdb_data(_User, _Server, {xmlcdata, _CData}) -> ok;
xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
From = jid:make(User, Server),
- LUser = From#jid.luser,
LServer = From#jid.lserver,
case fxml:get_attr_s(<<"xmlns">>, Attrs) of
?NS_AUTH ->
@@ -142,7 +141,7 @@ xdb_data(User, Server, #xmlel{attrs = Attrs} = El) ->
(_) -> true
end, Attrs),
catch mod_private:set_data(
- LUser, LServer,
+ From,
[{XMLNS, El#xmlel{attrs = NewAttrs}}]);
_ ->
?DEBUG("jd2ejd: Unknown namespace \"~s\"~n", [XMLNS])
diff --git a/src/misc.erl b/src/misc.erl
index 5aa281565..3c5407cd0 100644
--- a/src/misc.erl
+++ b/src/misc.erl
@@ -8,7 +8,7 @@
%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl
index 9071ca817..c77d6a047 100644
--- a/src/mod_adhoc.erl
+++ b/src/mod_adhoc.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 5db82f82a..80bd8aa62 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -5,7 +5,7 @@
%%% Created : 10 Aug 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -560,8 +560,10 @@ get_commands_spec() ->
desc = "Push template roster from file to a user",
longdesc = "The text file must contain an erlang term: a list "
"of tuples with username, servername, group and nick. Example:\n"
- "[{\"user1\", \"localhost\", \"Workers\", \"User 1\"},\n"
- " {\"user2\", \"localhost\", \"Workers\", \"User 2\"}].",
+ "[{<<\"user1\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 1\">>},\n"
+ " {<<\"user2\">>, <<\"localhost\">>, <<\"Workers\">>, <<\"User 2\">>}].\n"
+ "When using UTF8 character encoding add /utf8 to certain string. Example:\n"
+ "[{<<\"user2\">>, <<\"localhost\">>, <<\"Workers\"/utf8>>, <<\"User 2\"/utf8>>}].",
module = ?MODULE, function = push_roster,
args = [{file, binary}, {user, binary}, {host, binary}],
args_example = [<<"/home/ejabberd/roster.txt">>, <<"user1">>, <<"localhost">>],
@@ -1388,8 +1390,8 @@ private_set(Username, Host, ElementString) ->
private_set2(Username, Host, Xml) ->
NS = fxml:get_tag_attr_s(<<"xmlns">>, Xml),
- mod_private:set_data(jid:nodeprep(Username), jid:nameprep(Host),
- [{NS, Xml}]).
+ JID = jid:make(Username, Host),
+ mod_private:set_data(JID, [{NS, Xml}]).
%%%
%%% Shared Roster Groups
diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl
index 3c036f341..0212dab25 100644
--- a/src/mod_admin_update_sql.erl
+++ b/src/mod_admin_update_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Aug 2017 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index b8ca970c9..c8d3e9e74 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_announce_mnesia.erl b/src/mod_announce_mnesia.erl
index f3b8aef7c..ea7c54f36 100644
--- a/src/mod_announce_mnesia.erl
+++ b/src/mod_announce_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_announce_riak.erl b/src/mod_announce_riak.erl
index 55959b58b..1d2435151 100644
--- a/src/mod_announce_riak.erl
+++ b/src/mod_announce_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl
index c4f1ba86f..60c3edcf6 100644
--- a/src/mod_announce_sql.erl
+++ b/src/mod_announce_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl
index d5f24e75d..e706a23c3 100644
--- a/src/mod_avatar.erl
+++ b/src/mod_avatar.erl
@@ -3,7 +3,7 @@
%%% Created : 13 Sep 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl
index 6e9d1097a..486bea7fd 100644
--- a/src/mod_block_strangers.erl
+++ b/src/mod_block_strangers.erl
@@ -5,7 +5,7 @@
%%% Created : 25 Dec 2016 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -222,31 +222,11 @@ check_subscription(From, To) ->
end.
sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
- case ?SETS:next(sets_iterator_from(LBJID, Set)) of
+ case ?SETS:next(?SETS:iterator_from(LBJID, Set)) of
{{U, S, _}, _} -> true;
_ -> false
end.
--ifdef(GB_SETS_ITERATOR_FROM).
-sets_iterator_from(Element, Set) ->
- ?SETS:iterator_from(Element, Set).
--else.
-%% Copied from gb_sets.erl
-%% TODO: Remove after dropping R17 support
-sets_iterator_from(S, {_, T}) ->
- iterator_from(S, T, []).
-
-iterator_from(S, {K, _, T}, As) when K < S ->
- iterator_from(S, T, As);
-iterator_from(_, {_, nil, _} = T, As) ->
- [T | As];
-iterator_from(S, {_, L, _} = T, As) ->
- iterator_from(S, L, [T | As]);
-iterator_from(_, nil, As) ->
- As.
--endif.
-
-
depends(_Host, _Opts) ->
[].
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index d428e7d28..b1856c937 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl
index a393597eb..03bdc6e15 100644
--- a/src/mod_bosh.erl
+++ b/src/mod_bosh.erl
@@ -7,7 +7,7 @@
%%% Created : 20 Jul 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_mnesia.erl b/src/mod_bosh_mnesia.erl
index fdd7225ca..a0da1867f 100644
--- a/src/mod_bosh_mnesia.erl
+++ b/src/mod_bosh_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 12 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_redis.erl b/src/mod_bosh_redis.erl
index 7c7cf6578..b94322194 100644
--- a/src/mod_bosh_redis.erl
+++ b/src/mod_bosh_redis.erl
@@ -5,7 +5,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_riak.erl b/src/mod_bosh_riak.erl
index df376f3d1..7ebd1bd6f 100644
--- a/src/mod_bosh_riak.erl
+++ b/src/mod_bosh_riak.erl
@@ -3,7 +3,7 @@
%%% Created : 15 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl
index c32714c87..4ec65e779 100644
--- a/src/mod_bosh_sql.erl
+++ b/src/mod_bosh_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 28 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps.erl b/src/mod_caps.erl
index 3d2b52dac..96b44cd68 100644
--- a/src/mod_caps.erl
+++ b/src/mod_caps.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Oct 2006 by Magnus Henoch <henoch@dtek.chalmers.se>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps_mnesia.erl b/src/mod_caps_mnesia.erl
index c44834fc5..9855b1fc2 100644
--- a/src/mod_caps_mnesia.erl
+++ b/src/mod_caps_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps_riak.erl b/src/mod_caps_riak.erl
index 1d94ceee4..37a29ff7f 100644
--- a/src/mod_caps_riak.erl
+++ b/src/mod_caps_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl
index 01da67158..b0829156e 100644
--- a/src/mod_caps_sql.erl
+++ b/src/mod_caps_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index 73034ec3f..72c098570 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -7,7 +7,7 @@
%%% {mod_carboncopy, []}
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -35,24 +35,18 @@
-export([start/2, stop/1, reload/3]).
-export([user_send_packet/1, user_receive_packet/1,
- iq_handler/1, remove_connection/4, disco_features/5,
- is_carbon_copy/1, mod_opt_type/1, depends/2, clean_cache/1,
+ iq_handler/1, disco_features/5,
+ is_carbon_copy/1, mod_opt_type/1, depends/2,
mod_options/1]).
+-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]).
+%% For debugging purposes
+-export([list/2]).
-include("logger.hrl").
-include("xmpp.hrl").
--include("mod_carboncopy.hrl").
-type direction() :: sent | received.
-
--callback init(binary(), gen_mod:opts()) -> any().
--callback enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
--callback disable(binary(), binary(), binary()) -> ok | {error, any()}.
--callback list(binary(), binary()) -> [{binary(), binary(), node()}].
--callback use_cache(binary()) -> boolean().
--callback cache_nodes(binary()) -> [node()].
-
--optional_callbacks([use_cache/1, cache_nodes/1]).
+-type c2s_state() :: ejabberd_c2s:state().
-spec is_carbon_copy(stanza()) -> boolean().
is_carbon_copy(#message{meta = #{carbon_copy := true}}) ->
@@ -60,16 +54,14 @@ is_carbon_copy(#message{meta = #{carbon_copy := true}}) ->
is_carbon_copy(_) ->
false.
-start(Host, Opts) ->
+start(Host, _Opts) ->
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
- Mod = gen_mod:ram_db_mod(Host, ?MODULE),
- init_cache(Mod, Host, Opts),
- Mod:init(Host, Opts),
- clean_cache(),
- 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:add(user_send_packet,Host, ?MODULE, user_send_packet, 89),
ejabberd_hooks:add(user_receive_packet,Host, ?MODULE, user_receive_packet, 89),
+ ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50),
+ ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50),
+ ejabberd_hooks:add(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50),
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_CARBONS_2, ?MODULE, iq_handler).
stop(Host) ->
@@ -78,22 +70,12 @@ stop(Host) ->
%% why priority 89: to define clearly that we must run BEFORE mod_logdb hook (90)
ejabberd_hooks:delete(user_send_packet,Host, ?MODULE, user_send_packet, 89),
ejabberd_hooks:delete(user_receive_packet,Host, ?MODULE, user_receive_packet, 89),
- ejabberd_hooks:delete(unset_presence_hook,Host, ?MODULE, remove_connection, 10).
+ ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, c2s_copy_session, 50),
+ ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE, c2s_session_resumed, 50),
+ ejabberd_hooks:delete(c2s_session_opened, Host, ?MODULE, c2s_session_opened, 50).
-reload(Host, NewOpts, OldOpts) ->
- 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 ->
- ok
- end,
- case use_cache(NewMod, Host) of
- true ->
- ets_cache:new(?CARBONCOPY_CACHE, cache_opts(NewOpts));
- false ->
- ok
- end.
+reload(_Host, _NewOpts, _OldOpts) ->
+ ok.
-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
jid(), jid(), binary(), binary()) ->
@@ -113,19 +95,13 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
is_record(El, carbons_disable) ->
{U, S, R} = jid:tolower(From),
Result = case El of
- #carbons_enable{} ->
- ?DEBUG("Carbons enabled for user ~s@~s/~s", [U,S,R]),
- enable(S, U, R, ?NS_CARBONS_2);
- #carbons_disable{} ->
- ?DEBUG("Carbons disabled for user ~s@~s/~s", [U,S,R]),
- disable(S, U, R)
+ #carbons_enable{} -> enable(S, U, R, ?NS_CARBONS_2);
+ #carbons_disable{} -> disable(S, U, R)
end,
case Result of
ok ->
- ?DEBUG("carbons IQ result: ok", []),
xmpp:make_iq_result(IQ);
- {error,_Error} ->
- ?ERROR_MSG("Error enabling / disabling carbons: ~p", [Result]),
+ {error, _} ->
Txt = <<"Database failure">>,
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end;
@@ -155,6 +131,29 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
Pkt -> {Pkt, C2SState}
end.
+-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
+c2s_copy_session(State, #{user := U, server := S, resource := R}) ->
+ case ejabberd_sm:get_user_info(U, S, R) of
+ offline -> State;
+ Info ->
+ case lists:keyfind(carboncopy, 1, Info) of
+ {_, CC} -> State#{carboncopy => CC};
+ false -> State
+ end
+ end.
+
+-spec c2s_session_resumed(c2s_state()) -> c2s_state().
+c2s_session_resumed(#{user := U, server := S, resource := R,
+ carboncopy := CC} = State) ->
+ ejabberd_sm:set_user_info(U, S, R, carboncopy, CC),
+ maps:remove(carboncopy, State);
+c2s_session_resumed(State) ->
+ State.
+
+-spec c2s_session_opened(c2s_state()) -> c2s_state().
+c2s_session_opened(State) ->
+ maps:remove(carboncopy, State).
+
% Modified from original version:
% - registered to the user_send_packet hook, to be called only once even for multicast
% - do not support "private" message mode, and do not modify the original packet in any way
@@ -180,12 +179,6 @@ check_and_forward(JID, To, Packet, Direction)->
Packet
end.
--spec remove_connection(binary(), binary(), binary(), binary()) -> ok.
-remove_connection(User, Server, Resource, _Status)->
- disable(Server, User, Resource),
- ok.
-
-
%%% Internal
%% Direction = received | sent <received xmlns='urn:xmpp:carbons:1'/>
-spec send_copies(jid(), jid(), message(), direction()) -> ok.
@@ -248,22 +241,26 @@ build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
-spec enable(binary(), binary(), binary(), binary()) -> ok | {error, any()}.
enable(Host, U, R, CC)->
- ?DEBUG("enabling for ~p", [U]),
- Mod = gen_mod:ram_db_mod(Host, ?MODULE),
- case Mod:enable(U, Host, R, CC) of
- ok ->
- delete_cache(Mod, U, Host);
- {error, _} = Err ->
+ ?DEBUG("Enabling carbons for ~s@~s/~s", [U, Host, R]),
+ case ejabberd_sm:set_user_info(U, Host, R, carboncopy, CC) of
+ ok -> ok;
+ {error, Reason} = Err ->
+ ?ERROR_MSG("Failed to disable carbons for ~s@~s/~s: ~p",
+ [U, Host, R, Reason]),
Err
end.
-spec disable(binary(), binary(), binary()) -> ok | {error, any()}.
disable(Host, U, R)->
- ?DEBUG("disabling for ~p", [U]),
- Mod = gen_mod:ram_db_mod(Host, ?MODULE),
- Res = Mod:disable(U, Host, R),
- delete_cache(Mod, U, Host),
- Res.
+ ?DEBUG("Disabling carbons for ~s@~s/~s", [U, Host, R]),
+ case ejabberd_sm:del_user_info(U, Host, R, carboncopy) of
+ ok -> ok;
+ {error, notfound} -> ok;
+ {error, Reason} = Err ->
+ ?ERROR_MSG("Failed to disable carbons for ~s@~s/~s: ~p",
+ [U, Host, R, Reason]),
+ Err
+ end.
-spec complete_packet(jid(), message(), direction()) -> message().
complete_packet(From, #message{from = undefined} = Msg, sent) ->
@@ -291,99 +288,30 @@ is_received_muc_pm(_To, Packet, received) ->
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
list(User, Server) ->
- Mod = gen_mod:ram_db_mod(Server, ?MODULE),
- case use_cache(Mod, Server) of
- true ->
- case ets_cache:lookup(
- ?CARBONCOPY_CACHE, {User, Server},
- fun() ->
- case Mod:list(User, Server) of
- {ok, L} when L /= [] -> {ok, L};
- _ -> error
- end
- end) of
- {ok, L} -> [{Resource, NS} || {Resource, NS, _} <- L];
- error -> []
- end;
- false ->
- case Mod:list(User, Server) of
- {ok, L} -> [{Resource, NS} || {Resource, NS, _} <- L];
- error -> []
- end
- end.
-
--spec init_cache(module(), binary(), gen_mod:opts()) -> ok.
-init_cache(Mod, Host, Opts) ->
- case use_cache(Mod, Host) of
- true ->
- ets_cache:new(?CARBONCOPY_CACHE, cache_opts(Opts));
- false ->
- ets_cache:delete(?CARBONCOPY_CACHE)
- end.
-
--spec cache_opts(gen_mod:opts()) -> [proplists:property()].
-cache_opts(Opts) ->
- MaxSize = gen_mod:get_opt(cache_size, Opts),
- CacheMissed = gen_mod:get_opt(cache_missed, Opts),
- LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
- infinity -> infinity;
- I -> timer:seconds(I)
- end,
- [{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
-
--spec use_cache(module(), binary()) -> boolean().
-use_cache(Mod, Host) ->
- case erlang:function_exported(Mod, use_cache, 1) of
- true -> Mod:use_cache(Host);
- false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
- end.
-
--spec cache_nodes(module(), binary()) -> [node()].
-cache_nodes(Mod, Host) ->
- case erlang:function_exported(Mod, cache_nodes, 1) of
- true -> Mod:cache_nodes(Host);
- false -> ejabberd_cluster:get_nodes()
- end.
-
--spec clean_cache(node()) -> non_neg_integer().
-clean_cache(Node) ->
- ets_cache:filter(
- ?CARBONCOPY_CACHE,
- fun(_, error) ->
- false;
- (_, {ok, L}) ->
- not lists:any(fun({_, _, N}) -> N == Node end, L)
- end).
-
--spec clean_cache() -> ok.
-clean_cache() ->
- ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
-
--spec delete_cache(module(), binary(), binary()) -> ok.
-delete_cache(Mod, User, Server) ->
- case use_cache(Mod, Server) of
- true ->
- ets_cache:delete(?CARBONCOPY_CACHE, {User, Server},
- cache_nodes(Mod, Server));
- false ->
- ok
- end.
+ lists:filtermap(
+ fun({Resource, Info}) ->
+ case lists:keyfind(carboncopy, 1, Info) of
+ {_, NS} -> {true, {Resource, NS}};
+ false -> false
+ end
+ end, ejabberd_sm:get_user_info(User, Server)).
depends(_Host, _Opts) ->
[].
-mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(O) when O == use_cache; O == cache_missed ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type(O) when O == cache_size; O == cache_life_time ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
+mod_opt_type(O) when O == cache_size; O == cache_life_time;
+ O == use_cache; O == cache_missed;
+ O == ram_db_type ->
+ fun(deprecated) -> deprecated;
+ (_) ->
+ ?WARNING_MSG("Option ~s of ~s has no effect anymore "
+ "and will be ingored", [O, ?MODULE]),
+ deprecated
end.
-mod_options(Host) ->
- [{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
- {use_cache, ejabberd_config:use_cache(Host)},
- {cache_size, ejabberd_config:cache_size(Host)},
- {cache_missed, ejabberd_config:cache_missed(Host)},
- {cache_life_time, ejabberd_config:cache_life_time(Host)}].
+mod_options(_) ->
+ [{ram_db_type, deprecated},
+ {use_cache, deprecated},
+ {cache_size, deprecated},
+ {cache_missed, deprecated},
+ {cache_life_time, deprecated}].
diff --git a/src/mod_carboncopy_mnesia.erl b/src/mod_carboncopy_mnesia.erl
deleted file mode 100644
index 03bde9897..000000000
--- a/src/mod_carboncopy_mnesia.erl
+++ /dev/null
@@ -1,79 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : mod_carboncopy_mnesia.erl
-%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%
-%%%
-%%% 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_carboncopy_mnesia).
-
--behaviour(mod_carboncopy).
-
-%% API
--export([init/2, enable/4, disable/3, list/2, use_cache/1]).
-
--include("mod_carboncopy.hrl").
-
-%%%===================================================================
-%%% API
-%%%===================================================================
-init(_Host, _Opts) ->
- Fields = record_info(fields, carboncopy),
- try mnesia:table_info(carboncopy, attributes) of
- Fields ->
- ok;
- _ ->
- %% recreate..
- mnesia:delete_table(carboncopy)
- catch _:_Error ->
- %% probably table don't exist
- ok
- end,
- ejabberd_mnesia:create(?MODULE, carboncopy,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, carboncopy)},
- {type, bag}]).
-
-enable(LUser, LServer, LResource, NS) ->
- mnesia:dirty_write(
- #carboncopy{us = {LUser, LServer},
- resource = LResource,
- version = NS}).
-
-disable(LUser, LServer, LResource) ->
- ToDelete = mnesia:dirty_match_object(
- #carboncopy{us = {LUser, LServer},
- resource = LResource,
- _ = '_'}),
- lists:foreach(fun mnesia:dirty_delete_object/1, ToDelete).
-
-list(LUser, LServer) ->
- {ok, mnesia:dirty_select(
- carboncopy,
- [{#carboncopy{us = {LUser, LServer}, resource = '$2',
- version = '$3', node = '$4'},
- [], [{{'$2','$3','$4'}}]}])}.
-
-use_cache(_LServer) ->
- false.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
diff --git a/src/mod_carboncopy_redis.erl b/src/mod_carboncopy_redis.erl
deleted file mode 100644
index 90b55168e..000000000
--- a/src/mod_carboncopy_redis.erl
+++ /dev/null
@@ -1,176 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%
-%%%
-%%% 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_carboncopy_redis).
--behaviour(mod_carboncopy).
--behaviour(gen_server).
-
-%% API
--export([init/2, enable/4, disable/3, list/2, cache_nodes/1]).
-%% gen_server callbacks
--export([init/1, handle_cast/2, handle_call/3, handle_info/2,
- terminate/2, code_change/3, start_link/0]).
-
--include("logger.hrl").
--include("mod_carboncopy.hrl").
-
--define(CARBONCOPY_KEY, <<"ejabberd:carboncopy">>).
-
--record(state, {}).
-
-%%%===================================================================
-%%% API
-%%%===================================================================
-init(_Host, _Opts) ->
- Spec = {?MODULE, {?MODULE, start_link, []},
- transient, 5000, worker, [?MODULE]},
- case supervisor:start_child(ejabberd_backend_sup, Spec) of
- {ok, _Pid} -> ok;
- Err -> Err
- end.
-
--spec start_link() -> {ok, pid()} | {error, any()}.
-start_link() ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-cache_nodes(_LServer) ->
- [node()].
-
-enable(LUser, LServer, LResource, NS) ->
- USKey = us_key(LUser, LServer),
- NodeKey = node_key(),
- JID = jid:encode({LUser, LServer, LResource}),
- Data = term_to_binary({NS, node()}),
- case ejabberd_redis:multi(
- fun() ->
- ejabberd_redis:hset(USKey, LResource, Data),
- ejabberd_redis:sadd(NodeKey, [JID]),
- ejabberd_redis:publish(
- ?CARBONCOPY_KEY,
- term_to_binary({delete, {LUser, LServer}}))
- end) of
- {ok, _} ->
- ok;
- {error, _} ->
- {error, db_failure}
- end.
-
-disable(LUser, LServer, LResource) ->
- USKey = us_key(LUser, LServer),
- NodeKey = node_key(),
- JID = jid:encode({LUser, LServer, LResource}),
- case ejabberd_redis:multi(
- fun() ->
- ejabberd_redis:hdel(USKey, [LResource]),
- ejabberd_redis:srem(NodeKey, [JID]),
- ejabberd_redis:publish(
- ?CARBONCOPY_KEY,
- term_to_binary({delete, {LUser, LServer}}))
- end) of
- {ok, _} ->
- ok;
- {error, _} ->
- {error, db_failure}
- end.
-
-list(LUser, LServer) ->
- USKey = us_key(LUser, LServer),
- case ejabberd_redis:hgetall(USKey) of
- {ok, Pairs} ->
- {ok, lists:flatmap(
- fun({Resource, Data}) ->
- try
- {NS, Node} = binary_to_term(Data),
- [{Resource, NS, Node}]
- catch _:_ ->
- ?ERROR_MSG("invalid term stored in Redis "
- "(key = ~s): ~p",
- [USKey, Data]),
- []
- end
- end, Pairs)};
- {error, _} ->
- {error, db_failure}
- end.
-
-%%%===================================================================
-%%% gen_server callbacks
-%%%===================================================================
-init([]) ->
- ejabberd_redis:subscribe([?CARBONCOPY_KEY]),
- clean_table(),
- {ok, #state{}}.
-
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-handle_info({redis_message, ?CARBONCOPY_KEY, Data}, State) ->
- case binary_to_term(Data) of
- {delete, Key} ->
- ets_cache:delete(?CARBONCOPY_CACHE, Key);
- Msg ->
- ?WARNING_MSG("unexpected redis message: ~p", [Msg])
- end,
- {noreply, State};
-handle_info(Info, State) ->
- ?ERROR_MSG("unexpected info: ~p", [Info]),
- {noreply, State}.
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-clean_table() ->
- ?DEBUG("Cleaning Redis 'carboncopy' table...", []),
- NodeKey = node_key(),
- case ejabberd_redis:smembers(NodeKey) of
- {ok, JIDs} ->
- ejabberd_redis:multi(
- fun() ->
- lists:foreach(
- fun(JID) ->
- {U, S, R} = jid:split(jid:decode(JID)),
- USKey = us_key(U, S),
- ejabberd_redis:hdel(USKey, [R])
- end, JIDs)
- end);
- {error, _} ->
- ok
- end,
- ejabberd_redis:del([NodeKey]),
- ok.
-
-us_key(LUser, LServer) ->
- <<"ejabberd:carboncopy:users:", LUser/binary, $@, LServer/binary>>.
-
-node_key() ->
- Node = erlang:atom_to_binary(node(), latin1),
- <<"ejabberd:carboncopy:nodes:", Node/binary>>.
diff --git a/src/mod_carboncopy_riak.erl b/src/mod_carboncopy_riak.erl
deleted file mode 100644
index d6df3008e..000000000
--- a/src/mod_carboncopy_riak.erl
+++ /dev/null
@@ -1,82 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Created : 15 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%
-%%%
-%%% 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_carboncopy_riak).
--behaviour(mod_carboncopy).
-
-%% API
--export([init/2, enable/4, disable/3, list/2]).
-
--include("logger.hrl").
--include("mod_carboncopy.hrl").
-
-%%%===================================================================
-%%% API
-%%%===================================================================
-init(_Host, _Opts) ->
- clean_table().
-
-enable(LUser, LServer, LResource, NS) ->
- ejabberd_riak:put(#carboncopy{us = {LUser, LServer},
- resource = LResource,
- version = NS},
- carboncopy_schema(),
- [{i, {LUser, LServer, LResource}},
- {'2i', [{<<"us">>, {LUser, LServer}}]}]).
-
-disable(LUser, LServer, LResource) ->
- ejabberd_riak:delete(carboncopy, {LUser, LServer, LResource}).
-
-list(LUser, LServer) ->
- case ejabberd_riak:get_by_index(
- carboncopy, carboncopy_schema(),
- <<"us">>, {LUser, LServer}) of
- {ok, Rs} ->
- {ok, [{Resource, NS, Node}
- || #carboncopy{resource = Resource,
- version = NS,
- node = Node} <- Rs]};
- {error, _} = Err ->
- Err
- end.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-carboncopy_schema() ->
- {record_info(fields, carboncopy), #carboncopy{}}.
-
-clean_table() ->
- ?DEBUG("Cleaning Riak 'carboncopy' table...", []),
- case ejabberd_riak:get(carboncopy, carboncopy_schema()) of
- {ok, Rs} ->
- lists:foreach(
- fun(#carboncopy{us = {U, S}, resource = R, node = Node})
- when Node == node() ->
- ejabberd_riak:delete(carboncopy, {U, S, R});
- (_) ->
- ok
- end, Rs);
- {error, Reason} = Err ->
- ?ERROR_MSG("Failed to clean Riak 'carboncopy' table: ~p", [Reason]),
- Err
- end.
diff --git a/src/mod_carboncopy_sql.erl b/src/mod_carboncopy_sql.erl
deleted file mode 100644
index 46b6ea806..000000000
--- a/src/mod_carboncopy_sql.erl
+++ /dev/null
@@ -1,91 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Created : 29 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%
-%%%
-%%% 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_carboncopy_sql).
--behaviour(mod_carboncopy).
-
--compile([{parse_transform, ejabberd_sql_pt}]).
-
-%% API
--export([init/2, enable/4, disable/3, list/2]).
-
--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",
- "!server_host=%(LServer)s",
- "!resource=%(LResource)s",
- "namespace=%(NS)s",
- "node=%(NodeS)s"]) of
- ok ->
- ok;
- _Err ->
- {error, db_failure}
- end.
-
-disable(LUser, LServer, LResource) ->
- case ejabberd_sql:sql_query(
- LServer,
- ?SQL("delete from carboncopy where username=%(LUser)s "
- "and %(LServer)H and resource=%(LResource)s")) of
- {updated, _} ->
- ok;
- _Err ->
- {error, db_failure}
- end.
-
-list(LUser, LServer) ->
- case ejabberd_sql:sql_query(
- LServer,
- ?SQL("select @(resource)s, @(namespace)s, @(node)s from carboncopy "
- "where username=%(LUser)s and %(LServer)H")) of
- {selected, Rows} ->
- {ok, [{Resource, NS, binary_to_atom(Node, latin1)}
- || {Resource, NS, Node} <- Rows]};
- _Err ->
- {error, db_failure}
- end.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-clean_table(LServer) ->
- NodeS = erlang:atom_to_binary(node(), latin1),
- ?DEBUG("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]),
- {error, db_failure}
- end.
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index 156fde726..162d47aa2 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Sep 2014 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index a52376504..aa431e285 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl
index 45e44c497..532463e3a 100644
--- a/src/mod_delegation.erl
+++ b/src/mod_delegation.erl
@@ -4,7 +4,7 @@
%%% Purpose : XEP-0355: Namespace Delegation
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index 3b394b8dd..f0d23a0ca 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_echo.erl b/src/mod_echo.erl
index 26cf60611..eaa630ac7 100644
--- a/src/mod_echo.erl
+++ b/src/mod_echo.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl
index 0250392a3..141462566 100644
--- a/src/mod_fail2ban.erl
+++ b/src/mod_fail2ban.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Aug 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -36,7 +36,11 @@
handle_info/2, terminate/2, code_change/3,
mod_opt_type/1, mod_options/1, depends/2]).
+%% ejabberd command.
+-export([get_commands_spec/0, unban/1]).
+
-include_lib("stdlib/include/ms_transform.hrl").
+-include("ejabberd_commands.hrl").
-include("logger.hrl").
-include("xmpp.hrl").
@@ -101,9 +105,16 @@ c2s_stream_started(#{ip := {Addr, _}} = State, _) ->
start(Host, Opts) ->
catch ets:new(failed_auth, [named_table, public,
{heir, erlang:group_leader(), none}]),
+ ejabberd_commands:register_commands(get_commands_spec()),
gen_mod:start_child(?MODULE, Host, Opts).
stop(Host) ->
+ case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
+ false ->
+ ejabberd_commands:unregister_commands(get_commands_spec());
+ true ->
+ ok
+ end,
gen_mod:stop_child(?MODULE, Host).
reload(_Host, _NewOpts, _OldOpts) ->
@@ -155,6 +166,46 @@ terminate(_Reason, #state{host = Host}) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+%%--------------------------------------------------------------------
+%% ejabberd command callback.
+%%--------------------------------------------------------------------
+-spec get_commands_spec() -> [ejabberd_commands()].
+get_commands_spec() ->
+ [#ejabberd_commands{name = unban_ip, tags = [accounts],
+ desc = "Remove banned IP addresses from the fail2ban table",
+ longdesc = "Accepts an IP address with a network mask. "
+ "Returns the number of unbanned addresses, or a negative integer if there were any error.",
+ module = ?MODULE, function = unban,
+ args = [{address, binary}],
+ args_example = [<<"::FFFF:127.0.0.1/128">>],
+ args_desc = ["IP address, optionally with network mask."],
+ result_example = 3,
+ result_desc = "Amount of unbanned entries, or negative in case of error.",
+ result = {unbanned, integer}}].
+
+-spec unban(string()) -> integer().
+unban(S) ->
+ case acl:parse_ip_netmask(S) of
+ {ok, Net, Mask} ->
+ unban(Net, Mask);
+ error ->
+ ?WARNING_MSG("Invalid network address when trying to unban: ~p", [S]),
+ -1
+ end.
+
+unban(Net, Mask) ->
+ ets:foldl(
+ fun({Addr, _, _, _}, Acc) ->
+ case acl:ip_matches_mask(Addr, Net, Mask) of
+ true ->
+ ets:delete(failed_auth, Addr),
+ Acc+1;
+ false -> Acc
+ end
+ end,
+ 0,
+ failed_auth).
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl
index 4d4a40f79..0d1d7c662 100644
--- a/src/mod_http_api.erl
+++ b/src/mod_http_api.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Sep 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -80,6 +80,7 @@
-include("xmpp.hrl").
-include("logger.hrl").
-include("ejabberd_http.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(DEFAULT_API_VERSION, 0).
@@ -192,9 +193,8 @@ process([Call], #request{method = 'POST', data = Data, ip = IPPort} = Req) ->
_:{error,{_,invalid_json}} = _Err ->
?DEBUG("Bad Request: ~p", [_Err]),
badrequest_response(<<"Invalid JSON input">>);
- _:_Error ->
- St = erlang:get_stacktrace(),
- ?DEBUG("Bad Request: ~p ~p", [_Error, St]),
+ ?EX_RULE(_Class, _Error, Stack) ->
+ ?DEBUG("Bad Request: ~p ~p", [_Error, ?EX_STACK(Stack)]),
badrequest_response()
end;
process([Call], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) ->
@@ -210,9 +210,8 @@ process([Call], #request{method = 'GET', q = Data, ip = {IP, _}} = Req) ->
%% TODO We need to refactor to remove redundant error return formatting
throw:{error, unknown_command} ->
json_format({404, 44, <<"Command not found.">>});
- _:_Error ->
- St = erlang:get_stacktrace(),
- ?DEBUG("Bad Request: ~p ~p", [_Error, St]),
+ ?EX_RULE(_, _Error, Stack) ->
+ ?DEBUG("Bad Request: ~p ~p", [_Error, ?EX_STACK(Stack)]),
badrequest_response()
end;
process([_Call], #request{method = 'OPTIONS', data = <<>>}) ->
@@ -302,9 +301,8 @@ handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
{400, misc:atom_to_binary(Error)};
throw:Msg when is_list(Msg); is_binary(Msg) ->
{400, iolist_to_binary(Msg)};
- _Error ->
- St = erlang:get_stacktrace(),
- ?ERROR_MSG("REST API Error: ~p ~p", [_Error, St]),
+ ?EX_RULE(Class, Error, Stack) ->
+ ?ERROR_MSG("REST API Error: ~p:~p ~p", [Class, Error, ?EX_STACK(Stack)]),
{500, <<"internal_error">>}
end;
{error, Msg} ->
@@ -325,15 +323,20 @@ handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
format_command_result(Call, Auth, Res, Version)
end.
-get_elem_delete(A, L) ->
+get_elem_delete(A, L, F) ->
case proplists:get_all_values(A, L) of
[Value] -> {Value, proplists:delete(A, L)};
[_, _ | _] ->
%% Crash reporting the error
exit({duplicated_attribute, A, L});
[] ->
- %% Report the error and then force a crash
- exit({attribute_not_found, A, L})
+ case F of
+ {list, _} ->
+ {[], L};
+ _ ->
+ %% Report the error and then force a crash
+ exit({attribute_not_found, A, L})
+ end
end.
format_args(Args, ArgsFormat) ->
@@ -342,7 +345,7 @@ format_args(Args, ArgsFormat) ->
{Args1, Res}) ->
{ArgValue, Args2} =
get_elem_delete(ArgName,
- Args1),
+ Args1, ArgFormat),
Formatted = format_arg(ArgValue,
ArgFormat),
{Args2, Res ++ [Formatted]}
@@ -471,6 +474,9 @@ format_result(Code, {Name, restuple}) ->
format_result(Els, {Name, {list, {_, {tuple, [{_, atom}, _]}} = Fmt}}) ->
{misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}};
+format_result(Els, {Name, {list, {_, {tuple, [{name, string}, {value, _}]}} = Fmt}}) ->
+ {misc:atom_to_binary(Name), {[format_result(El, Fmt) || El <- Els]}};
+
format_result(Els, {Name, {list, Def}}) ->
{misc:atom_to_binary(Name), [element(2, format_result(El, Def)) || El <- Els]};
@@ -479,6 +485,11 @@ format_result(Tuple, {_Name, {tuple, [{_, atom}, ValFmt]}}) ->
{_, Val2} = format_result(Val, ValFmt),
{misc:atom_to_binary(Name2), Val2};
+format_result(Tuple, {_Name, {tuple, [{name, string}, {value, _} = ValFmt]}}) ->
+ {Name2, Val} = Tuple,
+ {_, Val2} = format_result(Val, ValFmt),
+ {iolist_to_binary(Name2), Val2};
+
format_result(Tuple, {Name, {tuple, Def}}) ->
Els = lists:zip(tuple_to_list(Tuple), Def),
{misc:atom_to_binary(Name), {[format_result(El, ElDef) || {El, ElDef} <- Els]}};
diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl
index 47cf31bce..d34d7193e 100644
--- a/src/mod_http_fileserver.erl
+++ b/src/mod_http_fileserver.erl
@@ -5,7 +5,7 @@
%%% Created :
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index fe3379f16..66df9f91d 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -5,7 +5,7 @@
%%% Created : 20 Aug 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2015-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2015-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl
index f27c0222e..10f7831bd 100644
--- a/src/mod_http_upload_quota.erl
+++ b/src/mod_http_upload_quota.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Oct 2015 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2015-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2015-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_last.erl b/src/mod_last.erl
index f0651b054..dcae1c27e 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Oct 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl
index de5e448d3..d8d5296f3 100644
--- a/src/mod_last_mnesia.erl
+++ b/src/mod_last_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_last_riak.erl b/src/mod_last_riak.erl
index ca41a6a06..cd5dd43d4 100644
--- a/src/mod_last_riak.erl
+++ b/src/mod_last_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl
index e8168f3f2..85f3e3895 100644
--- a/src/mod_last_sql.erl
+++ b/src/mod_last_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl
index 19b905ad8..c4614d74c 100644
--- a/src/mod_legacy_auth.erl
+++ b/src/mod_legacy_auth.erl
@@ -2,7 +2,7 @@
%%% Created : 11 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index 9689a93a2..294d4f401 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -5,7 +5,7 @@
%%% Created : 4 Jul 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2013-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2013-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -52,6 +52,7 @@
-define(MAX_PAGE_SIZE, 250).
-type c2s_state() :: ejabberd_c2s:state().
+-type count() :: non_neg_integer() | undefined.
-callback init(binary(), gen_mod:opts()) -> any().
-callback remove_user(binary(), binary()) -> any().
@@ -63,10 +64,11 @@
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
jid(), binary(), recv | send, integer()) -> ok | any().
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
--callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
+-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error | {error, db_failure}.
-callback select(binary(), jid(), jid(), mam_query:result(),
#rsm_set{} | undefined, chat | groupchat) ->
- {[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
+ {[{binary(), non_neg_integer(), xmlel()}], boolean(), count()} |
+ {error, db_failure}.
-callback use_cache(binary()) -> boolean().
-callback cache_nodes(binary()) -> [node()].
-callback remove_from_archive(binary(), binary(), jid() | none) -> ok | {error, any()}.
@@ -89,42 +91,45 @@ start(Host, Opts) ->
ok
end,
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
- Mod:init(Host, Opts),
- init_cache(Mod, Host, Opts),
- register_iq_handlers(Host),
- ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
- sm_receive_packet, 50),
- ejabberd_hooks:add(user_receive_packet, Host, ?MODULE,
- user_receive_packet, 88),
- ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
- user_send_packet, 88),
- ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
- user_send_packet_strip_tag, 500),
- ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
- offline_message, 50),
- ejabberd_hooks:add(muc_filter_message, Host, ?MODULE,
- muc_filter_message, 50),
- ejabberd_hooks:add(muc_process_iq, Host, ?MODULE,
- muc_process_iq, 50),
- ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
- disco_sm_features, 50),
- ejabberd_hooks:add(remove_user, Host, ?MODULE,
- remove_user, 50),
- ejabberd_hooks:add(remove_room, Host, ?MODULE,
- remove_room, 50),
- ejabberd_hooks:add(get_room_config, Host, ?MODULE,
- get_room_config, 50),
- ejabberd_hooks:add(set_room_option, Host, ?MODULE,
- set_room_option, 50),
- case gen_mod:get_opt(assume_mam_usage, Opts) of
- true ->
- ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
- message_is_archived, 50);
- false ->
- ok
- end,
- ejabberd_commands:register_commands(get_commands_spec()),
- ok.
+ case Mod:init(Host, Opts) of
+ ok ->
+ init_cache(Mod, Host, Opts),
+ register_iq_handlers(Host),
+ ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
+ sm_receive_packet, 50),
+ ejabberd_hooks:add(user_receive_packet, Host, ?MODULE,
+ user_receive_packet, 88),
+ ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
+ user_send_packet, 88),
+ ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
+ user_send_packet_strip_tag, 500),
+ ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
+ offline_message, 50),
+ ejabberd_hooks:add(muc_filter_message, Host, ?MODULE,
+ muc_filter_message, 50),
+ ejabberd_hooks:add(muc_process_iq, Host, ?MODULE,
+ muc_process_iq, 50),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
+ disco_sm_features, 50),
+ ejabberd_hooks:add(remove_user, Host, ?MODULE,
+ remove_user, 50),
+ ejabberd_hooks:add(remove_room, Host, ?MODULE,
+ remove_room, 50),
+ ejabberd_hooks:add(get_room_config, Host, ?MODULE,
+ get_room_config, 50),
+ ejabberd_hooks:add(set_room_option, Host, ?MODULE,
+ set_room_option, 50),
+ case gen_mod:get_opt(assume_mam_usage, Opts) of
+ true ->
+ ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
+ message_is_archived, 50);
+ false ->
+ ok
+ end,
+ ejabberd_commands:register_commands(get_commands_spec());
+ Err ->
+ Err
+ end.
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 2) of
@@ -594,37 +599,48 @@ process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end;
process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
- to = #jid{lserver = LServer},
+ to = #jid{lserver = LServer}, lang = Lang,
type = get, sub_els = [#mam_prefs{xmlns = NS}]} = IQ) ->
- Prefs = get_prefs(LUser, LServer),
- PrefsEl = prefs_el(Prefs#archive_prefs.default,
- Prefs#archive_prefs.always,
- Prefs#archive_prefs.never,
- NS),
- xmpp:make_iq_result(IQ, PrefsEl);
+ case get_prefs(LUser, LServer) of
+ {ok, Prefs} ->
+ PrefsEl = prefs_el(Prefs#archive_prefs.default,
+ Prefs#archive_prefs.always,
+ Prefs#archive_prefs.never,
+ NS),
+ xmpp:make_iq_result(IQ, PrefsEl);
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
+ end;
process_iq(IQ) ->
xmpp:make_error(IQ, xmpp:err_not_allowed()).
process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang,
sub_els = [SubEl]} = IQ, MsgType) ->
- case MsgType of
- chat ->
- maybe_activate_mam(LUser, LServer);
- {groupchat, _Role, _MUCState} ->
- ok
- end,
- case SubEl of
- #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) ->
- Txt = <<"Unsupported <index/> element">>,
- xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang));
- #mam_query{rsm = RSM, xmlns = NS} ->
- case parse_query(SubEl, Lang) of
- {ok, Query} ->
- NewRSM = limit_max(RSM, NS),
- select_and_send(LServer, Query, NewRSM, IQ, MsgType);
- {error, Err} ->
- xmpp:make_error(IQ, Err)
- end
+ Ret = case MsgType of
+ chat ->
+ maybe_activate_mam(LUser, LServer);
+ {groupchat, _Role, _MUCState} ->
+ ok
+ end,
+ case Ret of
+ ok ->
+ case SubEl of
+ #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) ->
+ Txt = <<"Unsupported <index/> element">>,
+ xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang));
+ #mam_query{rsm = RSM, xmlns = NS} ->
+ case parse_query(SubEl, Lang) of
+ {ok, Query} ->
+ NewRSM = limit_max(RSM, NS),
+ select_and_send(LServer, Query, NewRSM, IQ, MsgType);
+ {error, Err} ->
+ xmpp:make_error(IQ, Err)
+ end
+ end;
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
-spec should_archive(message(), binary()) -> boolean().
@@ -818,23 +834,27 @@ may_enter_room(From, MUCState) ->
-spec store_msg(message(), binary(), binary(), jid(), send | recv)
-> ok | pass | any().
store_msg(Pkt, LUser, LServer, Peer, Dir) ->
- Prefs = get_prefs(LUser, LServer),
- case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of
- {true, #message{meta = #{sm_copy := true}}} ->
- ok; % Already stored.
- {true, _} ->
- case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt,
- [LUser, LServer, Peer, chat, Dir]) of
- drop ->
- pass;
- Pkt1 ->
- US = {LUser, LServer},
- ID = get_stanza_id(Pkt1),
- El = xmpp:encode(Pkt1),
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir, ID)
+ case get_prefs(LUser, LServer) of
+ {ok, Prefs} ->
+ case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt} of
+ {true, #message{meta = #{sm_copy := true}}} ->
+ ok; % Already stored.
+ {true, _} ->
+ case ejabberd_hooks:run_fold(store_mam_message, LServer, Pkt,
+ [LUser, LServer, Peer, chat, Dir]) of
+ drop ->
+ pass;
+ Pkt1 ->
+ US = {LUser, LServer},
+ ID = get_stanza_id(Pkt1),
+ El = xmpp:encode(Pkt1),
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:store(El, LServer, US, chat, Peer, <<"">>, Dir, ID)
+ end;
+ {false, _} ->
+ pass
end;
- {false, _} ->
+ {error, _} ->
pass
end.
@@ -890,18 +910,20 @@ get_prefs(LUser, LServer) ->
end,
case Res of
{ok, Prefs} ->
- Prefs;
+ {ok, Prefs};
+ {error, _} ->
+ {error, db_failure};
error ->
ActivateOpt = gen_mod:get_module_opt(
LServer, ?MODULE,
request_activates_archiving),
case ActivateOpt of
true ->
- #archive_prefs{us = {LUser, LServer}, default = never};
+ {ok, #archive_prefs{us = {LUser, LServer}, default = never}};
false ->
Default = gen_mod:get_module_opt(
LServer, ?MODULE, default),
- #archive_prefs{us = {LUser, LServer}, default = Default}
+ {ok, #archive_prefs{us = {LUser, LServer}, default = Default}}
end
end.
@@ -930,6 +952,8 @@ maybe_activate_mam(LUser, LServer) ->
case Res of
{ok, _Prefs} ->
ok;
+ {error, _} ->
+ {error, db_failure};
error ->
Default = gen_mod:get_module_opt(
LServer, ?MODULE, default),
@@ -940,15 +964,21 @@ maybe_activate_mam(LUser, LServer) ->
end.
select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) ->
- {Msgs, IsComplete, Count} =
- case MsgType of
- chat ->
- select(LServer, From, From, Query, RSM, MsgType);
- {groupchat, _Role, _MUCState} ->
- select(LServer, From, To, Query, RSM, MsgType)
- end,
- SortedMsgs = lists:keysort(2, Msgs),
- send(SortedMsgs, Count, IsComplete, IQ).
+ Ret = case MsgType of
+ chat ->
+ select(LServer, From, From, Query, RSM, MsgType);
+ {groupchat, _Role, _MUCState} ->
+ select(LServer, From, To, Query, RSM, MsgType)
+ end,
+ case Ret of
+ {Msgs, IsComplete, Count} ->
+ SortedMsgs = lists:keysort(2, Msgs),
+ send(SortedMsgs, Count, IsComplete, IQ);
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang),
+ xmpp:make_error(IQ, Err)
+ end.
select(_LServer, JidRequestor, JidArchive, Query, RSM,
{groupchat, _Role, #state{config = #config{mam = false},
@@ -1045,7 +1075,7 @@ maybe_update_from_to(Pkt, _JidRequestor, _JidArchive, _Peer, chat, _Nick) ->
Pkt.
-spec send([{binary(), integer(), xmlel()}],
- non_neg_integer(), boolean(), iq()) -> iq() | ignore.
+ count(), boolean(), iq()) -> iq() | ignore.
send(Msgs, Count, IsComplete,
#iq{from = From, to = To,
sub_els = [#mam_query{id = QID, xmlns = NS}]} = IQ) ->
@@ -1083,7 +1113,7 @@ send(Msgs, Count, IsComplete,
ignore
end.
--spec make_rsm_out([{binary(), integer(), xmlel()}], non_neg_integer()) -> rsm_set().
+-spec make_rsm_out([{binary(), integer(), xmlel()}], count()) -> rsm_set().
make_rsm_out([], Count) ->
#rsm_set{count = Count};
make_rsm_out([{FirstID, _, _}|_] = Msgs, Count) ->
@@ -1172,7 +1202,7 @@ mod_opt_type(O) when O == cache_life_time; O == cache_size ->
fun (I) when is_integer(I), I > 0 -> I;
(infinity) -> infinity
end;
-mod_opt_type(O) when O == use_cache; O == cache_missed ->
+mod_opt_type(O) when O == use_cache; O == cache_missed; O == compress_xml ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
mod_opt_type(default) ->
@@ -1187,6 +1217,7 @@ mod_options(Host) ->
[{assume_mam_usage, false},
{default, never},
{request_activates_archiving, false},
+ {compress_xml, false},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{use_cache, ejabberd_config:use_cache(Host)},
{cache_size, ejabberd_config:cache_size(Host)},
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl
index 55154f6bb..08d551585 100644
--- a/src/mod_mam_mnesia.erl
+++ b/src/mod_mam_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -48,13 +48,20 @@
%%% API
%%%===================================================================
init(_Host, _Opts) ->
- ejabberd_mnesia:create(?MODULE, archive_msg,
+ try
+ {atomic, _} = ejabberd_mnesia:create(
+ ?MODULE, archive_msg,
[{disc_only_copies, [node()]},
{type, bag},
{attributes, record_info(fields, archive_msg)}]),
- ejabberd_mnesia:create(?MODULE, archive_prefs,
+ {atomic, _} = ejabberd_mnesia:create(
+ ?MODULE, archive_prefs,
[{disc_only_copies, [node()]},
- {attributes, record_info(fields, archive_prefs)}]).
+ {attributes, record_info(fields, archive_prefs)}]),
+ ok
+ catch _:{badmatch, _} ->
+ {error, db_failure}
+ end.
remove_user(LUser, LServer) ->
US = {LUser, LServer},
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index 37ea8dc6f..bcfa06708 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -102,9 +102,18 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) ->
jid:remove_resource(Peer))),
LPeer = jid:encode(
jid:tolower(Peer)),
- XML = fxml:element_to_binary(Pkt),
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
SType = misc:atom_to_binary(Type),
+ XML = case gen_mod:get_module_opt(LServer, mod_mam, compress_xml) of
+ true ->
+ J1 = case Type of
+ chat -> jid:encode({LUser, LHost, <<>>});
+ groupchat -> SUser
+ end,
+ xml_compress:encode(Pkt, J1, LPeer);
+ _ ->
+ fxml:element_to_binary(Pkt)
+ end,
case ejabberd_sql:sql_query(
LServer,
?SQL_INSERT(
@@ -192,8 +201,8 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
{lists:flatmap(
fun([TS, XML, PeerBin, Kind, Nick]) ->
case make_archive_el(
- TS, XML, PeerBin, Kind, Nick,
- MsgType, JidRequestor, JidArchive) of
+ jid:encode(JidArchive), TS, XML, PeerBin, Kind, Nick,
+ MsgType, JidRequestor, JidArchive) of
{ok, El} ->
[{TS, binary_to_integer(TS), El}];
{error, _} ->
@@ -399,13 +408,13 @@ get_max_direction_id(RSM) ->
{undefined, undefined, <<>>}
end.
--spec make_archive_el(binary(), binary(), binary(), binary(),
+-spec make_archive_el(binary(), binary(), binary(), binary(), binary(),
binary(), _, jid(), jid()) ->
{ok, xmpp_element()} | {error, invalid_jid |
invalid_timestamp |
invalid_xml}.
-make_archive_el(TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchive) ->
- case fxml_stream:parse_element(XML) of
+make_archive_el(User, TS, XML, Peer, Kind, Nick, MsgType, JidRequestor, JidArchive) ->
+ case xml_compress:decode(XML, User, Peer) of
#xmlel{} = El ->
try binary_to_integer(TS) of
TSInt ->
diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl
index 282dca7ef..a84d92b9c 100644
--- a/src/mod_metrics.erl
+++ b/src/mod_metrics.erl
@@ -5,7 +5,7 @@
%%% Created : 22 Oct 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_mix.erl b/src/mod_mix.erl
deleted file mode 100644
index 78e5d0251..000000000
--- a/src/mod_mix.erl
+++ /dev/null
@@ -1,323 +0,0 @@
-%%%-------------------------------------------------------------------
-%%% File : mod_mix.erl
-%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Created : 2 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%
-%%%
-%%% 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_mix).
-
--behaviour(gen_server).
--behaviour(gen_mod).
-
-%% API
--export([start/2, stop/1, process_iq/1,
- disco_items/5, disco_identity/5, disco_info/5,
- disco_features/5, mod_opt_type/1, mod_options/1, depends/2]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--include("logger.hrl").
--include("xmpp.hrl").
-
--define(NODES, [?NS_MIX_NODES_MESSAGES,
- ?NS_MIX_NODES_PRESENCE,
- ?NS_MIX_NODES_PARTICIPANTS,
- ?NS_MIX_NODES_SUBJECT,
- ?NS_MIX_NODES_CONFIG]).
-
--record(state, {server_host :: binary(),
- hosts :: [binary()]}).
-
-%%%===================================================================
-%%% API
-%%%===================================================================
-start(Host, Opts) ->
- gen_mod:start_child(?MODULE, Host, Opts).
-
-stop(Host) ->
- gen_mod:stop_child(?MODULE, Host).
-
--spec disco_features({error, stanza_error()} | {result, [binary()]} | empty,
- jid(), jid(), binary(), binary()) -> {result, [binary()]}.
-disco_features(_Acc, _From, _To, _Node, _Lang) ->
- {result, [?NS_MIX_0]}.
-
-disco_items(_Acc, _From, To, _Node, _Lang) when To#jid.luser /= <<"">> ->
- BareTo = jid:remove_resource(To),
- {result, [#disco_item{jid = BareTo, node = Node} || Node <- ?NODES]};
-disco_items(_Acc, _From, _To, _Node, _Lang) ->
- {result, []}.
-
-disco_identity(Acc, _From, To, _Node, _Lang) when To#jid.luser == <<"">> ->
- Acc ++ [#identity{category = <<"conference">>,
- name = <<"MIX service">>,
- type = <<"text">>}];
-disco_identity(Acc, _From, _To, _Node, _Lang) ->
- Acc ++ [#identity{category = <<"conference">>,
- type = <<"mix">>}].
-
--spec disco_info([xdata()], binary(), module(), binary(), binary()) -> [xdata()];
- ([xdata()], jid(), jid(), binary(), binary()) -> [xdata()].
-disco_info(_Acc, _From, To, _Node, _Lang) when is_atom(To) ->
- [#xdata{type = result,
- fields = [#xdata_field{var = <<"FORM_TYPE">>,
- type = hidden,
- values = [?NS_MIX_SERVICEINFO_0]}]}];
-disco_info(Acc, _From, _To, _Node, _Lang) ->
- Acc.
-
-process_iq(#iq{type = set, from = From, to = To,
- sub_els = [#mix_join{subscribe = SubNodes}]} = IQ) ->
- Nodes = [Node || Node <- SubNodes, lists:member(Node, ?NODES)],
- case subscribe_nodes(From, To, Nodes) of
- {result, _} ->
- case publish_participant(From, To) of
- {result, _} ->
- BareFrom = jid:remove_resource(From),
- xmpp:make_iq_result(
- IQ, #mix_join{jid = BareFrom, subscribe = Nodes});
- {error, Err} ->
- xmpp:make_error(IQ, Err)
- end;
- {error, Err} ->
- xmpp:make_error(IQ, Err)
- end;
-process_iq(#iq{type = set, from = From, to = To,
- sub_els = [#mix_leave{}]} = IQ) ->
- case delete_participant(From, To) of
- {result, _} ->
- case unsubscribe_nodes(From, To, ?NODES) of
- {result, _} ->
- xmpp:make_iq_result(IQ);
- {error, Err} ->
- xmpp:make_error(IQ, Err)
- end;
- {error, Err} ->
- xmpp:make_error(IQ, Err)
- end;
-process_iq(#iq{lang = Lang} = IQ) ->
- Txt = <<"Unsupported MIX query">>,
- xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)).
-
-%%%===================================================================
-%%% gen_server callbacks
-%%%===================================================================
-init([ServerHost, Opts]) ->
- process_flag(trap_exit, true),
- Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
- lists:foreach(
- fun(Host) ->
- ConfigTab = gen_mod:get_module_proc(Host, config),
- ets:new(ConfigTab, [named_table]),
- ets:insert(ConfigTab, {plugins, [<<"mix">>]}),
- ejabberd_hooks:add(disco_local_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:add(disco_info, Host, ?MODULE, disco_info, 100),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host,
- ?NS_DISCO_ITEMS, mod_disco,
- process_local_iq_items),
- gen_iq_handler:add_iq_handler(ejabberd_local, Host,
- ?NS_DISCO_INFO, mod_disco,
- process_local_iq_info),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_ITEMS, mod_disco,
- process_local_iq_items),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_DISCO_INFO, mod_disco,
- process_local_iq_info),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_PUBSUB, mod_pubsub, iq_sm),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_MIX_0, ?MODULE, process_iq),
- ejabberd_router:register_route(Host, ServerHost)
- end, Hosts),
- {ok, #state{server_host = ServerHost, hosts = Hosts}}.
-
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-handle_info({route, Packet}, State) ->
- case catch do_route(State, Packet) of
- {'EXIT', _} = Err ->
- try
- ?ERROR_MSG("failed to route packet:~n~s~nReason: ~p",
- [xmpp:pp(Packet), Err]),
- Error = xmpp:err_internal_server_error(),
- ejabberd_router:route_error(Packet, Error)
- catch _:_ ->
- ok
- end;
- _ ->
- ok
- end,
- {noreply, State};
-handle_info(_Info, State) ->
- {noreply, State}.
-
-terminate(_Reason, #state{hosts = Hosts}) ->
- lists:foreach(
- fun(Host) ->
- ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, disco_items, 100),
- ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, disco_features, 100),
- ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, disco_identity, 100),
- ejabberd_hooks:delete(disco_info, Host, ?MODULE, disco_info, 100),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUBSUB),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_0),
- ejabberd_router:unregister_route(Host)
- end, Hosts).
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-do_route(_State, #iq{} = Packet) ->
- ejabberd_router:process_iq(Packet);
-do_route(_State, #presence{from = From, to = To, type = unavailable})
- when To#jid.luser /= <<"">> ->
- delete_presence(From, To);
-do_route(_State, _Packet) ->
- ok.
-
-subscribe_nodes(From, To, Nodes) ->
- LTo = jid:tolower(jid:remove_resource(To)),
- LFrom = jid:tolower(jid:remove_resource(From)),
- lists:foldl(
- fun(_Node, {error, _} = Err) ->
- Err;
- (Node, {result, _}) ->
- case mod_pubsub:subscribe_node(LTo, Node, From, From, []) of
- {error, _} = Err ->
- case is_item_not_found(Err) of
- true ->
- case mod_pubsub:create_node(
- LTo, To#jid.lserver, Node, LFrom, <<"mix">>) of
- {result, _} ->
- mod_pubsub:subscribe_node(LTo, Node, From, From, []);
- Error ->
- Error
- end;
- false ->
- Err
- end;
- {result, _} = Result ->
- Result
- end
- end, {result, []}, Nodes).
-
-unsubscribe_nodes(From, To, Nodes) ->
- LTo = jid:tolower(jid:remove_resource(To)),
- BareFrom = jid:remove_resource(From),
- lists:foldl(
- fun(_Node, {error, _} = Err) ->
- Err;
- (Node, {result, _} = Result) ->
- case mod_pubsub:unsubscribe_node(LTo, Node, From, BareFrom, <<"">>) of
- {error, _} = Err ->
- case is_not_subscribed(Err) of
- true -> Result;
- _ -> Err
- end;
- {result, _} = Res ->
- Res
- end
- end, {result, []}, Nodes).
-
-publish_participant(From, To) ->
- BareFrom = jid:remove_resource(From),
- LFrom = jid:tolower(BareFrom),
- LTo = jid:tolower(jid:remove_resource(To)),
- Participant = #mix_participant{jid = BareFrom},
- ItemID = str:sha(jid:encode(LFrom)),
- mod_pubsub:publish_item(
- LTo, To#jid.lserver, ?NS_MIX_NODES_PARTICIPANTS,
- From, ItemID, [xmpp:encode(Participant)]).
-
-delete_presence(From, To) ->
- LFrom = jid:tolower(From),
- LTo = jid:tolower(jid:remove_resource(To)),
- case mod_pubsub:get_items(LTo, ?NS_MIX_NODES_PRESENCE) of
- Items when is_list(Items) ->
- lists:foreach(
- fun({pubsub_item, {ItemID, _}, _, {_, LJID}, _})
- when LJID == LFrom ->
- delete_item(From, To, ?NS_MIX_NODES_PRESENCE, ItemID);
- (_) ->
- ok
- end, Items);
- _ ->
- ok
- end.
-
-delete_participant(From, To) ->
- LFrom = jid:tolower(jid:remove_resource(From)),
- ItemID = str:sha(jid:encode(LFrom)),
- delete_presence(From, To),
- delete_item(From, To, ?NS_MIX_NODES_PARTICIPANTS, ItemID).
-
-delete_item(From, To, Node, ItemID) ->
- LTo = jid:tolower(jid:remove_resource(To)),
- case mod_pubsub:delete_item(
- LTo, Node, From, ItemID, true) of
- {result, _} = Res ->
- Res;
- {error, _} = Err ->
- case is_item_not_found(Err) of
- true -> {result, []};
- false -> Err
- end
- end.
-
--spec is_item_not_found({error, stanza_error()}) -> boolean().
-is_item_not_found({error, #stanza_error{reason = 'item-not-found'}}) -> true;
-is_item_not_found({error, _}) -> false.
-
--spec is_not_subscribed({error, stanza_error()}) -> boolean().
-is_not_subscribed({error, StanzaError}) ->
- xmpp:has_subtag(StanzaError, #ps_error{type = 'not-subscribed'}).
-
-depends(_Host, _Opts) ->
- [{mod_pubsub, hard}].
-
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1.
-
-mod_options(_Host) ->
- [{host, <<"mix.@HOST@">>},
- {hosts, []}].
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index 8a80e3012..1fadf1a1a 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index f5213f212..626744281 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Sep 2007 by Badlop <badlop@ono.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -263,9 +263,9 @@ get_commands_spec() ->
#ejabberd_commands{name = subscribe_room, tags = [muc_room],
desc = "Subscribe to a MUC conference",
module = ?MODULE, function = subscribe_room,
- args_desc = ["Full JID, including some resource", "a user's nick",
+ args_desc = ["User JID", "a user's nick",
"the room to subscribe", "nodes separated by commas: ,"],
- args_example = ["tom@localhost/dummy", "Tom", "room1@conference.localhost",
+ args_example = ["tom@localhost", "Tom", "room1@conference.localhost",
"urn:xmpp:mucsub:nodes:messages,urn:xmpp:mucsub:nodes:affiliations"],
result_desc = "The list of nodes that has subscribed",
result_example = ["urn:xmpp:mucsub:nodes:messages",
@@ -357,7 +357,7 @@ build_summary_room(Name, Host, Pid) ->
C = get_room_config(Pid),
Public = C#config.public,
S = get_room_state(Pid),
- Participants = dict:size(S#state.users),
+ Participants = maps:size(S#state.users),
{<<Name/binary, "@", Host/binary>>,
misc:atom_to_binary(Public),
Participants
@@ -523,7 +523,7 @@ build_info_room({Name, Host, Pid}) ->
S = get_room_state(Pid),
Just_created = S#state.just_created,
- Num_participants = length(dict:fetch_keys(S#state.users)),
+ Num_participants = maps:size(S#state.users),
History = (S#state.history)#lqueue.queue,
Ts_last_message =
@@ -778,7 +778,7 @@ decide_room({_Room_name, _Host, Room_pid}, Last_allowed) ->
Just_created = S#state.just_created,
Room_users = S#state.users,
- Num_users = length(?DICT:to_list(Room_users)),
+ Num_users = maps:size(Room_users),
History = (S#state.history)#lqueue.queue,
Ts_now = calendar:universal_time(),
@@ -854,7 +854,7 @@ get_room_occupants(Pid) ->
Info#user.nick,
atom_to_list(Info#user.role)}
end,
- dict:to_list(S#state.users)).
+ maps:to_list(S#state.users)).
get_room_occupants_number(Room, Host) ->
case get_room_pid(Room, Host) of
@@ -862,7 +862,7 @@ get_room_occupants_number(Room, Host) ->
throw({error, room_not_found});
Pid ->
S = get_room_state(Pid),
- dict:size(S#state.users)
+ maps:size(S#state.users)
end.
%%----------------------------
@@ -1039,7 +1039,7 @@ get_room_affiliations(Name, Service) ->
{ok, Pid} ->
%% Get the PID of the online room, then request its state
{ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
- Affiliations = ?DICT:to_list(StateData#state.affiliations),
+ Affiliations = maps:to_list(StateData#state.affiliations),
lists:map(
fun({{Uname, Domain, _Res}, {Aff, Reason}}) when is_atom(Aff)->
{Uname, Domain, Aff, Reason};
@@ -1104,9 +1104,8 @@ subscribe_room(User, Nick, Room, Nodes) ->
try jid:decode(Room) of
#jid{luser = Name, lserver = Host} when Name /= <<"">> ->
try jid:decode(User) of
- #jid{lresource = <<"">>} ->
- throw({error, "User's JID should have a resource"});
- UserJID ->
+ UserJID1 ->
+ UserJID = jid:replace_resource(UserJID1, <<"modmucadmin">>),
case get_room_pid(Name, Host) of
Pid when is_pid(Pid) ->
case p1_fsm:sync_send_all_state_event(
@@ -1173,7 +1172,7 @@ get_config_opt_name(Pos) ->
{get_config_opt_name(Opt), element(Opt, Config)}).
make_opts(StateData) ->
Config = StateData#state.config,
- Subscribers = (?DICT):fold(
+ Subscribers = maps:fold(
fun(_LJID, Sub, Acc) ->
[{Sub#subscriber.jid,
Sub#subscriber.nick,
@@ -1205,7 +1204,7 @@ make_opts(StateData) ->
{captcha_whitelist,
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
{affiliations,
- (?DICT):to_list(StateData#state.affiliations)},
+ maps:to_list(StateData#state.affiliations)},
{subject, StateData#state.subject},
{subject_author, StateData#state.subject_author},
{subscribers, Subscribers}].
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index 7f5ca1b71..42a9cc0a5 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Mar 2006 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -887,7 +887,7 @@ get_room_occupants(RoomJIDString) ->
MucService = RoomJID#jid.lserver,
StateData = get_room_state(RoomName, MucService),
[{U#user.jid, U#user.nick, U#user.role}
- || {_, U} <- (?DICT):to_list(StateData#state.users)].
+ || U <- maps:values(StateData#state.users)].
-spec get_room_state(binary(), binary()) -> mod_muc_room:state().
diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl
index 43cf1aa01..32006256e 100644
--- a/src/mod_muc_mnesia.erl
+++ b/src/mod_muc_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_muc_riak.erl b/src/mod_muc_riak.erl
index d9e0732db..a5d02fd1a 100644
--- a/src/mod_muc_riak.erl
+++ b/src/mod_muc_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 020772781..7c7a9ecf8 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -5,7 +5,7 @@
%%% Created : 19 Mar 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -51,10 +51,10 @@
code_change/4]).
-include("logger.hrl").
-
-include("xmpp.hrl").
-
+-include("translate.hrl").
-include("mod_muc_room.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(MAX_USERS_DEFAULT_LIST,
[5, 10, 20, 30, 50, 100, 200, 500, 1000, 2000, 5000]).
@@ -83,10 +83,10 @@
-callback set_affiliation(binary(), binary(), binary(), jid(), affiliation(),
binary()) -> ok | {error, any()}.
-callback set_affiliations(binary(), binary(), binary(),
- dict:dict()) -> ok | {error, any()}.
+ map()) -> ok | {error, any()}.
-callback get_affiliation(binary(), binary(), binary(),
binary(), binary()) -> {ok, affiliation()} | {error, any()}.
--callback get_affiliations(binary(), binary(), binary()) -> {ok, dict:dict()} | {error, any()}.
+-callback get_affiliations(binary(), binary(), binary()) -> {ok, map()} | {error, any()}.
-callback search_affiliation(binary(), binary(), binary(), affiliation()) ->
{ok, [{ljid(), {affiliation(), binary()}}]} | {error, any()}.
@@ -435,11 +435,10 @@ normal_state({route, ToNick,
{next_state, normal_state, StateData}
end;
normal_state({route, ToNick,
- #iq{from = From, type = Type, lang = Lang} = Packet},
- StateData) ->
- case {(StateData#state.config)#config.allow_query_users,
- (?DICT):find(jid:tolower(From), StateData#state.users)} of
- {true, {ok, #user{nick = FromNick}}} ->
+ #iq{from = From, lang = Lang} = Packet},
+ #state{config = #config{allow_query_users = AllowQuery}} = StateData) ->
+ try maps:get(jid:tolower(From), StateData#state.users) of
+ #user{nick = FromNick} when AllowQuery orelse ToNick == FromNick ->
case find_jid_by_nick(ToNick, StateData) of
false ->
ErrText = <<"Recipient is not in the conference room">>,
@@ -447,28 +446,33 @@ normal_state({route, ToNick,
ejabberd_router:route_error(Packet, Err);
To ->
FromJID = jid:replace_resource(StateData#state.jid, FromNick),
- if Type == get; Type == set ->
- ToJID = case is_vcard_request(Packet) of
- true -> jid:remove_resource(To);
- false -> To
- end,
+ case direct_iq_type(Packet) of
+ vcard ->
ejabberd_router:route_iq(
- xmpp:set_from_to(Packet, FromJID, ToJID), Packet, self());
- true ->
- ejabberd_router:route(
- xmpp:set_from_to(Packet, FromJID, To))
+ xmpp:set_from_to(Packet, FromJID, jid:remove_resource(To)),
+ Packet, self());
+ ping when ToNick == FromNick ->
+ %% Self-ping optimization from XEP-0410
+ ejabberd_router:route(xmpp:make_iq_result(Packet));
+ response ->
+ ejabberd_router:route(xmpp:set_from_to(Packet, FromJID, To));
+ #stanza_error{} = Err ->
+ ejabberd_router:route_error(Packet, Err);
+ _OtherRequest ->
+ ejabberd_router:route_iq(
+ xmpp:set_from_to(Packet, FromJID, To), Packet, self())
end
end;
- {true, error} ->
- ErrText = <<"Only occupants are allowed to send queries "
- "to the conference">>,
- Err = xmpp:err_not_acceptable(ErrText, Lang),
- ejabberd_router:route_error(Packet, Err);
_ ->
ErrText = <<"Queries to the conference members are "
"not allowed in this room">>,
Err = xmpp:err_not_allowed(ErrText, Lang),
ejabberd_router:route_error(Packet, Err)
+ catch _:{badkey, _} ->
+ ErrText = <<"Only occupants are allowed to send queries "
+ "to the conference">>,
+ Err = xmpp:err_not_acceptable(ErrText, Lang),
+ ejabberd_router:route_error(Packet, Err)
end,
{next_state, normal_state, StateData};
normal_state(_Event, StateData) ->
@@ -507,7 +511,7 @@ handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
- Len = ?DICT:size(StateData#state.nicks),
+ Len = maps:size(StateData#state.nicks),
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
true ->
get_roomdesc_reply(JID, StateData,
@@ -542,7 +546,7 @@ handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData
end;
handle_sync_event(get_subscribers, _From, StateName, StateData) ->
JIDs = lists:map(fun jid:make/1,
- ?DICT:fetch_keys(StateData#state.subscribers)),
+ maps:keys(StateData#state.subscribers)),
{reply, {ok, JIDs}, StateName, StateData};
handle_sync_event({muc_subscribe, From, Nick, Nodes}, _From,
StateName, StateData) ->
@@ -583,12 +587,10 @@ handle_sync_event({muc_unsubscribe, From}, _From, StateName, StateData) ->
{reply, {error, get_error_text(Err)}, StateName, StateData}
end;
handle_sync_event({is_subscribed, From}, _From, StateName, StateData) ->
- IsSubs = case (?DICT):find(jid:split(From), StateData#state.subscribers) of
- {ok, #subscriber{nodes = Nodes}} ->
- {true, Nodes};
- error ->
- false
- end,
+ IsSubs = try maps:get(jid:split(From), StateData#state.subscribers) of
+ #subscriber{nodes = Nodes} -> {true, Nodes}
+ catch _:{badkey, _} -> false
+ end,
{reply, IsSubs, StateName, StateData};
handle_sync_event(_Event, _From, StateName,
StateData) ->
@@ -643,30 +645,27 @@ handle_info(process_room_queue,
end;
handle_info({captcha_succeed, From}, normal_state,
StateData) ->
- NewState = case (?DICT):find(From,
- StateData#state.robots)
- of
- {ok, {Nick, Packet}} ->
- Robots = (?DICT):store(From, passed,
- StateData#state.robots),
- add_new_user(From, Nick, Packet,
- StateData#state{robots = Robots});
- _ -> StateData
+ NewState = case maps:get(From, StateData#state.robots, passed) of
+ {Nick, Packet} ->
+ Robots = maps:put(From, passed, StateData#state.robots),
+ add_new_user(From, Nick, Packet,
+ StateData#state{robots = Robots});
+ passed ->
+ StateData
end,
{next_state, normal_state, NewState};
handle_info({captcha_failed, From}, normal_state,
StateData) ->
- NewState = case (?DICT):find(From,
- StateData#state.robots)
- of
- {ok, {_Nick, Packet}} ->
- Robots = (?DICT):erase(From, StateData#state.robots),
- Txt = <<"The CAPTCHA verification has failed">>,
- Lang = xmpp:get_lang(Packet),
- Err = xmpp:err_not_authorized(Txt, Lang),
- ejabberd_router:route_error(Packet, Err),
- StateData#state{robots = Robots};
- _ -> StateData
+ NewState = case maps:get(From, StateData#state.robots, passed) of
+ {_Nick, Packet} ->
+ Robots = maps:remove(From, StateData#state.robots),
+ Txt = <<"The CAPTCHA verification has failed">>,
+ Lang = xmpp:get_lang(Packet),
+ Err = xmpp:err_not_authorized(Txt, Lang),
+ ejabberd_router:route_error(Packet, Err),
+ StateData#state{robots = Robots};
+ passed ->
+ StateData
end,
{next_state, normal_state, NewState};
handle_info(shutdown, _StateName, StateData) ->
@@ -714,20 +713,19 @@ terminate(Reason, _StateName, StateData) ->
reason = ReasonT,
role = none}],
status_codes = [332,110]}]},
- (?DICT):fold(fun (LJID, Info, _) ->
- Nick = Info#user.nick,
- case Reason of
- shutdown ->
- send_wrapped(jid:replace_resource(StateData#state.jid,
- Nick),
- Info#user.jid, Packet,
- ?NS_MUCSUB_NODES_PARTICIPANTS,
- StateData);
- _ -> ok
- end,
- tab_remove_online_user(LJID, StateData)
- end,
- [], get_users_and_subscribers(StateData)),
+ maps:fold(
+ fun(LJID, Info, _) ->
+ Nick = Info#user.nick,
+ case Reason of
+ shutdown ->
+ send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
+ Info#user.jid, Packet,
+ ?NS_MUCSUB_NODES_PARTICIPANTS,
+ StateData);
+ _ -> ok
+ end,
+ tab_remove_online_user(LJID, StateData)
+ end, [], get_users_and_subscribers(StateData)),
add_to_log(room_existence, stopped, StateData),
mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self(),
StateData#state.server_host),
@@ -957,11 +955,22 @@ process_voice_approval(From, Pkt, VoiceApproval, StateData) ->
StateData
end.
--spec is_vcard_request(iq()) -> boolean().
-is_vcard_request(#iq{type = T, sub_els = [El]}) ->
- (T == get orelse T == set) andalso xmpp:get_ns(El) == ?NS_VCARD;
-is_vcard_request(_) ->
- false.
+-spec direct_iq_type(iq()) -> vcard | ping | request | response | stanza_error().
+direct_iq_type(#iq{type = T, sub_els = SubEls, lang = Lang}) when T == get; T == set ->
+ case SubEls of
+ [El] ->
+ case xmpp:get_ns(El) of
+ ?NS_VCARD when T == get -> vcard;
+ ?NS_PING when T == get -> ping;
+ _ -> request
+ end;
+ [] ->
+ xmpp:err_bad_request(?T("No child elements found"), Lang);
+ [_|_] ->
+ xmpp:err_bad_request(?T("Too many child elements"), Lang)
+ end;
+direct_iq_type(#iq{}) ->
+ response.
%% @doc Check if this non participant can send message to room.
%%
@@ -982,17 +991,15 @@ is_user_allowed_message_nonparticipant(JID,
%% If the JID is not a participant, return values for a service message.
-spec get_participant_data(jid(), state()) -> {binary(), role()}.
get_participant_data(From, StateData) ->
- case (?DICT):find(jid:tolower(From),
- StateData#state.users)
- of
- {ok, #user{nick = FromNick, role = Role}} ->
- {FromNick, Role};
- error ->
- case ?DICT:find(jid:tolower(jid:remove_resource(From)),
- StateData#state.subscribers) of
- {ok, #subscriber{nick = FromNick}} ->
- {FromNick, none};
- error ->
+ try maps:get(jid:tolower(From), StateData#state.users) of
+ #user{nick = FromNick, role = Role} ->
+ {FromNick, Role}
+ catch _:{badkey, _} ->
+ try maps:get(jid:tolower(jid:remove_resource(From)),
+ StateData#state.subscribers) of
+ #subscriber{nick = FromNick} ->
+ {FromNick, none}
+ catch _:{badkey, _} ->
{<<"">>, moderator}
end
end.
@@ -1079,8 +1086,8 @@ do_process_presence(Nick, #presence{from = From, type = unavailable} = Packet,
_ -> Packet
end,
NewState = add_user_presence_un(From, NewPacket, StateData),
- case (?DICT):find(Nick, StateData#state.nicks) of
- {ok, [_, _ | _]} ->
+ case maps:get(Nick, StateData#state.nicks, []) of
+ [_, _ | _] ->
Aff = get_affiliation(From, StateData),
Item = #muc_item{affiliation = Aff, role = none, jid = From},
Pres = xmpp:set_subtag(
@@ -1114,8 +1121,8 @@ maybe_strip_status_from_presence(From, Packet, StateData) ->
-spec close_room_if_temporary_and_empty(state()) -> fsm_transition().
close_room_if_temporary_and_empty(StateData1) ->
case not (StateData1#state.config)#config.persistent
- andalso (?DICT):size(StateData1#state.users) == 0
- andalso (?DICT):size(StateData1#state.subscribers) == 0 of
+ andalso maps:size(StateData1#state.users) == 0
+ andalso maps:size(StateData1#state.subscribers) == 0 of
true ->
?INFO_MSG("Destroyed MUC room ~s because it's temporary "
"and empty",
@@ -1125,9 +1132,9 @@ close_room_if_temporary_and_empty(StateData1) ->
_ -> {next_state, normal_state, StateData1}
end.
--spec get_users_and_subscribers(state()) -> dict:dict().
+-spec get_users_and_subscribers(state()) -> map().
get_users_and_subscribers(StateData) ->
- OnlineSubscribers = ?DICT:fold(
+ OnlineSubscribers = maps:fold(
fun(LJID, _, Acc) ->
LBareJID = jid:remove_resource(LJID),
case is_subscriber(LBareJID, StateData) of
@@ -1137,16 +1144,16 @@ get_users_and_subscribers(StateData) ->
Acc
end
end, ?SETS:new(), StateData#state.users),
- ?DICT:fold(
+ maps:fold(
fun(LBareJID, #subscriber{nick = Nick}, Acc) ->
case ?SETS:is_element(LBareJID, OnlineSubscribers) of
false ->
- ?DICT:store(LBareJID,
- #user{jid = jid:make(LBareJID),
- nick = Nick,
- role = none,
- last_presence = undefined},
- Acc);
+ maps:put(LBareJID,
+ #user{jid = jid:make(LBareJID),
+ nick = Nick,
+ role = none,
+ last_presence = undefined},
+ Acc);
true ->
Acc
end
@@ -1155,12 +1162,12 @@ get_users_and_subscribers(StateData) ->
-spec is_user_online(jid(), state()) -> boolean().
is_user_online(JID, StateData) ->
LJID = jid:tolower(JID),
- (?DICT):is_key(LJID, StateData#state.users).
+ maps:is_key(LJID, StateData#state.users).
-spec is_subscriber(jid(), state()) -> boolean().
is_subscriber(JID, StateData) ->
LJID = jid:tolower(jid:remove_resource(JID)),
- (?DICT):is_key(LJID, StateData#state.subscribers).
+ maps:is_key(LJID, StateData#state.subscribers).
%% Check if the user is occupant of the room, or at least is an admin or owner.
-spec is_occupant_or_admin(jid(), state()) -> boolean().
@@ -1238,7 +1245,7 @@ get_error_text(#stanza_error{text = Txt}) ->
-spec make_reason(stanza(), jid(), state(), binary()) -> binary().
make_reason(Packet, From, StateData, Reason1) ->
- {ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users),
+ #user{nick = FromNick} = maps:get(jid:tolower(From), StateData#state.users),
Condition = get_error_condition(xmpp:get_error(Packet)),
str:format(Reason1, [FromNick, Condition]).
@@ -1251,9 +1258,9 @@ expulse_participant(Packet, From, StateData, Reason1) ->
status = xmpp:mk_text(Reason2)},
StateData),
LJID = jid:tolower(From),
- {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users),
- case (?DICT):find(Nick, StateData#state.nicks) of
- {ok, [_, _ | _]} ->
+ #user{nick = Nick} = maps:get(LJID, StateData#state.users),
+ case maps:get(Nick, StateData#state.nicks, []) of
+ [_, _ | _] ->
Aff = get_affiliation(From, StateData),
Item = #muc_item{affiliation = Aff, role = none, jid = From},
Pres = xmpp:set_subtag(
@@ -1268,7 +1275,7 @@ expulse_participant(Packet, From, StateData, Reason1) ->
-spec get_owners(state()) -> [jid:jid()].
get_owners(StateData) ->
- ?DICT:fold(
+ maps:fold(
fun(LJID, owner, Acc) ->
[jid:make(LJID)|Acc];
(LJID, {owner, _}, Acc) ->
@@ -1303,14 +1310,14 @@ set_affiliation_fallback(JID, Affiliation, StateData, Reason) ->
LJID = jid:remove_resource(jid:tolower(JID)),
Affiliations = case Affiliation of
none ->
- (?DICT):erase(LJID, StateData#state.affiliations);
+ maps:remove(LJID, StateData#state.affiliations);
_ ->
- (?DICT):store(LJID, {Affiliation, Reason},
- StateData#state.affiliations)
+ maps:put(LJID, {Affiliation, Reason},
+ StateData#state.affiliations)
end,
StateData#state{affiliations = Affiliations}.
--spec set_affiliations(dict:dict(), state()) -> state().
+-spec set_affiliations(map(), state()) -> state().
set_affiliations(Affiliations,
#state{config = #config{persistent = false}} = StateData) ->
set_affiliations_fallback(Affiliations, StateData);
@@ -1326,7 +1333,7 @@ set_affiliations(Affiliations, StateData) ->
set_affiliations_fallback(Affiliations, StateData)
end.
--spec set_affiliations_fallback(dict:dict(), state()) -> state().
+-spec set_affiliations_fallback(map(), state()) -> state().
set_affiliations_fallback(Affiliations, StateData) ->
StateData#state{affiliations = Affiliations}.
@@ -1364,32 +1371,23 @@ do_get_affiliation(JID, StateData) ->
-spec do_get_affiliation_fallback(jid(), state()) -> affiliation().
do_get_affiliation_fallback(JID, StateData) ->
LJID = jid:tolower(JID),
- case (?DICT):find(LJID, StateData#state.affiliations) of
- {ok, Affiliation} -> Affiliation;
- _ ->
- LJID1 = jid:remove_resource(LJID),
- case (?DICT):find(LJID1, StateData#state.affiliations)
- of
- {ok, Affiliation} -> Affiliation;
- _ ->
- LJID2 = setelement(1, LJID, <<"">>),
- case (?DICT):find(LJID2,
- StateData#state.affiliations)
- of
- {ok, Affiliation} -> Affiliation;
- _ ->
- LJID3 = jid:remove_resource(LJID2),
- case (?DICT):find(LJID3,
- StateData#state.affiliations)
- of
- {ok, Affiliation} -> Affiliation;
- _ -> none
+ try maps:get(LJID, StateData#state.affiliations)
+ catch _:{badkey, _} ->
+ BareLJID = jid:remove_resource(LJID),
+ try maps:get(BareLJID, StateData#state.affiliations)
+ catch _:{badkey, _} ->
+ DomainLJID = setelement(1, LJID, <<"">>),
+ try maps:get(DomainLJID, StateData#state.affiliations)
+ catch _:{badkey, _} ->
+ DomainBareLJID = jid:remove_resource(DomainLJID),
+ try maps:get(DomainBareLJID, StateData#state.affiliations)
+ catch _:{badkey, _} -> none
end
end
end
end.
--spec get_affiliations(state()) -> dict:dict().
+-spec get_affiliations(state()) -> map().
get_affiliations(#state{config = #config{persistent = false}} = StateData) ->
get_affiliations_callback(StateData);
get_affiliations(StateData) ->
@@ -1404,7 +1402,7 @@ get_affiliations(StateData) ->
Affiliations
end.
--spec get_affiliations_callback(state()) -> dict:dict().
+-spec get_affiliations_callback(state()) -> map().
get_affiliations_callback(StateData) ->
StateData#state.affiliations.
@@ -1425,56 +1423,52 @@ set_role(JID, Role, StateData) ->
LJID = jid:tolower(JID),
LJIDs = case LJID of
{U, S, <<"">>} ->
- (?DICT):fold(fun (J, _, Js) ->
- case J of
- {U, S, _} -> [J | Js];
- _ -> Js
- end
- end,
- [], StateData#state.users);
+ maps:fold(fun (J, _, Js) ->
+ case J of
+ {U, S, _} -> [J | Js];
+ _ -> Js
+ end
+ end, [], StateData#state.users);
_ ->
- case (?DICT):is_key(LJID, StateData#state.users) of
+ case maps:is_key(LJID, StateData#state.users) of
true -> [LJID];
_ -> []
end
end,
- {Users, Nicks} = case Role of
- none ->
- lists:foldl(fun (J, {Us, Ns}) ->
- NewNs = case (?DICT):find(J, Us)
- of
- {ok,
- #user{nick = Nick}} ->
- (?DICT):erase(Nick,
- Ns);
- _ -> Ns
- end,
- {(?DICT):erase(J, Us), NewNs}
- end,
- {StateData#state.users,
- StateData#state.nicks},
- LJIDs);
- _ ->
- {lists:foldl(
- fun (J, Us) ->
- {ok, User} = (?DICT):find(J, Us),
- if User#user.last_presence == undefined ->
- Us;
- true ->
- (?DICT):store(J, User#user{role = Role}, Us)
- end
- end,
- StateData#state.users, LJIDs),
- StateData#state.nicks}
- end,
+ {Users, Nicks} =
+ case Role of
+ none ->
+ lists:foldl(
+ fun (J, {Us, Ns}) ->
+ NewNs = try maps:get(J, Us) of
+ #user{nick = Nick} ->
+ maps:remove(Nick, Ns)
+ catch _:{badkey, _} ->
+ Ns
+ end,
+ {maps:remove(J, Us), NewNs}
+ end,
+ {StateData#state.users, StateData#state.nicks}, LJIDs);
+ _ ->
+ {lists:foldl(
+ fun (J, Us) ->
+ User = maps:get(J, Us),
+ if User#user.last_presence == undefined ->
+ Us;
+ true ->
+ maps:put(J, User#user{role = Role}, Us)
+ end
+ end, StateData#state.users, LJIDs),
+ StateData#state.nicks}
+ end,
StateData#state{users = Users, nicks = Nicks}.
-spec get_role(jid(), state()) -> role().
get_role(JID, StateData) ->
LJID = jid:tolower(JID),
- case (?DICT):find(LJID, StateData#state.users) of
- {ok, #user{role = Role}} -> Role;
- _ -> none
+ try maps:get(LJID, StateData#state.users) of
+ #user{role = Role} -> Role
+ catch _:{badkey, _} -> none
end.
-spec get_default_role(affiliation(), state()) -> role().
@@ -1659,29 +1653,29 @@ prepare_room_queue(StateData) ->
update_online_user(JID, #user{nick = Nick} = User, StateData) ->
LJID = jid:tolower(JID),
add_to_log(join, Nick, StateData),
- Nicks1 = case (?DICT):find(LJID, StateData#state.users) of
- {ok, #user{nick = OldNick}} ->
+ Nicks1 = try maps:get(LJID, StateData#state.users) of
+ #user{nick = OldNick} ->
case lists:delete(
- LJID, ?DICT:fetch(OldNick, StateData#state.nicks)) of
+ LJID, maps:get(OldNick, StateData#state.nicks)) of
[] ->
- ?DICT:erase(OldNick, StateData#state.nicks);
+ maps:remove(OldNick, StateData#state.nicks);
LJIDs ->
- ?DICT:store(OldNick, LJIDs, StateData#state.nicks)
- end;
- error ->
+ maps:put(OldNick, LJIDs, StateData#state.nicks)
+ end
+ catch _:{badkey, _} ->
StateData#state.nicks
end,
- Nicks = (?DICT):update(Nick,
- fun (LJIDs) -> [LJID|LJIDs -- [LJID]] end,
- [LJID], Nicks1),
- Users = (?DICT):update(LJID,
- fun(U) ->
- U#user{nick = Nick}
- end, User, StateData#state.users),
+ Nicks = maps:update_with(Nick,
+ fun (LJIDs) -> [LJID|LJIDs -- [LJID]] end,
+ [LJID], Nicks1),
+ Users = maps:update_with(LJID,
+ fun(U) ->
+ U#user{nick = Nick}
+ end, User, StateData#state.users),
NewStateData = StateData#state{users = Users, nicks = Nicks},
- case {?DICT:find(LJID, StateData#state.users),
- ?DICT:find(LJID, NewStateData#state.users)} of
- {{ok, #user{nick = Old}}, {ok, #user{nick = New}}} when Old /= New ->
+ case {maps:get(LJID, StateData#state.users, error),
+ maps:get(LJID, NewStateData#state.users, error)} of
+ {#user{nick = Old}, #user{nick = New}} when Old /= New ->
send_nick_changing(JID, Old, NewStateData, true, true);
_ ->
ok
@@ -1691,16 +1685,16 @@ update_online_user(JID, #user{nick = Nick} = User, StateData) ->
set_subscriber(JID, Nick, Nodes, StateData) ->
BareJID = jid:remove_resource(JID),
LBareJID = jid:tolower(BareJID),
- Subscribers = ?DICT:store(LBareJID,
- #subscriber{jid = BareJID,
- nick = Nick,
- nodes = Nodes},
- StateData#state.subscribers),
- Nicks = ?DICT:store(Nick, [LBareJID], StateData#state.subscriber_nicks),
+ Subscribers = maps:put(LBareJID,
+ #subscriber{jid = BareJID,
+ nick = Nick,
+ nodes = Nodes},
+ StateData#state.subscribers),
+ Nicks = maps:put(Nick, [LBareJID], StateData#state.subscriber_nicks),
NewStateData = StateData#state{subscribers = Subscribers,
subscriber_nicks = Nicks},
store_room(NewStateData, [{add_subscription, BareJID, Nick, Nodes}]),
- case not ?DICT:is_key(LBareJID, StateData#state.subscribers) of
+ case not maps:is_key(LBareJID, StateData#state.subscribers) of
true ->
send_subscriptions_change_notifications(BareJID, Nick, subscribe, NewStateData);
_ ->
@@ -1721,18 +1715,17 @@ remove_online_user(JID, StateData) ->
-spec remove_online_user(jid(), state(), binary()) -> state().
remove_online_user(JID, StateData, Reason) ->
LJID = jid:tolower(JID),
- {ok, #user{nick = Nick}} = (?DICT):find(LJID,
- StateData#state.users),
+ #user{nick = Nick} = maps:get(LJID, StateData#state.users),
add_to_log(leave, {Nick, Reason}, StateData),
tab_remove_online_user(JID, StateData),
- Users = (?DICT):erase(LJID, StateData#state.users),
- Nicks = case (?DICT):find(Nick, StateData#state.nicks)
- of
- {ok, [LJID]} ->
- (?DICT):erase(Nick, StateData#state.nicks);
- {ok, U} ->
- (?DICT):store(Nick, U -- [LJID], StateData#state.nicks);
- error -> StateData#state.nicks
+ Users = maps:remove(LJID, StateData#state.users),
+ Nicks = try maps:get(Nick, StateData#state.nicks) of
+ [LJID] ->
+ maps:remove(Nick, StateData#state.nicks);
+ U ->
+ maps:put(Nick, U -- [LJID], StateData#state.nicks)
+ catch _:{badkey, _} ->
+ StateData#state.nicks
end,
StateData#state{users = Users, nicks = Nicks}.
@@ -1756,63 +1749,54 @@ strip_status(Presence) ->
add_user_presence(JID, Presence, StateData) ->
LJID = jid:tolower(JID),
FPresence = filter_presence(Presence),
- Users = (?DICT):update(LJID,
- fun (#user{} = User) ->
- User#user{last_presence = FPresence}
- end,
- StateData#state.users),
+ Users = maps:update_with(LJID,
+ fun (#user{} = User) ->
+ User#user{last_presence = FPresence}
+ end, StateData#state.users),
StateData#state{users = Users}.
-spec add_user_presence_un(jid(), presence(), state()) -> state().
add_user_presence_un(JID, Presence, StateData) ->
LJID = jid:tolower(JID),
FPresence = filter_presence(Presence),
- Users = (?DICT):update(LJID,
- fun (#user{} = User) ->
- User#user{last_presence = FPresence,
- role = none}
- end,
- StateData#state.users),
+ Users = maps:update_with(LJID,
+ fun (#user{} = User) ->
+ User#user{last_presence = FPresence,
+ role = none}
+ end, StateData#state.users),
StateData#state{users = Users}.
%% Find and return a list of the full JIDs of the users of Nick.
%% Return jid record.
-spec find_jids_by_nick(binary(), state()) -> [jid()].
find_jids_by_nick(Nick, StateData) ->
- Nicks = ?DICT:merge(fun(_, Val, _) -> Val end,
- StateData#state.nicks,
- StateData#state.subscriber_nicks),
- case (?DICT):find(Nick, Nicks) of
- {ok, [User]} -> [jid:make(User)];
- {ok, Users} -> [jid:make(LJID) || LJID <- Users];
- error -> []
- end.
+ Users = case maps:get(Nick, StateData#state.nicks, []) of
+ [] -> maps:get(Nick, StateData#state.subscriber_nicks, []);
+ Us -> Us
+ end,
+ [jid:make(LJID) || LJID <- Users].
%% Find and return the full JID of the user of Nick with
%% highest-priority presence. Return jid record.
-spec find_jid_by_nick(binary(), state()) -> jid() | false.
find_jid_by_nick(Nick, StateData) ->
- case (?DICT):find(Nick, StateData#state.nicks) of
- {ok, [User]} -> jid:make(User);
- {ok, [FirstUser | Users]} ->
- #user{last_presence = FirstPresence} =
- (?DICT):fetch(FirstUser, StateData#state.users),
- {LJID, _} = lists:foldl(fun (Compare,
- {HighestUser, HighestPresence}) ->
- #user{last_presence = P1} =
- (?DICT):fetch(Compare,
- StateData#state.users),
- case higher_presence(P1,
- HighestPresence)
- of
- true -> {Compare, P1};
- false ->
- {HighestUser, HighestPresence}
- end
- end,
- {FirstUser, FirstPresence}, Users),
- jid:make(LJID);
- error -> false
+ try maps:get(Nick, StateData#state.nicks) of
+ [User] -> jid:make(User);
+ [FirstUser | Users] ->
+ #user{last_presence = FirstPresence} =
+ maps:get(FirstUser, StateData#state.users),
+ {LJID, _} = lists:foldl(
+ fun(Compare, {HighestUser, HighestPresence}) ->
+ #user{last_presence = P1} =
+ maps:get(Compare, StateData#state.users),
+ case higher_presence(P1, HighestPresence) of
+ true -> {Compare, P1};
+ false -> {HighestUser, HighestPresence}
+ end
+ end, {FirstUser, FirstPresence}, Users),
+ jid:make(LJID)
+ catch _:{badkey, _} ->
+ false
end.
-spec higher_presence(undefined | presence(),
@@ -1834,7 +1818,7 @@ get_priority_from_presence(#presence{priority = Prio}) ->
-spec find_nick_by_jid(jid(), state()) -> binary().
find_nick_by_jid(JID, StateData) ->
LJID = jid:tolower(JID),
- {ok, #user{nick = Nick}} = (?DICT):find(LJID, StateData#state.users),
+ #user{nick = Nick} = maps:get(LJID, StateData#state.users),
Nick.
-spec is_nick_change(jid(), binary(), state()) -> boolean().
@@ -1843,8 +1827,7 @@ is_nick_change(JID, Nick, StateData) ->
case Nick of
<<"">> -> false;
_ ->
- {ok, #user{nick = OldNick}} = (?DICT):find(LJID,
- StateData#state.users),
+ #user{nick = OldNick} = maps:get(LJID, StateData#state.users),
Nick /= OldNick
end.
@@ -1852,9 +1835,9 @@ is_nick_change(JID, Nick, StateData) ->
nick_collision(User, Nick, StateData) ->
UserOfNick = case find_jid_by_nick(Nick, StateData) of
false ->
- case ?DICT:find(Nick, StateData#state.subscriber_nicks) of
- {ok, [J]} -> J;
- error -> false
+ try maps:get(Nick, StateData#state.subscriber_nicks) of
+ [J] -> J
+ catch _:{badkey, _} -> false
end;
J -> J
end,
@@ -1871,8 +1854,7 @@ add_new_user(From, Nick, Packet, StateData) ->
MaxUsers = get_max_users(StateData),
MaxAdminUsers = MaxUsers +
get_max_users_admin_threshold(StateData),
- NUsers = dict:fold(fun (_, _, Acc) -> Acc + 1 end, 0,
- StateData#state.users),
+ NUsers = maps:size(StateData#state.users),
Affiliation = get_affiliation(From, StateData),
ServiceAffiliation = get_service_affiliation(From,
StateData),
@@ -1974,7 +1956,7 @@ add_new_user(From, Nick, Packet, StateData) ->
true ->
NewStateData#state{just_created = false};
false ->
- Robots = (?DICT):erase(From, StateData#state.robots),
+ Robots = maps:remove(From, StateData#state.robots),
NewStateData#state{robots = Robots}
end,
if not IsSubscribeRequest -> ResultState;
@@ -2002,8 +1984,8 @@ add_new_user(From, Nick, Packet, StateData) ->
to = From,
id = ID, body = Body,
sub_els = CaptchaEls},
- Robots = (?DICT):store(From, {Nick, Packet},
- StateData#state.robots),
+ Robots = maps:put(From, {Nick, Packet},
+ StateData#state.robots),
ejabberd_router:route(MsgPkt),
NewState = StateData#state{robots = Robots},
if not IsSubscribeRequest ->
@@ -2072,9 +2054,9 @@ check_captcha(Affiliation, From, StateData) ->
andalso ejabberd_captcha:is_feature_available()
of
true when Affiliation == none ->
- case (?DICT):find(From, StateData#state.robots) of
- {ok, passed} -> true;
- _ ->
+ case maps:get(From, StateData#state.robots, error) of
+ passed -> true;
+ _ ->
WList =
(StateData#state.config)#config.captcha_whitelist,
#jid{luser = U, lserver = S, lresource = R} = From,
@@ -2151,7 +2133,7 @@ is_room_overcrowded(StateData) ->
MaxUsersPresence = gen_mod:get_module_opt(
StateData#state.server_host,
mod_muc, max_users_presence),
- (?DICT):size(StateData#state.users) > MaxUsersPresence.
+ maps:size(StateData#state.users) > MaxUsersPresence.
-spec presence_broadcast_allowed(jid(), state()) -> boolean().
presence_broadcast_allowed(JID, StateData) ->
@@ -2189,7 +2171,7 @@ send_self_presence(JID, State) ->
-spec send_initial_presence(jid(), state(), state()) -> ok.
send_initial_presence(NJID, StateData, OldStateData) ->
- send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
+ send_new_presence(NJID, <<"">>, true, StateData, OldStateData).
-spec send_update_presence(jid(), state(), state()) -> ok.
send_update_presence(JID, StateData, OldStateData) ->
@@ -2207,22 +2189,21 @@ send_update_presence1(JID, Reason, StateData, OldStateData) ->
LJID = jid:tolower(JID),
LJIDs = case LJID of
{U, S, <<"">>} ->
- (?DICT):fold(fun (J, _, Js) ->
- case J of
- {U, S, _} -> [J | Js];
- _ -> Js
- end
- end,
- [], StateData#state.users);
+ maps:fold(fun (J, _, Js) ->
+ case J of
+ {U, S, _} -> [J | Js];
+ _ -> Js
+ end
+ end, [], StateData#state.users);
_ ->
- case (?DICT):is_key(LJID, StateData#state.users) of
+ case maps:is_key(LJID, StateData#state.users) of
true -> [LJID];
_ -> []
end
end,
lists:foreach(fun (J) ->
- send_new_presence1(J, Reason, false, StateData,
- OldStateData)
+ send_new_presence(J, Reason, false, StateData,
+ OldStateData)
end,
LJIDs).
@@ -2234,14 +2215,6 @@ send_new_presence(NJID, StateData, OldStateData) ->
send_new_presence(NJID, Reason, StateData, OldStateData) ->
send_new_presence(NJID, Reason, false, StateData, OldStateData).
--spec send_new_presence(jid(), binary(), boolean(), state(), state()) -> ok.
-send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
- case is_room_overcrowded(StateData) of
- true -> ok;
- false -> send_new_presence1(NJID, Reason, IsInitialPresence, StateData,
- OldStateData)
- end.
-
-spec is_ra_changed(jid(), boolean(), state(), state()) -> boolean().
is_ra_changed(_, _IsInitialPresence = true, _, _) ->
false;
@@ -2257,32 +2230,31 @@ is_ra_changed(JID, _IsInitialPresence = false, NewStateData, OldStateData) ->
(NewRole /= OldRole) or (NewAff /= OldAff)
end.
--spec send_new_presence1(jid(), binary(), boolean(), state(), state()) -> ok.
-send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
+-spec send_new_presence(jid(), binary(), boolean(), state(), state()) -> ok.
+send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
LNJID = jid:tolower(NJID),
- #user{nick = Nick} = (?DICT):fetch(LNJID, StateData#state.users),
+ #user{nick = Nick} = maps:get(LNJID, StateData#state.users),
LJID = find_jid_by_nick(Nick, StateData),
- {ok,
- #user{jid = RealJID, role = Role0,
- last_presence = Presence0} = UserInfo} =
- (?DICT):find(jid:tolower(LJID),
- StateData#state.users),
+ #user{jid = RealJID, role = Role0,
+ last_presence = Presence0} = UserInfo =
+ maps:get(jid:tolower(LJID), StateData#state.users),
{Role1, Presence1} =
case presence_broadcast_allowed(NJID, StateData) of
true -> {Role0, Presence0};
false -> {none, #presence{type = unavailable}}
end,
Affiliation = get_affiliation(LJID, StateData),
- UserList =
- case not (presence_broadcast_allowed(NJID, StateData) orelse
- presence_broadcast_allowed(NJID, OldStateData)) of
+ UserMap =
+ case is_room_overcrowded(StateData) orelse
+ (not (presence_broadcast_allowed(NJID, StateData) orelse
+ presence_broadcast_allowed(NJID, OldStateData))) of
true ->
- [{LNJID, UserInfo}];
+ #{LNJID => UserInfo};
false ->
- (?DICT):to_list(get_users_and_subscribers(StateData))
+ get_users_and_subscribers(StateData)
end,
- lists:foreach(
- fun({LUJID, Info}) ->
+ maps:fold(
+ fun(LUJID, Info, _) ->
IsSelfPresence = LNJID == LUJID,
{Role, Presence} = if IsSelfPresence -> {Role0, Presence0};
true -> {Role1, Presence1}
@@ -2321,8 +2293,7 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
true ->
ok
end
- end,
- UserList).
+ end, ok, UserMap).
-spec send_existing_presences(jid(), state()) -> ok.
send_existing_presences(ToJID, StateData) ->
@@ -2334,15 +2305,13 @@ send_existing_presences(ToJID, StateData) ->
-spec send_existing_presences1(jid(), state()) -> ok.
send_existing_presences1(ToJID, StateData) ->
LToJID = jid:tolower(ToJID),
- {ok, #user{jid = RealToJID, role = Role}} =
- (?DICT):find(LToJID, StateData#state.users),
- lists:foreach(
- fun({FromNick, _Users}) ->
+ #user{jid = RealToJID, role = Role} = maps:get(LToJID, StateData#state.users),
+ maps:fold(
+ fun(FromNick, _Users, _) ->
LJID = find_jid_by_nick(FromNick, StateData),
#user{jid = FromJID, role = FromRole,
last_presence = Presence} =
- (?DICT):fetch(jid:tolower(LJID),
- StateData#state.users),
+ maps:get(jid:tolower(LJID), StateData#state.users),
PresenceBroadcast =
lists:member(
FromRole, (StateData#state.config)#config.presence_broadcast),
@@ -2364,41 +2333,34 @@ send_existing_presences1(ToJID, StateData) ->
send_wrapped(jid:replace_resource(StateData#state.jid, FromNick),
RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData)
end
- end,
- (?DICT):to_list(StateData#state.nicks)).
+ end, ok, StateData#state.nicks).
-spec set_nick(jid(), binary(), state()) -> state().
set_nick(JID, Nick, State) ->
LJID = jid:tolower(JID),
- {ok, #user{nick = OldNick}} = (?DICT):find(LJID, State#state.users),
- Users = (?DICT):update(LJID,
- fun (#user{} = User) -> User#user{nick = Nick} end,
- State#state.users),
- OldNickUsers = (?DICT):fetch(OldNick, State#state.nicks),
- NewNickUsers = case (?DICT):find(Nick, State#state.nicks) of
- {ok, U} -> U;
- error -> []
- end,
+ #user{nick = OldNick} = maps:get(LJID, State#state.users),
+ Users = maps:update_with(LJID,
+ fun (#user{} = User) -> User#user{nick = Nick} end,
+ State#state.users),
+ OldNickUsers = maps:get(OldNick, State#state.nicks),
+ NewNickUsers = maps:get(Nick, State#state.nicks, []),
Nicks = case OldNickUsers of
[LJID] ->
- (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]],
- (?DICT):erase(OldNick, State#state.nicks));
+ maps:put(Nick, [LJID | NewNickUsers -- [LJID]],
+ maps:remove(OldNick, State#state.nicks));
[_ | _] ->
- (?DICT):store(Nick, [LJID | NewNickUsers -- [LJID]],
- (?DICT):store(OldNick, OldNickUsers -- [LJID],
- State#state.nicks))
+ maps:put(Nick, [LJID | NewNickUsers -- [LJID]],
+ maps:put(OldNick, OldNickUsers -- [LJID],
+ State#state.nicks))
end,
State#state{users = Users, nicks = Nicks}.
-spec change_nick(jid(), binary(), state()) -> state().
change_nick(JID, Nick, StateData) ->
LJID = jid:tolower(JID),
- {ok, #user{nick = OldNick}} = (?DICT):find(LJID, StateData#state.users),
- OldNickUsers = (?DICT):fetch(OldNick, StateData#state.nicks),
- NewNickUsers = case (?DICT):find(Nick, StateData#state.nicks) of
- {ok, U} -> U;
- error -> []
- end,
+ #user{nick = OldNick} = maps:get(LJID, StateData#state.users),
+ OldNickUsers = maps:get(OldNick, StateData#state.nicks),
+ NewNickUsers = maps:get(Nick, StateData#state.nicks, []),
SendOldUnavailable = length(OldNickUsers) == 1,
SendNewAvailable = SendOldUnavailable orelse NewNickUsers == [],
NewStateData = set_nick(JID, Nick, StateData),
@@ -2414,14 +2376,12 @@ change_nick(JID, Nick, StateData) ->
-spec send_nick_changing(jid(), binary(), state(), boolean(), boolean()) -> ok.
send_nick_changing(JID, OldNick, StateData,
SendOldUnavailable, SendNewAvailable) ->
- {ok,
- #user{jid = RealJID, nick = Nick, role = Role,
- last_presence = Presence}} =
- (?DICT):find(jid:tolower(JID),
- StateData#state.users),
+ #user{jid = RealJID, nick = Nick, role = Role,
+ last_presence = Presence} =
+ maps:get(jid:tolower(JID), StateData#state.users),
Affiliation = get_affiliation(JID, StateData),
- lists:foreach(
- fun({LJID, Info}) when Presence /= undefined ->
+ maps:fold(
+ fun(LJID, Info, _) when Presence /= undefined ->
IsSelfPresence = LJID == jid:tolower(JID),
Item0 = #muc_item{affiliation = Affiliation, role = Role},
Item = case Info#user.role == moderator orelse
@@ -2456,24 +2416,23 @@ send_nick_changing(JID, OldNick, StateData,
StateData);
true -> ok
end;
- (_) ->
+ (_, _, _) ->
ok
- end,
- ?DICT:to_list(get_users_and_subscribers(StateData))).
+ end, ok, get_users_and_subscribers(StateData)).
-spec maybe_send_affiliation(jid(), affiliation(), state()) -> ok.
maybe_send_affiliation(JID, Affiliation, StateData) ->
LJID = jid:tolower(JID),
Users = get_users_and_subscribers(StateData),
IsOccupant = case LJID of
- {LUser, LServer, <<"">>} ->
- not (?DICT):is_empty(
- (?DICT):filter(fun({U, S, _}, _) ->
- U == LUser andalso
- S == LServer
- end, Users));
- {_LUser, _LServer, _LResource} ->
- (?DICT):is_key(LJID, Users)
+ {LUser, LServer, <<"">>} ->
+ #{} /= maps:filter(
+ fun({U, S, _}, _) ->
+ U == LUser andalso
+ S == LServer
+ end, Users);
+ {_LUser, _LServer, _LResource} ->
+ maps:is_key(LJID, Users)
end,
case IsOccupant of
true ->
@@ -2492,11 +2451,11 @@ send_affiliation(JID, Affiliation, StateData) ->
Users = get_users_and_subscribers(StateData),
Recipients = case (StateData#state.config)#config.anonymous of
true ->
- (?DICT):filter(fun(_, #user{role = moderator}) ->
- true;
- (_, _) ->
- false
- end, Users);
+ maps:filter(fun(_, #user{role = moderator}) ->
+ true;
+ (_, _) ->
+ false
+ end, Users);
false ->
Users
end,
@@ -2690,7 +2649,7 @@ user_to_item(#user{role = Role, nick = Nick, jid = JID},
search_role(Role, StateData) ->
lists:filter(fun ({_, #user{role = R}}) -> Role == R
end,
- (?DICT):to_list(StateData#state.users)).
+ maps:to_list(StateData#state.users)).
-spec search_affiliation(affiliation(), state()) ->
[{ljid(),
@@ -2720,8 +2679,7 @@ search_affiliation_fallback(Affiliation, StateData) ->
{A1, _Reason} -> Affiliation == A1;
_ -> Affiliation == A
end
- end,
- (?DICT):to_list(StateData#state.affiliations)).
+ end, maps:to_list(StateData#state.affiliations)).
-spec process_admin_items_set(jid(), [muc_item()], binary(),
#state{}) -> {result, undefined, #state{}} |
@@ -2808,7 +2766,7 @@ process_item_change(Item, SD, UJID) ->
maybe_send_affiliation(JID, A, SD1),
SD1
end
- catch E:R ->
+ catch ?EX_RULE(E, R, St) ->
FromSuffix = case UJID of
#jid{} ->
JidString = jid:encode(UJID),
@@ -2816,9 +2774,8 @@ process_item_change(Item, SD, UJID) ->
undefined ->
<<"">>
end,
- St = erlang:get_stacktrace(),
?ERROR_MSG("failed to set item ~p~s: ~p",
- [Item, FromSuffix, {E, {R, St}}]),
+ [Item, FromSuffix, {E, {R, ?EX_STACK(St)}}]),
{error, xmpp:err_internal_server_error()}
end.
@@ -3087,23 +3044,21 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
StateData) ->
LJID = jid:tolower(JID),
LJIDs = case LJID of
- {U, S, <<"">>} ->
- (?DICT):fold(fun (J, _, Js) ->
- case J of
- {U, S, _} -> [J | Js];
- _ -> Js
- end
- end,
- [], StateData#state.users);
- _ ->
- case (?DICT):is_key(LJID, StateData#state.users) of
- true -> [LJID];
- _ -> []
- end
+ {U, S, <<"">>} ->
+ maps:fold(fun (J, _, Js) ->
+ case J of
+ {U, S, _} -> [J | Js];
+ _ -> Js
+ end
+ end, [], StateData#state.users);
+ _ ->
+ case maps:is_key(LJID, StateData#state.users) of
+ true -> [LJID];
+ _ -> []
+ end
end,
lists:foreach(fun (J) ->
- {ok, #user{nick = Nick}} = (?DICT):find(J,
- StateData#state.users),
+ #user{nick = Nick} = maps:get(J, StateData#state.users),
add_to_log(kickban, {Nick, Reason, Code}, StateData),
tab_remove_online_user(J, StateData),
send_kickban_presence1(UJID, J, Reason, Code,
@@ -3115,12 +3070,10 @@ send_kickban_presence(UJID, JID, Reason, Code, NewAffiliation,
affiliation(), state()) -> ok.
send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
StateData) ->
- {ok, #user{jid = RealJID, nick = Nick}} =
- (?DICT):find(jid:tolower(UJID),
- StateData#state.users),
+ #user{jid = RealJID, nick = Nick} = maps:get(jid:tolower(UJID), StateData#state.users),
ActorNick = get_actor_nick(MJID, StateData),
- lists:foreach(
- fun({LJID, Info}) ->
+ maps:fold(
+ fun(LJID, Info, _) ->
IsSelfPresence = jid:tolower(UJID) == LJID,
Item0 = #muc_item{affiliation = Affiliation,
role = none},
@@ -3152,16 +3105,15 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
true ->
ok
end
- end,
- (?DICT):to_list(get_users_and_subscribers(StateData))).
+ end, ok, get_users_and_subscribers(StateData)).
-spec get_actor_nick(undefined | jid(), state()) -> binary().
get_actor_nick(undefined, _StateData) ->
<<"">>;
get_actor_nick(MJID, StateData) ->
- case (?DICT):find(jid:tolower(MJID), StateData#state.users) of
- {ok, #user{nick = ActorNick}} -> ActorNick;
- _ -> <<"">>
+ try maps:get(jid:tolower(MJID), StateData#state.users) of
+ #user{nick = ActorNick} -> ActorNick
+ catch _:{badkey, _} -> <<"">>
end.
convert_legacy_fields(Fs) ->
@@ -3416,7 +3368,7 @@ set_config(Options, StateData, Lang) ->
{_, _} -> roomconfig_change
end,
Users = [{U#user.jid, U#user.nick, U#user.role}
- || {_, U} <- (?DICT):to_list(StateData#state.users)],
+ || U <- maps:values(StateData#state.users)],
add_to_log(Type, Users, NSD),
Res
catch _:{badmatch, {error, #stanza_error{}} = Err} ->
@@ -3539,15 +3491,15 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
_ -> [104]
end,
if Codes /= [] ->
- lists:foreach(
- fun({_LJID, #user{jid = JID}}) ->
+ maps:fold(
+ fun(_LJID, #user{jid = JID}, _) ->
send_self_presence(JID, StateData#state{config = New})
- end, ?DICT:to_list(StateData#state.users)),
+ end, ok, StateData#state.users),
Message = #message{type = groupchat,
id = p1_rand:get_string(),
sub_els = [#muc_user{status_codes = Codes}]},
send_wrapped_multiple(StateData#state.jid,
- get_users_and_subscribers(StateData),
+ get_users_and_subscribers(StateData),
Message,
?NS_MUCSUB_NODES_CONFIG,
StateData);
@@ -3557,17 +3509,16 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
-spec remove_nonmembers(state()) -> state().
remove_nonmembers(StateData) ->
- lists:foldl(fun ({_LJID, #user{jid = JID}}, SD) ->
- Affiliation = get_affiliation(JID, SD),
- case Affiliation of
- none ->
- catch send_kickban_presence(undefined, JID, <<"">>,
- 322, SD),
- set_role(JID, none, SD);
- _ -> SD
- end
- end,
- StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
+ maps:fold(
+ fun(_LJID, #user{jid = JID}, SD) ->
+ Affiliation = get_affiliation(JID, SD),
+ case Affiliation of
+ none ->
+ catch send_kickban_presence(undefined, JID, <<"">>, 322, SD),
+ set_role(JID, none, SD);
+ _ -> SD
+ end
+ end, StateData, get_users_and_subscribers(StateData)).
-spec set_opts([{atom(), any()}], state()) -> state().
set_opts([], StateData) ->
@@ -3704,18 +3655,18 @@ set_opts([{Opt, Val} | Opts], StateData) ->
lists:foldl(
fun({JID, Nick, Nodes}, {SubAcc, NickAcc}) ->
BareJID = jid:remove_resource(JID),
- {?DICT:store(
- jid:tolower(BareJID),
- #subscriber{jid = BareJID,
- nick = Nick,
- nodes = Nodes},
- SubAcc),
- ?DICT:store(Nick, [jid:tolower(BareJID)], NickAcc)}
- end, {?DICT:new(), ?DICT:new()}, Val),
+ {maps:put(
+ jid:tolower(BareJID),
+ #subscriber{jid = BareJID,
+ nick = Nick,
+ nodes = Nodes},
+ SubAcc),
+ maps:put(Nick, [jid:tolower(BareJID)], NickAcc)}
+ end, {#{}, #{}}, Val),
StateData#state{subscribers = Subscribers,
subscriber_nicks = Nicks};
affiliations ->
- StateData#state{affiliations = (?DICT):from_list(Val)};
+ StateData#state{affiliations = maps:from_list(Val)};
subject ->
Subj = if Val == <<"">> -> [];
is_binary(Val) -> [#text{data = Val}];
@@ -3747,7 +3698,7 @@ set_vcard_xupdate(State) ->
-spec make_opts(state()) -> [{atom(), any()}].
make_opts(StateData) ->
Config = StateData#state.config,
- Subscribers = (?DICT):fold(
+ Subscribers = maps:fold(
fun(_LJID, Sub, Acc) ->
[{Sub#subscriber.jid,
Sub#subscriber.nick,
@@ -3782,7 +3733,7 @@ make_opts(StateData) ->
{captcha_whitelist,
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
{affiliations,
- (?DICT):to_list(StateData#state.affiliations)},
+ maps:to_list(StateData#state.affiliations)},
{subject, StateData#state.subject},
{subject_author, StateData#state.subject_author},
{subscribers, Subscribers}].
@@ -3819,8 +3770,8 @@ config_fields() ->
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
destroy_room(DEl, StateData) ->
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
- lists:foreach(
- fun({_LJID, Info}) ->
+ maps:fold(
+ fun(_LJID, Info, _) ->
Nick = Info#user.nick,
Item = #muc_item{affiliation = none,
role = none},
@@ -3831,8 +3782,7 @@ destroy_room(DEl, StateData) ->
send_wrapped(jid:replace_resource(StateData#state.jid, Nick),
Info#user.jid, Packet,
?NS_MUCSUB_NODES_CONFIG, StateData)
- end,
- (?DICT):to_list(get_users_and_subscribers(StateData))),
+ end, ok, get_users_and_subscribers(StateData)),
case (StateData#state.config)#config.persistent of
true ->
mod_muc:forget_room(StateData#state.server_host,
@@ -3853,7 +3803,7 @@ destroy_room(DEl, StateData) ->
-spec make_disco_info(jid(), state()) -> disco_info().
make_disco_info(_From, StateData) ->
Config = StateData#state.config,
- Feats = [?NS_VCARD, ?NS_MUC,
+ Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
?CONFIG_OPT_TO_FEATURE((Config#config.public),
<<"muc_public">>, <<"muc_hidden">>),
?CONFIG_OPT_TO_FEATURE((Config#config.persistent),
@@ -3935,7 +3885,7 @@ iq_disco_info_extras(Lang, StateData, Static) ->
end,
Fs3 = case Static of
false ->
- [{occupants, ?DICT:size(StateData#state.nicks)}|Fs2];
+ [{occupants, maps:size(StateData#state.nicks)}|Fs2];
true ->
Fs2
end,
@@ -4035,8 +3985,8 @@ process_iq_mucsub(From,
sub_els = [#muc_subscribe{nick = Nick}]} = Packet,
StateData) ->
LBareJID = jid:tolower(jid:remove_resource(From)),
- case (?DICT):find(LBareJID, StateData#state.subscribers) of
- {ok, #subscriber{nick = Nick1}} when Nick1 /= Nick ->
+ try maps:get(LBareJID, StateData#state.subscribers) of
+ #subscriber{nick = Nick1} when Nick1 /= Nick ->
Nodes = get_subscription_nodes(Packet),
case {nick_collision(From, Nick, StateData),
mod_muc:can_use_nick(StateData#state.server_host,
@@ -4052,11 +4002,11 @@ process_iq_mucsub(From,
NewStateData = set_subscriber(From, Nick, Nodes, StateData),
{result, subscribe_result(Packet), NewStateData}
end;
- {ok, #subscriber{}} ->
+ #subscriber{} ->
Nodes = get_subscription_nodes(Packet),
NewStateData = set_subscriber(From, Nick, Nodes, StateData),
- {result, subscribe_result(Packet), NewStateData};
- error ->
+ {result, subscribe_result(Packet), NewStateData}
+ catch _:{badkey, _} ->
SD2 = StateData#state{config = (StateData#state.config)#config{allow_subscription = true}},
add_new_user(From, Nick, Packet, SD2)
end;
@@ -4077,10 +4027,10 @@ process_iq_mucsub(From, #iq{type = set, lang = Lang,
process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]},
StateData) ->
LBareJID = jid:tolower(jid:remove_resource(From)),
- case ?DICT:find(LBareJID, StateData#state.subscribers) of
- {ok, #subscriber{nick = Nick}} ->
- Nicks = ?DICT:erase(Nick, StateData#state.subscriber_nicks),
- Subscribers = ?DICT:erase(LBareJID, StateData#state.subscribers),
+ try maps:get(LBareJID, StateData#state.subscribers) of
+ #subscriber{nick = Nick} ->
+ Nicks = maps:remove(Nick, StateData#state.subscriber_nicks),
+ Subscribers = maps:remove(LBareJID, StateData#state.subscribers),
NewStateData = StateData#state{subscribers = Subscribers,
subscriber_nicks = Nicks},
store_room(NewStateData, [{del_subscription, LBareJID}]),
@@ -4089,8 +4039,8 @@ process_iq_mucsub(From, #iq{type = set, sub_els = [#muc_unsubscribe{}]},
{stop, normal, _} -> stop;
{next_state, normal_state, SD} -> SD
end,
- {result, undefined, NewStateData2};
- _ ->
+ {result, undefined, NewStateData2}
+ catch _:{badkey, _} ->
{result, undefined, StateData}
end;
process_iq_mucsub(From, #iq{type = get, lang = Lang,
@@ -4099,7 +4049,7 @@ process_iq_mucsub(From, #iq{type = get, lang = Lang,
FAffiliation = get_affiliation(From, StateData),
FRole = get_role(From, StateData),
if FRole == moderator; FAffiliation == owner; FAffiliation == admin ->
- Subs = dict:fold(
+ Subs = maps:fold(
fun(_, #subscriber{jid = J, nodes = Nodes}, Acc) ->
[#muc_subscription{jid = J, events = Nodes}|Acc]
end, [], StateData#state.subscribers),
@@ -4114,8 +4064,8 @@ process_iq_mucsub(_From, #iq{type = get, lang = Lang}, _StateData) ->
remove_subscriptions(StateData) ->
if not (StateData#state.config)#config.allow_subscription ->
- StateData#state{subscribers = ?DICT:new(),
- subscriber_nicks = ?DICT:new()};
+ StateData#state{subscribers = #{},
+ subscriber_nicks = #{}};
true ->
StateData
end.
@@ -4166,12 +4116,12 @@ get_roomdesc_tail(StateData, Lang) ->
true -> <<"">>;
_ -> translate:translate(Lang, <<"private, ">>)
end,
- Len = (?DICT):size(StateData#state.nicks),
+ Len = maps:size(StateData#state.nicks),
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
-spec get_mucroom_disco_items(state()) -> disco_items().
get_mucroom_disco_items(StateData) ->
- Items = ?DICT:fold(
+ Items = maps:fold(
fun(Nick, _, Acc) ->
[#disco_item{jid = jid:make(StateData#state.room,
StateData#state.host,
@@ -4369,7 +4319,7 @@ store_room(StateData, ChangesHints) ->
-spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok.
send_subscriptions_change_notifications(From, Nick, Type, State) ->
- ?DICT:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
+ maps:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of
true ->
ShowJid = case (State#state.config)#config.anonymous == false orelse
@@ -4405,23 +4355,23 @@ send_subscriptions_change_notifications(From, Nick, Type, State) ->
send_wrapped(From, To, Packet, Node, State) ->
LTo = jid:tolower(To),
LBareTo = jid:tolower(jid:remove_resource(To)),
- IsOffline = case ?DICT:find(LTo, State#state.users) of
- {ok, #user{last_presence = undefined}} -> true;
+ IsOffline = case maps:get(LTo, State#state.users, error) of
+ #user{last_presence = undefined} -> true;
error -> true;
_ -> false
end,
if IsOffline ->
- case ?DICT:find(LBareTo, State#state.subscribers) of
- {ok, #subscriber{nodes = Nodes, jid = JID}} ->
- case lists:member(Node, Nodes) of
- true ->
+ try maps:get(LBareTo, State#state.subscribers) of
+ #subscriber{nodes = Nodes, jid = JID} ->
+ case lists:member(Node, Nodes) of
+ true ->
NewPacket = wrap(From, JID, Packet, Node),
ejabberd_router:route(
xmpp:set_from_to(NewPacket, State#state.jid, JID));
- false ->
- ok
- end;
- _ ->
+ false ->
+ ok
+ end
+ catch _:{badkey, _} ->
ok
end;
true ->
@@ -4461,17 +4411,12 @@ wrap(From, To, Packet, Node) ->
id = p1_rand:get_string(),
sub_els = [El]}]}}]}.
-%% -spec send_multiple(jid(), binary(), [#user{}], stanza()) -> ok.
-%% send_multiple(From, Server, Users, Packet) ->
-%% JIDs = [ User#user.jid || {_, User} <- ?DICT:to_list(Users)],
-%% ejabberd_router_multicast:route_multicast(From, Server, JIDs, Packet).
-
--spec send_wrapped_multiple(jid(), dict:dict(), stanza(), binary(), state()) -> ok.
+-spec send_wrapped_multiple(jid(), map(), stanza(), binary(), state()) -> ok.
send_wrapped_multiple(From, Users, Packet, Node, State) ->
- lists:foreach(
- fun({_, #user{jid = To}}) ->
+ maps:fold(
+ fun(_, #user{jid = To}, _) ->
send_wrapped(From, To, Packet, Node, State)
- end, ?DICT:to_list(Users)).
+ end, ok, Users).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Detect messange stanzas that don't have meaninful content
diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl
index e92b4bc54..12487e628 100644
--- a/src/mod_muc_sql.erl
+++ b/src/mod_muc_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index 72c177b80..509fe8893 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -5,7 +5,7 @@
%%% Created : 29 May 2007 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 53e437020..2582a7c98 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -101,6 +101,8 @@
-callback remove_all_messages(binary(), binary()) -> {atomic, any()}.
-callback count_messages(binary(), binary()) -> non_neg_integer().
+-optional_callbacks([remove_expired_messages/1, remove_old_messages/2]).
+
depends(_Host, _Opts) ->
[].
@@ -365,7 +367,6 @@ remove_msg_by_node(To, Seq) ->
-spec need_to_store(binary(), message()) -> boolean().
need_to_store(_LServer, #message{type = error}) -> false;
-need_to_store(_LServer, #message{type = groupchat}) -> false;
need_to_store(LServer, #message{type = Type} = Packet) ->
case xmpp:has_subtag(Packet, #offline{}) of
false ->
@@ -374,16 +375,25 @@ need_to_store(LServer, #message{type = Type} = Packet) ->
true;
no_store ->
false;
- none when Type == headline ->
- false;
none ->
- case gen_mod:get_module_opt(
- LServer, ?MODULE, store_empty_body) of
- true ->
+ Store = case Type of
+ groupchat ->
+ gen_mod:get_module_opt(
+ LServer, ?MODULE, store_groupchat);
+ headline ->
+ false;
+ _ ->
+ true
+ end,
+ case {Store, gen_mod:get_module_opt(
+ LServer, ?MODULE, store_empty_body)} of
+ {false, _} ->
+ false;
+ {_, true} ->
true;
- false ->
+ {_, false} ->
Packet#message.body /= [];
- unless_chat_state ->
+ {_, unless_chat_state} ->
not misc:is_standalone_chat_state(Packet)
end
end;
@@ -543,12 +553,18 @@ privacy_check_packet(#{lserver := LServer} = State, Pkt, Dir) ->
remove_expired_messages(Server) ->
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:remove_expired_messages(LServer).
+ case erlang:function_exported(Mod, remove_expired_messages, 1) of
+ true -> Mod:remove_expired_messages(LServer);
+ false -> erlang:error(not_implemented)
+ end.
remove_old_messages(Days, Server) ->
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:remove_old_messages(Days, LServer).
+ case erlang:function_exported(Mod, remove_old_messages, 2) of
+ true -> Mod:remove_old_messages(Days, LServer);
+ false -> erlang:error(not_implemented)
+ end.
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
@@ -837,6 +853,8 @@ import(LServer, {sql, _}, DBType, <<"spool">>,
mod_opt_type(access_max_user_messages) ->
fun acl:shaper_rules_validator/1;
mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+mod_opt_type(store_groupchat) ->
+ fun(V) when is_boolean(V) -> V end;
mod_opt_type(store_empty_body) ->
fun (V) when is_boolean(V) -> V;
(unless_chat_state) -> unless_chat_state
@@ -845,4 +863,5 @@ mod_opt_type(store_empty_body) ->
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{access_max_user_messages, max_user_offline_messages},
- {store_empty_body, unless_chat_state}].
+ {store_empty_body, unless_chat_state},
+ {store_groupchat, false}].
diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl
index a7f0cd02f..77c5b2e44 100644
--- a/src/mod_offline_mnesia.erl
+++ b/src/mod_offline_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_offline_riak.erl b/src/mod_offline_riak.erl
index 2c03df366..db86767ce 100644
--- a/src/mod_offline_riak.erl
+++ b/src/mod_offline_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl
index c114b1dce..cb0efa51e 100644
--- a/src/mod_offline_sql.erl
+++ b/src/mod_offline_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -90,8 +90,17 @@ remove_expired_messages(_LServer) ->
remove_old_messages(Days, LServer) ->
case ejabberd_sql:sql_query(
LServer,
- ?SQL("DELETE FROM spool"
- " WHERE created_at < NOW() - INTERVAL %(Days)d DAY")) of
+ fun(pgsql, _) ->
+ ejabberd_sql:sql_query_t(
+ ?SQL("DELETE FROM spool"
+ " WHERE created_at <"
+ " NOW() - INTERVAL '%(Days)d DAY'"));
+ (_, _) ->
+ ejabberd_sql:sql_query_t(
+ ?SQL("DELETE FROM spool"
+ " WHERE created_at < NOW() - INTERVAL %(Days)d DAY"))
+ end)
+ of
{updated, N} ->
?INFO_MSG("~p message(s) deleted from offline spool", [N]);
_Error ->
diff --git a/src/mod_ping.erl b/src/mod_ping.erl
index e0b4a36a9..ffdee2f01 100644
--- a/src/mod_ping.erl
+++ b/src/mod_ping.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jul 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl
index 6c58d2641..c1473e344 100644
--- a/src/mod_pres_counter.erl
+++ b/src/mod_pres_counter.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Sep 2010 by Ahmed Omar
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 6cb573439..0b534d272 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Jul 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl
index a28910c0f..1be9912fb 100644
--- a/src/mod_privacy_mnesia.erl
+++ b/src/mod_privacy_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privacy_riak.erl b/src/mod_privacy_riak.erl
index 71d744c8a..51caaafe7 100644
--- a/src/mod_privacy_riak.erl
+++ b/src/mod_privacy_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl
index 4ee0984a8..e5a97c96b 100644
--- a/src/mod_privacy_sql.erl
+++ b/src/mod_privacy_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_private.erl b/src/mod_private.erl
index f6a57c63c..f69f2cc96 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -28,17 +28,21 @@
-author('alexey@process-one.net').
-protocol({xep, 49, '1.2'}).
+-protocol({xep, 411, '0.2.0'}).
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, process_sm_iq/1, import_info/0,
remove_user/2, get_data/2, get_data/3, export/1,
- import/5, import_start/2, mod_opt_type/1, set_data/3,
- mod_options/1, depends/2]).
+ import/5, import_start/2, mod_opt_type/1, set_data/2,
+ mod_options/1, depends/2, get_sm_features/5, pubsub_publish_item/6]).
+
+-export([get_commands_spec/0, bookmarks_to_pep/2]).
-include("logger.hrl").
-include("xmpp.hrl").
-include("mod_private.hrl").
+-include("ejabberd_commands.hrl").
-define(PRIVATE_CACHE, private_cache).
@@ -57,16 +61,23 @@ start(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
- ejabberd_hooks:add(remove_user, Host, ?MODULE,
- remove_user, 50),
- gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
- ?NS_PRIVATE, ?MODULE, process_sm_iq).
+ ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+ ejabberd_hooks:add(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50),
+ gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE, ?MODULE, process_sm_iq),
+ ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
- ejabberd_hooks:delete(remove_user, Host, ?MODULE,
- remove_user, 50),
- gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
- ?NS_PRIVATE).
+ ejabberd_hooks:delete(remove_user, Host, ?MODULE, remove_user, 50),
+ ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 50),
+ ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE, pubsub_publish_item, 50),
+ gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PRIVATE),
+ case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
+ false ->
+ ejabberd_commands:unregister_commands(get_commands_spec());
+ true ->
+ ok
+ end.
reload(Host, NewOpts, OldOpts) ->
NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
@@ -78,9 +89,46 @@ reload(Host, NewOpts, OldOpts) ->
end,
init_cache(NewMod, Host, NewOpts).
+depends(_Host, _Opts) ->
+ [{mod_pubsub, soft}].
+
+mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+mod_opt_type(O) when O == cache_life_time; O == cache_size ->
+ fun (I) when is_integer(I), I > 0 -> I;
+ (infinity) -> infinity
+ end;
+mod_opt_type(O) when O == use_cache; O == cache_missed ->
+ fun (B) when is_boolean(B) -> B end.
+
+mod_options(Host) ->
+ [{db_type, ejabberd_config:default_db(Host, ?MODULE)},
+ {use_cache, ejabberd_config:use_cache(Host)},
+ {cache_size, ejabberd_config:cache_size(Host)},
+ {cache_missed, ejabberd_config:cache_missed(Host)},
+ {cache_life_time, ejabberd_config:cache_life_time(Host)}].
+
+-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
+ jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | empty | {result, [binary()]}.
+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+ Acc;
+get_sm_features(Acc, _From, To, <<"">>, _Lang) ->
+ case gen_mod:is_loaded(To#jid.lserver, mod_pubsub) of
+ true ->
+ {result, [?NS_BOOKMARKS_CONVERSION_0 |
+ case Acc of
+ {result, Features} -> Features;
+ empty -> []
+ end]};
+ false ->
+ Acc
+ end;
+get_sm_features(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
-spec process_sm_iq(iq()) -> iq().
process_sm_iq(#iq{type = Type, lang = Lang,
- from = #jid{luser = LUser, lserver = LServer},
+ from = #jid{luser = LUser, lserver = LServer} = From,
to = #jid{luser = LUser, lserver = LServer},
sub_els = [#private{sub_els = Els0}]} = IQ) ->
case filter_xmlels(Els0) of
@@ -88,9 +136,11 @@ process_sm_iq(#iq{type = Type, lang = Lang,
Txt = <<"No private data found in this query">>,
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
Data when Type == set ->
- case set_data(LUser, LServer, Data) of
+ case set_data(From, Data) of
ok ->
xmpp:make_iq_result(IQ);
+ {error, #stanza_error{} = Err} ->
+ xmpp:make_error(IQ, Err);
{error, _} ->
Txt = <<"Database failure">>,
Err = xmpp:err_internal_server_error(Txt, Lang),
@@ -120,12 +170,21 @@ filter_xmlels(Els) ->
end
end, Els).
--spec set_data(binary(), binary(), [{binary(), xmlel()}]) -> ok | {error, _}.
-set_data(LUser, LServer, Data) ->
+-spec set_data(jid(), [{binary(), xmlel()}]) -> ok | {error, _}.
+set_data(JID, Data) ->
+ set_data(JID, Data, true).
+
+-spec set_data(jid(), [{binary(), xmlel()}], boolean()) -> ok | {error, _}.
+set_data(JID, Data, Publish) ->
+ {LUser, LServer, _} = jid:tolower(JID),
Mod = gen_mod:db_mod(LServer, ?MODULE),
case Mod:set_data(LUser, LServer, Data) of
ok ->
- delete_cache(Mod, LUser, LServer, Data);
+ delete_cache(Mod, LUser, LServer, Data),
+ case Publish of
+ true -> publish_data(JID, Data);
+ false -> ok
+ end;
{error, _} = Err ->
Err
end.
@@ -181,6 +240,87 @@ remove_user(User, Server) ->
Mod:del_data(LUser, LServer),
delete_cache(Mod, LUser, LServer, Data).
+%%%===================================================================
+%%% Pubsub
+%%%===================================================================
+-spec publish_data(jid(), [{binary(), xmlel()}]) -> ok | {error, stanza_error()}.
+publish_data(JID, Data) ->
+ {_, LServer, _} = LBJID = jid:remove_resource(jid:tolower(JID)),
+ case gen_mod:is_loaded(LServer, mod_pubsub) of
+ true ->
+ case lists:keyfind(?NS_STORAGE_BOOKMARKS, 1, Data) of
+ false -> ok;
+ {_, El} ->
+ PubOpts = [{persist_items, true},
+ {access_model, whitelist}],
+ case mod_pubsub:publish_item(
+ LBJID, LServer, ?NS_STORAGE_BOOKMARKS, JID,
+ <<"current">>, [El], PubOpts, all) of
+ {result, _} -> ok;
+ {error, _} = Err -> Err
+ end
+ end;
+ false ->
+ ok
+ end.
+
+-spec pubsub_publish_item(binary(), binary(), jid(), jid(),
+ binary(), [xmlel()]) -> any().
+pubsub_publish_item(LServer, ?NS_STORAGE_BOOKMARKS,
+ #jid{luser = LUser, lserver = LServer} = From,
+ #jid{luser = LUser, lserver = LServer},
+ _ItemId, [Payload|_]) ->
+ set_data(From, [{?NS_STORAGE_BOOKMARKS, Payload}], false);
+pubsub_publish_item(_, _, _, _, _, _) ->
+ ok.
+
+%%%===================================================================
+%%% Commands
+%%%===================================================================
+-spec get_commands_spec() -> [ejabberd_commands()].
+get_commands_spec() ->
+ [#ejabberd_commands{name = bookmarks_to_pep, tags = [private],
+ desc = "Export private XML storage bookmarks to PEP",
+ module = ?MODULE, function = bookmarks_to_pep,
+ args = [{user, binary}, {server, binary}],
+ args_desc = ["Username", "Server"],
+ args_example = [<<"bob">>, <<"example.com">>],
+ result = {res, restuple},
+ result_desc = "Result tuple",
+ result_example = {ok, <<"Bookmarks exported">>}}].
+
+-spec bookmarks_to_pep(binary(), binary())
+ -> {ok, binary()} | {error, binary()}.
+bookmarks_to_pep(User, Server) ->
+ LUser = jid:nodeprep(User),
+ LServer = jid:nameprep(Server),
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Res = case use_cache(Mod, LServer) of
+ true ->
+ ets_cache:lookup(
+ ?PRIVATE_CACHE, {LUser, LServer, ?NS_STORAGE_BOOKMARKS},
+ fun() ->
+ Mod:get_data(LUser, LServer, ?NS_STORAGE_BOOKMARKS)
+ end);
+ false ->
+ Mod:get_data(LUser, LServer, ?NS_STORAGE_BOOKMARKS)
+ end,
+ case Res of
+ {ok, El} ->
+ Data = [{?NS_STORAGE_BOOKMARKS, El}],
+ case publish_data(jid:make(User, Server), Data) of
+ ok ->
+ {ok, <<"Bookmarks exported to PEP node">>};
+ {error, Err} ->
+ {error, xmpp:format_stanza_error(Err)}
+ end;
+ _ ->
+ {error, <<"Cannot retrieve bookmarks from private XML storage">>}
+ end.
+
+%%%===================================================================
+%%% Cache
+%%%===================================================================
-spec delete_cache(module(), binary(), binary(), [{binary(), xmlel()}]) -> ok.
delete_cache(Mod, LUser, LServer, Data) ->
case use_cache(Mod, LServer) of
@@ -230,6 +370,9 @@ cache_nodes(Mod, Host) ->
false -> ejabberd_cluster:get_nodes()
end.
+%%%===================================================================
+%%% Import/Export
+%%%===================================================================
import_info() ->
[{<<"private_storage">>, 4}].
@@ -244,21 +387,3 @@ export(LServer) ->
import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, L).
-
-depends(_Host, _Opts) ->
- [].
-
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(O) when O == cache_life_time; O == cache_size ->
- fun (I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
-mod_opt_type(O) when O == use_cache; O == cache_missed ->
- fun (B) when is_boolean(B) -> B end.
-
-mod_options(Host) ->
- [{db_type, ejabberd_config:default_db(Host, ?MODULE)},
- {use_cache, ejabberd_config:use_cache(Host)},
- {cache_size, ejabberd_config:cache_size(Host)},
- {cache_missed, ejabberd_config:cache_missed(Host)},
- {cache_life_time, ejabberd_config:cache_life_time(Host)}].
diff --git a/src/mod_private_mnesia.erl b/src/mod_private_mnesia.erl
index b981a803a..03d98b1ca 100644
--- a/src/mod_private_mnesia.erl
+++ b/src/mod_private_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_private_riak.erl b/src/mod_private_riak.erl
index 6ddf9cc10..c0a225a14 100644
--- a/src/mod_private_riak.erl
+++ b/src/mod_private_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl
index a0ec03c6a..1fa91a9d4 100644
--- a/src/mod_private_sql.erl
+++ b/src/mod_private_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl
index 5f2fa3297..abb38456a 100644
--- a/src/mod_privilege.erl
+++ b/src/mod_privilege.erl
@@ -4,7 +4,7 @@
%%% Purpose : XEP-0356: Privileged Entity
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl
index c911dd7aa..0fca1cdcb 100644
--- a/src/mod_proxy65.erl
+++ b/src/mod_proxy65.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_lib.erl b/src/mod_proxy65_lib.erl
index 23e1108ad..70a762413 100644
--- a/src/mod_proxy65_lib.erl
+++ b/src/mod_proxy65_lib.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_mnesia.erl b/src/mod_proxy65_mnesia.erl
index 7423697c1..89dd24800 100644
--- a/src/mod_proxy65_mnesia.erl
+++ b/src/mod_proxy65_mnesia.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Jan 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_redis.erl b/src/mod_proxy65_redis.erl
index 99eda70bd..a3f7bb5a7 100644
--- a/src/mod_proxy65_redis.erl
+++ b/src/mod_proxy65_redis.erl
@@ -3,7 +3,7 @@
%%% Created : 31 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_riak.erl b/src/mod_proxy65_riak.erl
index 543015654..ec1015772 100644
--- a/src/mod_proxy65_riak.erl
+++ b/src/mod_proxy65_riak.erl
@@ -3,7 +3,7 @@
%%% Created : 15 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl
index 9510ff4b2..433371240 100644
--- a/src/mod_proxy65_service.erl
+++ b/src/mod_proxy65_service.erl
@@ -5,7 +5,7 @@
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl
index 0f51adb55..20fed2209 100644
--- a/src/mod_proxy65_sql.erl
+++ b/src/mod_proxy65_sql.erl
@@ -3,7 +3,7 @@
%%% Created : 30 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl
index 668817868..7e5c180ae 100644
--- a/src/mod_proxy65_stream.erl
+++ b/src/mod_proxy65_stream.erl
@@ -4,7 +4,7 @@
%%% Purpose : Bytestream process.
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 2609beb86..b332dd321 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -65,7 +65,7 @@
%% exports for console debug manual use
-export([create_node/5, create_node/7, delete_node/3,
- subscribe_node/5, unsubscribe_node/5, publish_item/6,
+ subscribe_node/5, unsubscribe_node/5, publish_item/6, publish_item/8,
delete_item/4, delete_item/5, send_items/7, get_items/2, get_item/3,
get_cached_item/2, get_configure/5, set_configure/5,
tree_action/3, node_action/4, node_call/4]).
@@ -2990,6 +2990,7 @@ send_last_pep(From, To) ->
Host = host(ServerHost),
Publisher = jid:tolower(From),
Owner = jid:remove_resource(Publisher),
+ RecipientIsOwner = jid:remove_resource(jid:tolower(To)) == Owner,
lists:foreach(
fun(#pubsub_node{nodeid = {_, Node}, type = Type, id = Nidx, options = Options}) ->
case match_option(Options, send_last_published_item, on_sub_and_presence) of
@@ -2998,8 +2999,11 @@ send_last_pep(From, To) ->
Subscribed = case get_option(Options, access_model) of
open -> true;
presence -> true;
- whitelist -> false; % subscribers are added manually
- authorize -> false; % likewise
+ %% TODO: Fix the 'whitelist'/'authorize'
+ %% cases. Currently, only node owners
+ %% receive last PEP notifications.
+ whitelist -> RecipientIsOwner;
+ authorize -> RecipientIsOwner;
roster ->
Grps = get_option(Options, roster_groups_allowed, []),
{OU, OS, _} = Owner,
diff --git a/src/mod_push.erl b/src/mod_push.erl
index cadce92b0..2aa699597 100644
--- a/src/mod_push.erl
+++ b/src/mod_push.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl
index 779fd0006..a7a7d8c92 100644
--- a/src/mod_push_keepalive.erl
+++ b/src/mod_push_keepalive.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_push_mnesia.erl b/src/mod_push_mnesia.erl
index 3b7f9aae4..ee3891e07 100644
--- a/src/mod_push_mnesia.erl
+++ b/src/mod_push_mnesia.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jul 2017 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl
index 5879e163a..50ad30684 100644
--- a/src/mod_push_sql.erl
+++ b/src/mod_push_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Oct 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2017-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2017-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_register.erl b/src/mod_register.erl
index dbbddbd5d..841053a43 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -5,7 +5,7 @@
%%% Created : 8 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -73,8 +73,14 @@ depends(_Host, _Opts) ->
[].
-spec stream_feature_register([xmpp_element()], binary()) -> [xmpp_element()].
-stream_feature_register(Acc, _Host) ->
- [#feature_register{}|Acc].
+stream_feature_register(Acc, Host) ->
+ case {gen_mod:get_module_opt(Host, ?MODULE, access),
+ gen_mod:get_module_opt(Host, ?MODULE, ip_access),
+ gen_mod:get_module_opt(Host, ?MODULE, redirect_url)} of
+ {none, _, <<>>} -> Acc;
+ {_, none, <<>>} -> Acc;
+ {_, _, _} -> [#feature_register{}|Acc]
+ end.
c2s_unauthenticated_packet(#{ip := IP, server := Server} = State,
#iq{type = T, sub_els = [_]} = IQ)
diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl
index ae659ddc8..1e188d333 100644
--- a/src/mod_register_web.erl
+++ b/src/mod_register_web.erl
@@ -5,7 +5,7 @@
%%% Created : 4 May 2008 by Badlop <badlop@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 1f42b69e0..652eb20a9 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Dec 2002 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -53,14 +53,11 @@
depends/2]).
-include("logger.hrl").
-
-include("xmpp.hrl").
-
-include("mod_roster.hrl").
-
-include("ejabberd_http.hrl").
-
-include("ejabberd_web_admin.hrl").
+-include("ejabberd_stacktrace.hrl").
-define(ROSTER_CACHE, roster_cache).
-define(ROSTER_ITEM_CACHE, roster_item_cache).
@@ -231,7 +228,7 @@ roster_version(LServer, LUser) ->
case roster_version_on_db(LServer) of
true ->
case read_roster_version(LUser, LServer) of
- error -> not_found;
+ error -> undefined;
{ok, V} -> V
end;
false ->
@@ -320,10 +317,9 @@ process_iq_get(#iq{to = To, lang = Lang,
#roster_query{items = Items,
ver = Version}
end)
- catch E:R ->
- St = erlang:get_stacktrace(),
+ catch ?EX_RULE(E, R, St) ->
?ERROR_MSG("failed to process roster get for ~s: ~p",
- [jid:encode(To), {E, {R, St}}]),
+ [jid:encode(To), {E, {R, ?EX_STACK(St)}}]),
Txt = <<"Roster module has failed">>,
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
index 01e671b43..c67d7d5e4 100644
--- a/src/mod_roster_mnesia.erl
+++ b/src/mod_roster_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_roster_riak.erl b/src/mod_roster_riak.erl
index 1f65d384c..d267856c3 100644
--- a/src/mod_roster_riak.erl
+++ b/src/mod_roster_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl
index 85019e21d..91242f610 100644
--- a/src/mod_roster_sql.erl
+++ b/src/mod_roster_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl
index ff4174ebf..55854a82b 100644
--- a/src/mod_s2s_dialback.erl
+++ b/src/mod_s2s_dialback.erl
@@ -2,7 +2,7 @@
%%% Created : 16 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl
index d8c9c565e..62b5e289f 100644
--- a/src/mod_service_log.erl
+++ b/src/mod_service_log.erl
@@ -5,7 +5,7 @@
%%% Created : 24 Aug 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index 79b782e17..ee1aa8cf0 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -303,10 +303,10 @@ get_jid_info({Subscription, Ask, Groups}, User, Server,
US1 = {U1, S1},
DisplayedGroups = get_user_displayed_groups(US),
SRUsers = lists:foldl(fun (Group, Acc1) ->
+ GroupName = get_group_name(LServer, Group),
lists:foldl(fun (User1, Acc2) ->
dict:append(User1,
- get_group_name(LServer,
- Group),
+ GroupName,
Acc2)
end,
Acc1,
@@ -451,18 +451,24 @@ get_group_name(Host1, Group1) ->
%% Get list of names of groups that have @all@/@online@/etc in the memberlist
get_special_users_groups(Host) ->
- lists:filter(fun (Group) ->
- get_group_opt(Host, Group, all_users, false) orelse
- get_group_opt(Host, Group, online_users, false)
- end,
- list_groups(Host)).
+ lists:filtermap(fun ({Group, Opts}) ->
+ case proplists:get_value(all_users, Opts, false) orelse
+ proplists:get_value(online_users, Opts, false) of
+ true -> {true, Group};
+ false -> false
+ end
+ end,
+ groups_with_opts(Host)).
%% Get list of names of groups that have @online@ in the memberlist
get_special_users_groups_online(Host) ->
- lists:filter(fun (Group) ->
- get_group_opt(Host, Group, online_users, false)
- end,
- list_groups(Host)).
+ lists:filtermap(fun ({Group, Opts}) ->
+ case proplists:get_value(online_users, Opts, false) of
+ true -> {true, Group};
+ false -> false
+ end
+ end,
+ groups_with_opts(Host)).
%% Given two lists of groupnames and their options,
%% return the list of displayed groups to the second list
diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl
index b50ad93d0..327ec0a9d 100644
--- a/src/mod_shared_roster_ldap.erl
+++ b/src/mod_shared_roster_ldap.erl
@@ -7,7 +7,7 @@
%%% Created : 5 Mar 2005 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl
index 2995c2230..a54b9687f 100644
--- a/src/mod_shared_roster_mnesia.erl
+++ b/src/mod_shared_roster_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster_riak.erl b/src/mod_shared_roster_riak.erl
index 94ed737ec..87bdd80c1 100644
--- a/src/mod_shared_roster_riak.erl
+++ b/src/mod_shared_roster_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl
index 00714fca9..39ca9fb0d 100644
--- a/src/mod_shared_roster_sql.erl
+++ b/src/mod_shared_roster_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sic.erl b/src/mod_sic.erl
index 765b9adcd..3ca8e6da9 100644
--- a/src/mod_sic.erl
+++ b/src/mod_sic.erl
@@ -5,7 +5,7 @@
%%% Created : 6 Mar 2010 by Karim Gemayel <karim.gemayel@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sip.erl b/src/mod_sip.erl
index 814b9df6f..3cb2ac13e 100644
--- a/src/mod_sip.erl
+++ b/src/mod_sip.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl
index 3ee0a6fee..5efc238e0 100644
--- a/src/mod_sip_proxy.erl
+++ b/src/mod_sip_proxy.erl
@@ -5,7 +5,7 @@
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl
index dfb9a50f0..787ec0cb8 100644
--- a/src/mod_sip_registrar.erl
+++ b/src/mod_sip_registrar.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2014-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2014-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index 706c22a70..772d51098 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -5,7 +5,7 @@
%%% Created : 11 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl
index 1927afa95..4c38b87af 100644
--- a/src/mod_stream_mgmt.erl
+++ b/src/mod_stream_mgmt.erl
@@ -3,7 +3,7 @@
%%% Created : 25 Dec 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -40,6 +40,8 @@
-include("logger.hrl").
-include("p1_queue.hrl").
+-define(STREAM_MGMT_CACHE, stream_mgmt_cache).
+
-define(is_sm_packet(Pkt),
is_record(Pkt, sm_enable) or
is_record(Pkt, sm_resume) or
@@ -51,7 +53,8 @@
%%%===================================================================
%%% API
%%%===================================================================
-start(Host, _Opts) ->
+start(Host, Opts) ->
+ init_cache(Opts),
ejabberd_hooks:add(c2s_init, ?MODULE, c2s_stream_init, 50),
ejabberd_hooks:add(c2s_stream_started, Host, ?MODULE,
c2s_stream_started, 50),
@@ -94,7 +97,8 @@ stop(Host) ->
ejabberd_hooks:delete(c2s_closed, Host, ?MODULE, c2s_closed, 50),
ejabberd_hooks:delete(c2s_terminated, Host, ?MODULE, c2s_terminated, 50).
-reload(_Host, _NewOpts, _OldOpts) ->
+reload(_Host, NewOpts, _OldOpts) ->
+ init_cache(NewOpts),
?WARNING_MSG("module ~s is reloaded, but new configuration will take "
"effect for newly created client connections only", [?MODULE]).
@@ -284,23 +288,16 @@ c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) ->
[jid:encode(JID)]),
bounce_message_queue(),
{stop, State};
-c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID,
- user := U, server := S, resource := R} = State, Reason) ->
- Result = case MgmtState of
- timeout ->
- Info = [{num_stanzas_in, In}],
- %% TODO: Usually, ejabberd_c2s:process_terminated/2 is
- %% called later in the hook chain. We swap the order so
- %% that the offline info won't be purged after we stored
- %% it. This should be fixed in a proper way.
- State1 = ejabberd_c2s:process_terminated(State, Reason),
- ejabberd_sm:set_offline_info(SID, U, S, R, Info),
- {stop, State1};
- _ ->
- State
- end,
+c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In,
+ sid := {Time, _}, jid := JID} = State, _Reason) ->
+ case MgmtState of
+ timeout ->
+ store_stanzas_in(jid:tolower(JID), Time, In);
+ _ ->
+ ok
+ end,
route_unacked_stanzas(State),
- Result;
+ State;
c2s_terminated(State, _Reason) ->
State.
@@ -641,16 +638,11 @@ inherit_session_state(#{user := U, server := S,
{term, {R, Time}} ->
case ejabberd_sm:get_session_pid(U, S, R) of
none ->
- case ejabberd_sm:get_offline_info(Time, U, S, R) of
- none ->
+ case pop_stanzas_in({U, S, R}, Time) of
+ error ->
{error, <<"Previous session PID not found">>};
- Info ->
- case proplists:get_value(num_stanzas_in, Info) of
- undefined ->
- {error, <<"Previous session timed out">>};
- H ->
- {error, <<"Previous session timed out">>, H}
- end
+ {ok, H} ->
+ {error, <<"Previous session timed out">>, H}
end;
OldPID ->
OldSID = {Time, OldPID},
@@ -751,6 +743,32 @@ need_to_enqueue(State, _) ->
{false, State}.
%%%===================================================================
+%%% Cache-like storage for last handled stanzas
+%%%===================================================================
+init_cache(Opts) ->
+ ets_cache:new(?STREAM_MGMT_CACHE, cache_opts(Opts)).
+
+cache_opts(Opts) ->
+ [{max_size, gen_mod:get_opt(cache_size, Opts)},
+ {life_time, infinity}].
+
+-spec store_stanzas_in(ljid(), erlang:timestamp(), non_neg_integer()) -> boolean().
+store_stanzas_in(LJID, Time, Num) ->
+ ets_cache:insert(?STREAM_MGMT_CACHE, {LJID, Time}, Num,
+ ejabberd_cluster:get_nodes()).
+
+-spec pop_stanzas_in(ljid(), erlang:timestamp()) -> {ok, non_neg_integer()} | error.
+pop_stanzas_in(LJID, Time) ->
+ case ets_cache:lookup(?STREAM_MGMT_CACHE, {LJID, Time}) of
+ {ok, Val} ->
+ ets_cache:delete(?STREAM_MGMT_CACHE, {LJID, Time},
+ ejabberd_cluster:get_nodes()),
+ {ok, Val};
+ error ->
+ error
+ end.
+
+%%%===================================================================
%%% Configuration processing
%%%===================================================================
get_max_ack_queue(Host) ->
@@ -796,6 +814,11 @@ mod_opt_type(resend_on_timeout) ->
fun(B) when is_boolean(B) -> B;
(if_offline) -> if_offline
end;
+mod_opt_type(cache_size) ->
+ fun(I) when is_integer(I), I>0 -> I;
+ (unlimited) -> infinity;
+ (infinity) -> infinity
+ end;
mod_opt_type(queue_type) ->
fun(ram) -> ram; (file) -> file end.
@@ -804,5 +827,6 @@ mod_options(Host) ->
{resume_timeout, 300},
{max_resume_timeout, undefined},
{ack_timeout, 60},
+ {cache_size, ejabberd_config:cache_size(Host)},
{resend_on_timeout, false},
{queue_type, ejabberd_config:default_queue_type(Host)}].
diff --git a/src/mod_time.erl b/src/mod_time.erl
index 78c75f8af..6c4a0bae5 100644
--- a/src/mod_time.erl
+++ b/src/mod_time.erl
@@ -6,7 +6,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index 39da3472e..5caecae50 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -5,7 +5,7 @@
%%% Created : 2 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index acfd0a052..2d00d4465 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -4,7 +4,7 @@
%%% Created : 29 Jul 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl
index af361dbb3..31e9f6d43 100644
--- a/src/mod_vcard_mnesia.erl
+++ b/src/mod_vcard_mnesia.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_riak.erl b/src/mod_vcard_riak.erl
index 49a303c88..ec43cc8d1 100644
--- a/src/mod_vcard_riak.erl
+++ b/src/mod_vcard_riak.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index 57d2052a0..93ef2e948 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index 2f3853357..a674598b8 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -5,7 +5,7 @@
%%% Created : 9 Mar 2007 by Igor Goryachev <igor@goryachev.org>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/mod_version.erl b/src/mod_version.erl
index da0f736eb..7c1f28aea 100644
--- a/src/mod_version.erl
+++ b/src/mod_version.erl
@@ -5,7 +5,7 @@
%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_buddy.erl b/src/node_buddy.erl
index 485675253..a975cb3eb 100644
--- a/src/node_buddy.erl
+++ b/src/node_buddy.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_club.erl b/src/node_club.erl
index 8aa8380a2..953aca117 100644
--- a/src/node_club.erl
+++ b/src/node_club.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_dag.erl b/src/node_dag.erl
index 9d84c484e..d1d8ccd8e 100644
--- a/src/node_dag.erl
+++ b/src/node_dag.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jun 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_dispatch.erl b/src/node_dispatch.erl
index 1cc96c22a..d0c1b6165 100644
--- a/src/node_dispatch.erl
+++ b/src/node_dispatch.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_flat.erl b/src/node_flat.erl
index 11e0785c0..2c0d7869a 100644
--- a/src/node_flat.erl
+++ b/src/node_flat.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl
index ebe164a15..cdf7fe3a9 100644
--- a/src/node_flat_sql.erl
+++ b/src/node_flat_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_hometree.erl b/src/node_hometree.erl
index 1e4bc0cf7..c55c696f4 100644
--- a/src/node_hometree.erl
+++ b/src/node_hometree.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_hometree_sql.erl b/src/node_hometree_sql.erl
index c2776e126..8e0a8f281 100644
--- a/src/node_hometree_sql.erl
+++ b/src/node_hometree_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_mb.erl b/src/node_mb.erl
index 615ccadec..9042f27cf 100644
--- a/src/node_mb.erl
+++ b/src/node_mb.erl
@@ -5,7 +5,7 @@
%%% Created : 25 Sep 2008 by Eric Cestari <ecestari@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_mb_sql.erl b/src/node_mb_sql.erl
index 78122b72d..bc06be24d 100644
--- a/src/node_mb_sql.erl
+++ b/src/node_mb_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 6 Sep 2016 by Holger Weiss <holger@zedat.fu-berlin.de>
%%%
%%%
-%%% ejabberd, Copyright (C) 2016-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2016-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_mix.erl b/src/node_mix.erl
index 0cd6f84e9..4d2741a5e 100644
--- a/src/node_mix.erl
+++ b/src/node_mix.erl
@@ -4,7 +4,7 @@
%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_mix_sql.erl b/src/node_mix_sql.erl
index 4ac01c62e..961d34da8 100644
--- a/src/node_mix_sql.erl
+++ b/src/node_mix_sql.erl
@@ -4,7 +4,7 @@
%%% Created : 8 Mar 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_online.erl b/src/node_online.erl
index a9a39be3b..0bdcb60c3 100644
--- a/src/node_online.erl
+++ b/src/node_online.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Dec 2015 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_pep.erl b/src/node_pep.erl
index 9d5f21285..3baafc10d 100644
--- a/src/node_pep.erl
+++ b/src/node_pep.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -118,8 +118,7 @@ create_node(Nidx, Owner) ->
node_flat:create_node(Nidx, Owner).
delete_node(Nodes) ->
- {result, {_, _, Result}} = node_flat:delete_node(Nodes),
- {result, {default, Result}}.
+ node_flat:delete_node(Nodes).
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 bf989f173..4e6dd4872 100644
--- a/src/node_pep_sql.erl
+++ b/src/node_pep_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -74,8 +74,7 @@ create_node(Nidx, Owner) ->
{result, {default, broadcast}}.
delete_node(Nodes) ->
- {result, {_, _, Result}} = node_flat_sql:delete_node(Nodes),
- {result, {default, Result}}.
+ node_flat_sql:delete_node(Nodes).
subscribe_node(Nidx, Sender, Subscriber, AccessModel,
SendLast, PresenceSubscription, RosterGroup, Options) ->
diff --git a/src/node_private.erl b/src/node_private.erl
index 22f966dbd..d98669372 100644
--- a/src/node_private.erl
+++ b/src/node_private.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/node_public.erl b/src/node_public.erl
index 63468dbf3..28dafa791 100644
--- a/src/node_public.erl
+++ b/src/node_public.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/nodetree_dag.erl b/src/nodetree_dag.erl
index 93f414f31..1185ed817 100644
--- a/src/nodetree_dag.erl
+++ b/src/nodetree_dag.erl
@@ -5,7 +5,7 @@
%%% Created : 15 Jun 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl
index c0da117b0..084fa322a 100644
--- a/src/nodetree_tree.erl
+++ b/src/nodetree_tree.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl
index c311ea3ab..311bbbf07 100644
--- a/src/nodetree_tree_sql.erl
+++ b/src/nodetree_tree_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/nodetree_virtual.erl b/src/nodetree_virtual.erl
index 4d0485eff..c27efe44b 100644
--- a/src/nodetree_virtual.erl
+++ b/src/nodetree_virtual.erl
@@ -5,7 +5,7 @@
%%% Created : 1 Dec 2007 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index b95faad51..3fc3cc8b5 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -4,7 +4,7 @@
%%% Created : 20 Jan 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
@@ -169,8 +169,6 @@ convert_data(Host, "roster", User, [Data]) ->
end, Data),
lists:foreach(fun mod_roster:set_roster/1, Rosters);
convert_data(Host, "private", User, [Data]) ->
- LUser = jid:nodeprep(User),
- LServer = jid:nameprep(Host),
PrivData = lists:flatmap(
fun({_TagXMLNS, Raw}) ->
case deserialize(Raw) of
@@ -181,7 +179,7 @@ convert_data(Host, "private", User, [Data]) ->
[]
end
end, Data),
- mod_private:set_data(LUser, LServer, PrivData);
+ mod_private:set_data(jid:make(User, Host), PrivData);
convert_data(Host, "vcard", User, [Data]) ->
LServer = jid:nameprep(Host),
case deserialize(Data) of
@@ -191,7 +189,11 @@ convert_data(Host, "vcard", User, [Data]) ->
ok
end;
convert_data(_Host, "config", _User, [Data]) ->
- RoomJID = jid:decode(proplists:get_value(<<"jid">>, Data, <<"">>)),
+ RoomJID1 = case proplists:get_value(<<"jid">>, Data, not_found) of
+ not_found -> proplists:get_value(<<"_jid">>, Data, room_jid_not_found);
+ A when is_binary(A) -> A
+ end,
+ RoomJID = jid:decode(RoomJID1),
Config = proplists:get_value(<<"_data">>, Data, []),
RoomCfg = convert_room_config(Data),
case proplists:get_bool(<<"persistent">>, Config) of
@@ -305,22 +307,24 @@ convert_roster_item(LUser, LServer, JIDstring, LuaList) ->
InitR = #roster{usj = {LUser, LServer, LJID},
us = {LUser, LServer},
jid = LJID},
- Roster =
- lists:foldl(
- fun({<<"groups">>, Val}, R) ->
+ lists:foldl(
+ fun({<<"groups">>, Val}, [R]) ->
Gs = lists:flatmap(
fun({G, true}) -> [G];
(_) -> []
end, Val),
- R#roster{groups = Gs};
- ({<<"subscription">>, Sub}, R) ->
- R#roster{subscription = misc:binary_to_atom(Sub)};
- ({<<"ask">>, <<"subscribe">>}, R) ->
- R#roster{ask = out};
- ({<<"name">>, Name}, R) ->
- R#roster{name = Name}
- end, InitR, LuaList),
- [Roster]
+ [R#roster{groups = Gs}];
+ ({<<"subscription">>, Sub}, [R]) ->
+ [R#roster{subscription = misc:binary_to_atom(Sub)}];
+ ({<<"ask">>, <<"subscribe">>}, [R]) ->
+ [R#roster{ask = out}];
+ ({<<"name">>, Name}, [R]) ->
+ [R#roster{name = Name}];
+ ({<<"persist">>, false}, _) ->
+ [];
+ (_, []) ->
+ []
+ end, [InitR], LuaList)
catch _:{bad_jid, _} ->
[]
end.
@@ -360,9 +364,11 @@ convert_room_config(Data) ->
end,
[{affiliations, convert_room_affiliations(Data)},
{allow_change_subj, proplists:get_bool(<<"changesubject">>, Config)},
+ {mam, proplists:get_bool(<<"archiving">>, Config)},
{description, proplists:get_value(<<"description">>, Config, <<"">>)},
{members_only, proplists:get_bool(<<"members_only">>, Config)},
{moderated, proplists:get_bool(<<"moderated">>, Config)},
+ {persistent, proplists:get_bool(<<"persistent">>, Config)},
{anonymous, Anonymous}] ++ Pass ++ Subj.
convert_privacy_item({_, Item}) ->
@@ -519,6 +525,11 @@ el_to_offline_msg(LUser, LServer, #xmlel{attrs = Attrs} = El) ->
deserialize(L) ->
deserialize(L, #xmlel{}, []).
+deserialize([{Other, _}|T], El, Acc)
+ when (Other == <<"key">>)
+ or (Other == <<"when">>)
+ or (Other == <<"with">>) ->
+ deserialize(T, El, Acc);
deserialize([{<<"attr">>, Attrs}|T], El, Acc) ->
deserialize(T, El#xmlel{attrs = Attrs ++ El#xmlel.attrs}, Acc);
deserialize([{<<"name">>, Name}|T], El, Acc) ->
diff --git a/src/proxy_protocol.erl b/src/proxy_protocol.erl
new file mode 100644
index 000000000..5c33b130d
--- /dev/null
+++ b/src/proxy_protocol.erl
@@ -0,0 +1,184 @@
+%%%----------------------------------------------------------------------
+%%% File : ejabberd_http.erl
+%%% Author : Paweł Chmielowski <pawel@process-one.net>
+%%% Purpose :
+%%% Created : 27 Nov 2018 by Paweł Chmielowski <pawel@process-one.net>
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2019 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(proxy_protocol).
+-author("pawel@process-one.net").
+
+%% API
+-export([decode/3]).
+
+decode(SockMod, Socket, Timeout) ->
+ V = SockMod:recv(Socket, 6, Timeout),
+ case V of
+ {ok, <<"PROXY ">>} ->
+ decode_v1(SockMod, Socket, Timeout);
+ {ok, <<16#0d, 16#0a, 16#0d, 16#0a, 16#00, 16#0d>>} ->
+ decode_v2(SockMod, Socket, Timeout);
+ _ ->
+ {error, eproto}
+ end.
+
+decode_v1(SockMod, Socket, Timeout) ->
+ case read_until_rn(SockMod, Socket, <<>>, false, Timeout) of
+ {error, _} = Err ->
+ Err;
+ Val ->
+ case binary:split(Val, <<" ">>, [global]) of
+ [<<"TCP4">>, SAddr, DAddr, SPort, DPort] ->
+ try {inet_parse:ipv4strict_address(binary_to_list(SAddr)),
+ inet_parse:ipv4strict_address(binary_to_list(DAddr)),
+ binary_to_integer(SPort),
+ binary_to_integer(DPort)}
+ of
+ {{ok, DA}, {ok, SA}, DP, SP} ->
+ {{SA, SP}, {DA, DP}};
+ _ ->
+ {error, eproto}
+ catch
+ error:badarg ->
+ {error, eproto}
+ end;
+ [<<"TCP6">>, SAddr, DAddr, SPort, DPort] ->
+ try {inet_parse:ipv6strict_address(binary_to_list(SAddr)),
+ inet_parse:ipv6strict_address(binary_to_list(DAddr)),
+ binary_to_integer(SPort),
+ binary_to_integer(DPort)}
+ of
+ {{ok, DA}, {ok, SA}, DP, SP} ->
+ {{SA, SP}, {DA, DP}};
+ _ ->
+ {error, eproto}
+ catch
+ error:badarg ->
+ {error, eproto}
+ end;
+ [<<"UNKNOWN">> | _] ->
+ {undefined, undefined}
+ end
+ end.
+
+decode_v2(SockMod, Socket, Timeout) ->
+ case SockMod:recv(Socket, 10, Timeout) of
+ {error, _} = Err ->
+ Err;
+ {ok, <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a,
+ 2:4, Command:4, Transport:8, AddrLen:16/big-unsigned-integer>>} ->
+ case SockMod:recv(Socket, AddrLen, Timeout) of
+ {error, _} = Err ->
+ Err;
+ {ok, Data} ->
+ case Command of
+ 0 ->
+ case {inet:sockname(Socket), inet:peername(Socket)} of
+ {{ok, SA}, {ok, DA}} ->
+ {SA, DA};
+ {{error, _} = E, _} ->
+ E;
+ {_, {error, _} = E} ->
+ E
+ end;
+ 1 ->
+ case Transport of
+ % UNSPEC or UNIX
+ V when V == 0; V == 16#31; V == 16#32 ->
+ {{unknown, unknown}, {unknown, unknown}};
+ % IPV4 over TCP or UDP
+ V when V == 16#11; V == 16#12 ->
+ case Data of
+ <<D1:8, D2:8, D3:8, D4:8,
+ S1:8, S2:8, S3:8, S4:8,
+ DP:16/big-unsigned-integer,
+ SP:16/big-unsigned-integer,
+ _/binary>> ->
+ {{{S1, S2, S3, S4}, SP},
+ {{D1, D2, D3, D4}, DP}};
+ _ ->
+ {error, eproto}
+ end;
+ % IPV6 over TCP or UDP
+ V when V == 16#21; V == 16#22 ->
+ case Data of
+ <<D1:16/big-unsigned-integer,
+ D2:16/big-unsigned-integer,
+ D3:16/big-unsigned-integer,
+ D4:16/big-unsigned-integer,
+ D5:16/big-unsigned-integer,
+ D6:16/big-unsigned-integer,
+ D7:16/big-unsigned-integer,
+ D8:16/big-unsigned-integer,
+ S1:16/big-unsigned-integer,
+ S2:16/big-unsigned-integer,
+ S3:16/big-unsigned-integer,
+ S4:16/big-unsigned-integer,
+ S5:16/big-unsigned-integer,
+ S6:16/big-unsigned-integer,
+ S7:16/big-unsigned-integer,
+ S8:16/big-unsigned-integer,
+ DP:16/big-unsigned-integer,
+ SP:16/big-unsigned-integer,
+ _/binary>> ->
+ {{{S1, S2, S3, S4, S5, S6, S7, S8}, SP},
+ {{D1, D2, D3, D4, D5, D6, D7, D8}, DP}};
+ _ ->
+ {error, eproto}
+ end
+ end;
+ _ ->
+ {error, eproto}
+ end
+ end;
+ <<16#0a, 16#51, 16#55, 16#49, 16#54, 16#0a, _/binary>> ->
+ {error, eproto};
+ _ ->
+ {error, eproto}
+ end.
+
+read_until_rn(_SockMod, _Socket, Data, _, _) when size(Data) > 107 ->
+ {error, eproto};
+read_until_rn(SockMod, Socket, Data, true, Timeout) ->
+ case SockMod:recv(Socket, 1, Timeout) of
+ {ok, <<"\n">>} ->
+ Data;
+ {ok, <<"\r">>} ->
+ read_until_rn(SockMod, Socket, <<Data/binary, "\r">>,
+ true, Timeout);
+ {ok, Other} ->
+ read_until_rn(SockMod, Socket, <<Data/binary, "\r", Other/binary>>,
+ false, Timeout);
+ {error, _} = Err ->
+ Err
+ end;
+read_until_rn(SockMod, Socket, Data, false, Timeout) ->
+ case SockMod:recv(Socket, 2, Timeout) of
+ {ok, <<"\r\n">>} ->
+ Data;
+ {ok, <<Byte:8, "\r">>} ->
+ read_until_rn(SockMod, Socket, <<Data/binary, Byte:8>>,
+ true, Timeout);
+ {ok, Other} ->
+ read_until_rn(SockMod, Socket, <<Data/binary, Other/binary>>,
+ false, Timeout);
+ {error, _} = Err ->
+ Err
+ end.
diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl
index 6a13054a8..a709ce8b2 100644
--- a/src/pubsub_db_sql.erl
+++ b/src/pubsub_db_sql.erl
@@ -5,7 +5,7 @@
%%% Created : 7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_index.erl b/src/pubsub_index.erl
index 9c80e265f..de71b6395 100644
--- a/src/pubsub_index.erl
+++ b/src/pubsub_index.erl
@@ -5,7 +5,7 @@
%%% Created : 30 Apr 2009 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl
index 0728da765..da7d5b997 100644
--- a/src/pubsub_migrate.erl
+++ b/src/pubsub_migrate.erl
@@ -5,7 +5,7 @@
%%% Created : 26 Jul 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl
index 615ab322f..88a19c3c6 100644
--- a/src/pubsub_subscription.erl
+++ b/src/pubsub_subscription.erl
@@ -5,7 +5,7 @@
%%% Created : 29 May 2009 by Brian Cully <bjc@kublai.com>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl
index 2fcfb0f19..194b2676a 100644
--- a/src/pubsub_subscription_sql.erl
+++ b/src/pubsub_subscription_sql.erl
@@ -6,7 +6,7 @@
%%% Created : 7 Aug 2009 by Pablo Polvorin <pablo.polvorin@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/rest.erl b/src/rest.erl
index 16fa20eb0..8aab0a5ae 100644
--- a/src/rest.erl
+++ b/src/rest.erl
@@ -5,7 +5,7 @@
%%% Created : 16 Oct 2014 by Christophe Romain <christophe.romain@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/str.erl b/src/str.erl
index b314acf5b..bbf0d6a6e 100644
--- a/src/str.erl
+++ b/src/str.erl
@@ -5,7 +5,7 @@
%%% Created : 23 Feb 2012 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/translate.erl b/src/translate.erl
index 5a1e13e82..b2c3e4481 100644
--- a/src/translate.erl
+++ b/src/translate.erl
@@ -5,7 +5,7 @@
%%% Created : 6 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/win32_dns.erl b/src/win32_dns.erl
index fe1ba5395..dcb1d34eb 100644
--- a/src/win32_dns.erl
+++ b/src/win32_dns.erl
@@ -5,7 +5,7 @@
%%% Created : 5 Mar 2009 by Geoff Cant
%%%
%%%
-%%% ejabberd, Copyright (C) 2002-2018 ProcessOne
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
diff --git a/src/xml_compress.erl b/src/xml_compress.erl
new file mode 100644
index 000000000..a85ec56b2
--- /dev/null
+++ b/src/xml_compress.erl
@@ -0,0 +1,958 @@
+-module(xml_compress).
+-export([encode/3, decode/3]).
+
+% This file was generated by xml_compress_gen
+%
+% Rules used:
+%
+% [{<<"eu.siacs.conversations.axolotl">>,<<"key">>,
+% [{<<"prekey">>,[<<"true">>]},{<<"rid">>,[]}],
+% []},
+% {<<"jabber:client">>,<<"message">>,
+% [{<<"from">>,[j2,{j1}]},
+% {<<"id">>,[]},
+% {<<"to">>,[j1,j2,{j1}]},
+% {<<"type">>,[<<"chat">>,<<"groupchat">>,<<"normal">>]},
+% {<<"xml:lang">>,[<<"en">>]}],
+% []},
+% {<<"urn:xmpp:hints">>,<<"store">>,[],[]},
+% {<<"jabber:client">>,<<"body">>,[],
+% [<<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,77,79,32,101,
+% 110,99,114,121,112,116,101,100,32,109,101,115,115,97,103,101,32,98,117,
+% 116,32,121,111,117,114,32,99,108,105,101,110,116,32,100,111,101,115,110,
+% 226,128,153,116,32,115,101,101,109,32,116,111,32,115,117,112,112,111,
+% 114,116,32,116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,32,
+% 105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,104,116,116,
+% 112,115,58,47,47,99,111,110,118,101,114,115,97,116,105,111,110,115,46,
+% 105,109,47,111,109,101,109,111>>]},
+% {<<"urn:xmpp:sid:0">>,<<"origin-id">>,[{<<"id">>,[]}],[]},
+% {<<"urn:xmpp:chat-markers:0">>,<<"markable">>,[],[]},
+% {<<"eu.siacs.conversations.axolotl">>,<<"encrypted">>,[],[]},
+% {<<"eu.siacs.conversations.axolotl">>,<<"header">>,[{<<"sid">>,[]}],[]},
+% {<<"eu.siacs.conversations.axolotl">>,<<"iv">>,[],[]},
+% {<<"eu.siacs.conversations.axolotl">>,<<"payload">>,[],[]},
+% {<<"urn:xmpp:eme:0">>,<<"encryption">>,
+% [{<<"name">>,[<<"OMEMO">>]},
+% {<<"namespace">>,[<<"eu.siacs.conversations.axolotl">>]}],
+% []},
+% {<<"urn:xmpp:delay">>,<<"delay">>,[{<<"from">>,[j1]},{<<"stamp">>,[]}],[]},
+% {<<"http://jabber.org/protocol/address">>,<<"address">>,
+% [{<<"jid">>,[{j1}]},{<<"type">>,[<<"ofrom">>]}],
+% []},
+% {<<"http://jabber.org/protocol/address">>,<<"addresses">>,[],[]},
+% {<<"urn:xmpp:chat-markers:0">>,<<"displayed">>,
+% [{<<"id">>,[]},{<<"sender">>,[{j1},{j2}]}],
+% []},
+% {<<"urn:xmpp:mam:tmp">>,<<"archived">>,[{<<"by">>,[]},{<<"id">>,[]}],[]},
+% {<<"urn:xmpp:sid:0">>,<<"stanza-id">>,[{<<"by">>,[]},{<<"id">>,[]}],[]},
+% {<<"urn:xmpp:receipts">>,<<"request">>,[],[]},
+% {<<"urn:xmpp:chat-markers:0">>,<<"received">>,[{<<"id">>,[]}],[]},
+% {<<"urn:xmpp:receipts">>,<<"received">>,[{<<"id">>,[]}],[]},
+% {<<"http://jabber.org/protocol/chatstates">>,<<"active">>,[],[]},
+% {<<"http://jabber.org/protocol/muc#user">>,<<"invite">>,
+% [{<<"from">>,[{j1}]}],
+% []},
+% {<<"http://jabber.org/protocol/muc#user">>,<<"reason">>,[],[]},
+% {<<"http://jabber.org/protocol/muc#user">>,<<"x">>,[],[]},
+% {<<"jabber:x:conference">>,<<"x">>,[{<<"jid">>,[j2]}],[]},
+% {<<"jabber:client">>,<<"subject">>,[],[]},
+% {<<"jabber:client">>,<<"thread">>,[],[]},
+% {<<"http://jabber.org/protocol/pubsub#event">>,<<"event">>,[],[]},
+% {<<"http://jabber.org/protocol/pubsub#event">>,<<"item">>,[{<<"id">>,[]}],[]},
+% {<<"http://jabber.org/protocol/pubsub#event">>,<<"items">>,
+% [{<<"node">>,[<<"urn:xmpp:mucsub:nodes:messages">>]}],
+% []},
+% {<<"p1:push:custom">>,<<"x">>,[{<<"key">>,[]},{<<"value">>,[]}],[]},
+% {<<"p1:pushed">>,<<"x">>,[],[]},
+% {<<"urn:xmpp:message-correct:0">>,<<"replace">>,[{<<"id">>,[]}],[]},
+% {<<"http://jabber.org/protocol/chatstates">>,<<"composing">>,[],[]}]
+
+encode(El, J1, J2) ->
+ encode_child(El, <<"jabber:client">>,
+ J1, J2, byte_size(J1), byte_size(J2), <<1:8>>).
+
+encode_attr({<<"xmlns">>, _}, Acc) ->
+ Acc;
+encode_attr({N, V}, Acc) ->
+ <<Acc/binary, 1:8, (encode_string(N))/binary,
+ (encode_string(V))/binary>>.
+
+encode_attrs(Attrs, Acc) ->
+ lists:foldl(fun encode_attr/2, Acc, Attrs).
+
+encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ E1 = if
+ PNs == Ns -> encode_attrs(Attrs, <<Pfx/binary, 2:8, (encode_string(Name))/binary>>);
+ true -> encode_attrs(Attrs, <<Pfx/binary, 3:8, (encode_string(Ns))/binary, (encode_string(Name))/binary>>)
+ end,
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E1/binary, 2:8>>),
+ <<E2/binary, 4:8>>.
+
+encode_child({xmlel, Name, Attrs, Children}, PNs, J1, J2, J1L, J2L, Pfx) ->
+ case lists:keyfind(<<"xmlns">>, 1, Attrs) of
+ false ->
+ encode(PNs, PNs, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx);
+ {_, Ns} ->
+ encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+ end;
+encode_child({xmlcdata, Data}, _PNs, _J1, _J2, _J1L, _J2L, Pfx) ->
+ <<Pfx/binary, 1:8, (encode_string(Data))/binary>>.
+
+encode_children(Children, PNs, J1, J2, J1L, J2L, Pfx) ->
+ lists:foldl(
+ fun(Child, Acc) ->
+ encode_child(Child, PNs, J1, J2, J1L, J2L, Acc)
+ end, Pfx, Children).
+
+encode_string(Data) ->
+ <<V1:4, V2:6, V3:6>> = <<(byte_size(Data)):16/unsigned-big-integer>>,
+ case {V1, V2, V3} of
+ {0, 0, V3} ->
+ <<V3:8, Data/binary>>;
+ {0, V2, V3} ->
+ <<(V3 bor 64):8, V2:8, Data/binary>>;
+ _ ->
+ <<(V3 bor 64):8, (V2 bor 64):8, V1:8, Data/binary>>
+ end.
+
+encode(PNs, <<"eu.siacs.conversations.axolotl">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"key">> ->
+ E = lists:foldl(fun
+ ({<<"prekey">>, AVal}, Acc) ->
+ case AVal of
+ <<"true">> -> <<Acc/binary, 3:8>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"rid">>, AVal}, Acc) ->
+ <<Acc/binary, 5:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 5:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"encrypted">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 12:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"header">> ->
+ E = lists:foldl(fun
+ ({<<"sid">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 13:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"iv">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 14:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"payload">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 15:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"jabber:client">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"message">> ->
+ E = lists:foldl(fun
+ ({<<"from">>, AVal}, Acc) ->
+ case AVal of
+ J2 -> <<Acc/binary, 3:8>>;
+ <<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 4:8, (encode_string(Rest))/binary>>;
+ _ -> <<Acc/binary, 5:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 6:8, (encode_string(AVal))/binary>>;
+ ({<<"to">>, AVal}, Acc) ->
+ case AVal of
+ J1 -> <<Acc/binary, 7:8>>;
+ J2 -> <<Acc/binary, 8:8>>;
+ <<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 9:8, (encode_string(Rest))/binary>>;
+ _ -> <<Acc/binary, 10:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"type">>, AVal}, Acc) ->
+ case AVal of
+ <<"chat">> -> <<Acc/binary, 11:8>>;
+ <<"groupchat">> -> <<Acc/binary, 12:8>>;
+ <<"normal">> -> <<Acc/binary, 13:8>>;
+ _ -> <<Acc/binary, 14:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"xml:lang">>, AVal}, Acc) ->
+ case AVal of
+ <<"en">> -> <<Acc/binary, 15:8>>;
+ _ -> <<Acc/binary, 16:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 6:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"body">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 8:8>>),
+ E2 = lists:foldl(fun
+ ({xmlcdata, <<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,
+ 77,79,32,101,110,99,114,121,112,116,101,100,32,109,101,115,
+ 115,97,103,101,32,98,117,116,32,121,111,117,114,32,99,108,
+ 105,101,110,116,32,100,111,101,115,110,226,128,153,116,32,
+ 115,101,101,109,32,116,111,32,115,117,112,112,111,114,116,32,
+ 116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,32,
+ 105,110,102,111,114,109,97,116,105,111,110,32,111,110,32,104,
+ 116,116,112,115,58,47,47,99,111,110,118,101,114,115,97,116,
+ 105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Acc) -> <<Acc/binary, 9:8>>;
+ (El, Acc) -> encode_child(El, Ns, J1, J2, J1L, J2L, Acc)
+ end, <<E/binary, 2:8>>, Children),
+ <<E2/binary, 4:8>>;
+ <<"subject">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 31:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"thread">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 32:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:hints">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"store">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 7:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:sid:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"origin-id">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 10:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"stanza-id">> ->
+ E = lists:foldl(fun
+ ({<<"by">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 22:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:chat-markers:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"markable">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 11:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"displayed">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ ({<<"sender">>, AVal}, Acc) ->
+ case AVal of
+ <<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 4:8, (encode_string(Rest))/binary>>;
+ <<J2:J2L/binary, Rest/binary>> -> <<Acc/binary, 5:8, (encode_string(Rest))/binary>>;
+ _ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 20:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"received">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 24:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:eme:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"encryption">> ->
+ E = lists:foldl(fun
+ ({<<"name">>, AVal}, Acc) ->
+ case AVal of
+ <<"OMEMO">> -> <<Acc/binary, 3:8>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"namespace">>, AVal}, Acc) ->
+ case AVal of
+ <<"eu.siacs.conversations.axolotl">> -> <<Acc/binary, 5:8>>;
+ _ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 16:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:delay">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"delay">> ->
+ E = lists:foldl(fun
+ ({<<"from">>, AVal}, Acc) ->
+ case AVal of
+ J1 -> <<Acc/binary, 3:8>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"stamp">>, AVal}, Acc) ->
+ <<Acc/binary, 5:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 17:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"http://jabber.org/protocol/address">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"address">> ->
+ E = lists:foldl(fun
+ ({<<"jid">>, AVal}, Acc) ->
+ case AVal of
+ <<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 3:8, (encode_string(Rest))/binary>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ ({<<"type">>, AVal}, Acc) ->
+ case AVal of
+ <<"ofrom">> -> <<Acc/binary, 5:8>>;
+ _ -> <<Acc/binary, 6:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 18:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"addresses">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 19:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:mam:tmp">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"archived">> ->
+ E = lists:foldl(fun
+ ({<<"by">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 21:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:receipts">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"request">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 23:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"received">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 25:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"http://jabber.org/protocol/chatstates">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"active">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 26:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"composing">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 39:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"http://jabber.org/protocol/muc#user">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"invite">> ->
+ E = lists:foldl(fun
+ ({<<"from">>, AVal}, Acc) ->
+ case AVal of
+ <<J1:J1L/binary, Rest/binary>> -> <<Acc/binary, 3:8, (encode_string(Rest))/binary>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 27:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"reason">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 28:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"x">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 29:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"jabber:x:conference">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"x">> ->
+ E = lists:foldl(fun
+ ({<<"jid">>, AVal}, Acc) ->
+ case AVal of
+ J2 -> <<Acc/binary, 3:8>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 30:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"http://jabber.org/protocol/pubsub#event">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"event">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 33:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"item">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 34:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ <<"items">> ->
+ E = lists:foldl(fun
+ ({<<"node">>, AVal}, Acc) ->
+ case AVal of
+ <<"urn:xmpp:mucsub:nodes:messages">> -> <<Acc/binary, 3:8>>;
+ _ -> <<Acc/binary, 4:8, (encode_string(AVal))/binary>>
+ end;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 35:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"p1:push:custom">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"x">> ->
+ E = lists:foldl(fun
+ ({<<"key">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ ({<<"value">>, AVal}, Acc) ->
+ <<Acc/binary, 4:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 36:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"p1:pushed">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"x">> ->
+ E = encode_attrs(Attrs, <<Pfx/binary, 37:8>>),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, <<"urn:xmpp:message-correct:0">> = Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ case Name of
+ <<"replace">> ->
+ E = lists:foldl(fun
+ ({<<"id">>, AVal}, Acc) ->
+ <<Acc/binary, 3:8, (encode_string(AVal))/binary>>;
+ (Attr, Acc) -> encode_attr(Attr, Acc)
+ end, <<Pfx/binary, 38:8>>, Attrs),
+ E2 = encode_children(Children, Ns, J1, J2, J1L, J2L, <<E/binary, 2:8>>),
+ <<E2/binary, 4:8>>;
+ _ -> encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx)
+end;
+encode(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx) ->
+ encode_el(PNs, Ns, Name, Attrs, Children, J1, J2, J1L, J2L, Pfx).
+
+decode(<<$<, _/binary>> = Data, _J1, _J2) ->
+ fxml_stream:parse_element(Data);
+decode(<<1:8, Rest/binary>>, J1, J2) ->
+ {El, _} = decode(Rest, <<"jabber:client">>, J1, J2),
+ El.
+
+decode_string(Data) ->
+ case Data of
+ <<0:2, L:6, Str:L/binary, Rest/binary>> ->
+ {Str, Rest};
+ <<1:2, L1:6, 0:2, L2:6, Rest/binary>> ->
+ L = L2*64 + L1,
+ <<Str:L/binary, Rest2/binary>> = Rest,
+ {Str, Rest2};
+ <<1:2, L1:6, 1:2, L2:6, L3:8, Rest/binary>> ->
+ L = (L3*64 + L2)*64 + L1,
+ <<Str:L/binary, Rest2/binary>> = Rest,
+ {Str, Rest2}
+ end.
+
+decode_child(<<1:8, Rest/binary>>, _PNs, _J1, _J2) ->
+ {Text, Rest2} = decode_string(Rest),
+ {{xmlcdata, Text}, Rest2};
+decode_child(<<2:8, Rest/binary>>, PNs, J1, J2) ->
+ {Name, Rest2} = decode_string(Rest),
+ {Attrs, Rest3} = decode_attrs(Rest2),
+ {Children, Rest4} = decode_children(Rest3, PNs, J1, J2),
+ {{xmlel, Name, Attrs, Children}, Rest4};
+decode_child(<<3:8, Rest/binary>>, PNs, J1, J2) ->
+ {Ns, Rest2} = decode_string(Rest),
+ {Name, Rest3} = decode_string(Rest2),
+ {Attrs, Rest4} = decode_attrs(Rest3),
+ {Children, Rest5} = decode_children(Rest4, Ns, J1, J2),
+ {{xmlel, Name, add_ns(PNs, Ns, Attrs), Children}, Rest5};
+decode_child(<<4:8, Rest/binary>>, _PNs, _J1, _J2) ->
+ {stop, Rest};
+decode_child(Other, PNs, J1, J2) ->
+ decode(Other, PNs, J1, J2).
+
+decode_children(Data, PNs, J1, J2) ->
+ prefix_map(fun(Data2) -> decode(Data2, PNs, J1, J2) end, Data).
+
+decode_attr(<<1:8, Rest/binary>>) ->
+ {Name, Rest2} = decode_string(Rest),
+ {Val, Rest3} = decode_string(Rest2),
+ {{Name, Val}, Rest3};
+decode_attr(<<2:8, Rest/binary>>) ->
+ {stop, Rest}.
+
+decode_attrs(Data) ->
+ prefix_map(fun decode_attr/1, Data).
+
+prefix_map(F, Data) ->
+ prefix_map(F, Data, []).
+
+prefix_map(F, Data, Acc) ->
+ case F(Data) of
+ {stop, Rest} ->
+ {lists:reverse(Acc), Rest};
+ {Val, Rest} ->
+ prefix_map(F, Rest, [Val | Acc])
+ end.
+
+add_ns(Ns, Ns, Attrs) ->
+ Attrs;
+add_ns(_, Ns, Attrs) ->
+ [{<<"xmlns">>, Ns} | Attrs].
+
+decode(<<5:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"eu.siacs.conversations.axolotl">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"prekey">>, <<"true">>}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"prekey">>, AVal}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"rid">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"key">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<12:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"eu.siacs.conversations.axolotl">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"encrypted">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<13:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"eu.siacs.conversations.axolotl">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"sid">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"header">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<14:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"eu.siacs.conversations.axolotl">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"iv">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<15:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"eu.siacs.conversations.axolotl">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"payload">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<6:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"jabber:client">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"from">>, J2}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"from">>, <<J1/binary, AVal/binary>>}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"from">>, AVal}, Rest4};
+ (<<6:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<7:8, Rest3/binary>>) ->
+ {{<<"to">>, J1}, Rest3};
+ (<<8:8, Rest3/binary>>) ->
+ {{<<"to">>, J2}, Rest3};
+ (<<9:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"to">>, <<J1/binary, AVal/binary>>}, Rest4};
+ (<<10:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"to">>, AVal}, Rest4};
+ (<<11:8, Rest3/binary>>) ->
+ {{<<"type">>, <<"chat">>}, Rest3};
+ (<<12:8, Rest3/binary>>) ->
+ {{<<"type">>, <<"groupchat">>}, Rest3};
+ (<<13:8, Rest3/binary>>) ->
+ {{<<"type">>, <<"normal">>}, Rest3};
+ (<<14:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"type">>, AVal}, Rest4};
+ (<<15:8, Rest3/binary>>) ->
+ {{<<"xml:lang">>, <<"en">>}, Rest3};
+ (<<16:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"xml:lang">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"message">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<8:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"jabber:client">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = prefix_map(fun (<<9:8, Rest5/binary>>) ->
+ {{xmlcdata, <<73,32,115,101,110,116,32,121,111,117,32,97,110,32,79,77,69,
+ 77,79,32,101,110,99,114,121,112,116,101,100,32,109,101,115,
+ 115,97,103,101,32,98,117,116,32,121,111,117,114,32,99,108,
+ 105,101,110,116,32,100,111,101,115,110,226,128,153,116,32,
+ 115,101,101,109,32,116,111,32,115,117,112,112,111,114,116,
+ 32,116,104,97,116,46,32,70,105,110,100,32,109,111,114,101,
+ 32,105,110,102,111,114,109,97,116,105,111,110,32,111,110,
+ 32,104,116,116,112,115,58,47,47,99,111,110,118,101,114,115,
+ 97,116,105,111,110,115,46,105,109,47,111,109,101,109,111>>}, Rest5};
+ (Other) ->
+ decode_child(Other, Ns, J1, J2)
+ end, Rest2),
+ {{xmlel, <<"body">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<31:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"jabber:client">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"subject">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<32:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"jabber:client">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"thread">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<7:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:hints">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"store">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<10:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:sid:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"origin-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<22:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:sid:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"by">>, AVal}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"stanza-id">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<11:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:chat-markers:0">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"markable">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<20:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:chat-markers:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"sender">>, <<J1/binary, AVal/binary>>}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"sender">>, <<J2/binary, AVal/binary>>}, Rest4};
+ (<<6:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"sender">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"displayed">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<24:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:chat-markers:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<16:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:eme:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"name">>, <<"OMEMO">>}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"name">>, AVal}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {{<<"namespace">>, <<"eu.siacs.conversations.axolotl">>}, Rest3};
+ (<<6:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"namespace">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"encryption">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<17:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:delay">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"from">>, J1}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"from">>, AVal}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"stamp">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"delay">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<18:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/address">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"jid">>, <<J1/binary, AVal/binary>>}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"jid">>, AVal}, Rest4};
+ (<<5:8, Rest3/binary>>) ->
+ {{<<"type">>, <<"ofrom">>}, Rest3};
+ (<<6:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"type">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"address">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<19:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/address">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"addresses">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<21:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:mam:tmp">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"by">>, AVal}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"archived">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<23:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:receipts">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"request">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<25:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:receipts">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"received">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<26:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/chatstates">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"active">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<39:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/chatstates">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"composing">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<27:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/muc#user">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"from">>, <<J1/binary, AVal/binary>>}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"from">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"invite">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<28:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/muc#user">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"reason">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<29:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/muc#user">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<30:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"jabber:x:conference">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"jid">>, J2}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"jid">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<33:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/pubsub#event">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"event">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<34:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/pubsub#event">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"item">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<35:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"http://jabber.org/protocol/pubsub#event">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {{<<"node">>, <<"urn:xmpp:mucsub:nodes:messages">>}, Rest3};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"node">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"items">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<36:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"p1:push:custom">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"key">>, AVal}, Rest4};
+ (<<4:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"value">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<37:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"p1:pushed">>,
+ {Attrs, Rest2} = decode_attrs(Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"x">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(<<38:8, Rest/binary>>, PNs, J1, J2) ->
+ Ns = <<"urn:xmpp:message-correct:0">>,
+ {Attrs, Rest2} = prefix_map(fun
+ (<<3:8, Rest3/binary>>) ->
+ {AVal, Rest4} = decode_string(Rest3),
+ {{<<"id">>, AVal}, Rest4};
+ (<<2:8, Rest3/binary>>) ->
+ {stop, Rest3};
+ (Data) ->
+ decode_attr(Data)
+ end, Rest),
+ {Children, Rest6} = decode_children(Rest2, Ns, J1, J2),
+ {{xmlel, <<"replace">>, add_ns(PNs, Ns, Attrs), Children}, Rest6};
+decode(Other, PNs, J1, J2) ->
+ decode_child(Other, PNs, J1, J2).
+