aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeny Khramtsov <ekhramtsov@process-one.net>2019-06-14 12:33:26 +0300
committerEvgeny Khramtsov <ekhramtsov@process-one.net>2019-06-14 12:33:26 +0300
commita02cff0e780bb735531594c4ece81e8628f79782 (patch)
tree6fe7d8219d14f58183be1741fcea262c216db447 /src
parentReturn jid_malformed error when sending presence without nick to conference (diff)
Use new configuration validator
Diffstat (limited to 'src')
-rw-r--r--src/ELDAPv3.erl293
-rw-r--r--src/acl.erl1022
-rw-r--r--src/econf.erl529
-rw-r--r--src/ejabberd.erl63
-rw-r--r--src/ejabberd_access_permissions.erl510
-rw-r--r--src/ejabberd_acme.erl183
-rw-r--r--src/ejabberd_admin.erl60
-rw-r--r--src/ejabberd_app.erl121
-rw-r--r--src/ejabberd_auth.erl58
-rw-r--r--src/ejabberd_auth_anonymous.erl18
-rw-r--r--src/ejabberd_auth_external.erl29
-rw-r--r--src/ejabberd_auth_ldap.erl75
-rw-r--r--src/ejabberd_auth_mnesia.erl12
-rw-r--r--src/ejabberd_auth_pam.erl17
-rw-r--r--src/ejabberd_auth_riak.erl2
-rw-r--r--src/ejabberd_auth_sql.erl12
-rw-r--r--src/ejabberd_bosh.erl30
-rw-r--r--src/ejabberd_c2s.erl191
-rw-r--r--src/ejabberd_c2s_config.erl29
-rw-r--r--src/ejabberd_captcha.erl100
-rw-r--r--src/ejabberd_cluster.erl23
-rw-r--r--src/ejabberd_commands.erl147
-rw-r--r--src/ejabberd_config.erl2070
-rw-r--r--src/ejabberd_config_transformer.erl517
-rw-r--r--src/ejabberd_ctl.erl89
-rw-r--r--src/ejabberd_db_sup.erl46
-rw-r--r--src/ejabberd_hooks.erl15
-rw-r--r--src/ejabberd_http.erl193
-rw-r--r--src/ejabberd_http_ws.erl32
-rw-r--r--src/ejabberd_iq.erl6
-rw-r--r--src/ejabberd_listener.erl762
-rw-r--r--src/ejabberd_local.erl4
-rw-r--r--src/ejabberd_logger.erl40
-rw-r--r--src/ejabberd_oauth.erl69
-rw-r--r--src/ejabberd_oauth_mnesia.erl5
-rw-r--r--src/ejabberd_oauth_rest.erl12
-rw-r--r--src/ejabberd_oauth_sql.erl1
-rw-r--r--src/ejabberd_old_config.erl655
-rw-r--r--src/ejabberd_option.erl1058
-rw-r--r--src/ejabberd_options.erl757
-rw-r--r--src/ejabberd_piefxis.erl2
-rw-r--r--src/ejabberd_pkix.erl106
-rw-r--r--src/ejabberd_rdbms.erl28
-rw-r--r--src/ejabberd_redis.erl47
-rw-r--r--src/ejabberd_redis_sup.erl100
-rw-r--r--src/ejabberd_regexp.erl8
-rw-r--r--src/ejabberd_riak.erl11
-rw-r--r--src/ejabberd_riak_sup.erl133
-rw-r--r--src/ejabberd_router.erl81
-rw-r--r--src/ejabberd_router_mnesia.erl30
-rw-r--r--src/ejabberd_router_riak.erl2
-rw-r--r--src/ejabberd_router_sql.erl1
-rw-r--r--src/ejabberd_s2s.erl240
-rw-r--r--src/ejabberd_s2s_in.erl19
-rw-r--r--src/ejabberd_s2s_out.erl109
-rw-r--r--src/ejabberd_service.erl66
-rw-r--r--src/ejabberd_shaper.erl300
-rw-r--r--src/ejabberd_sip.erl14
-rw-r--r--src/ejabberd_sm.erl68
-rw-r--r--src/ejabberd_sm_sql.erl1
-rw-r--r--src/ejabberd_sql.erl135
-rw-r--r--src/ejabberd_sql_pt.erl14
-rw-r--r--src/ejabberd_sql_sup.erl55
-rw-r--r--src/ejabberd_stun.erl51
-rw-r--r--src/ejabberd_sup.erl5
-rw-r--r--src/ejabberd_system_monitor.erl29
-rw-r--r--src/ejabberd_web_admin.erl927
-rw-r--r--src/ejabberd_websocket.erl28
-rw-r--r--src/ejabberd_xmlrpc.erl43
-rw-r--r--src/eldap_utils.erl194
-rw-r--r--src/elixir_logger_backend.erl9
-rw-r--r--src/ext_mod.erl24
-rw-r--r--src/extauth.erl6
-rw-r--r--src/gen_iq_handler.erl24
-rw-r--r--src/gen_mod.erl895
-rw-r--r--src/gen_pubsub_node.erl2
-rw-r--r--src/gen_pubsub_nodetree.erl2
-rw-r--r--src/misc.erl113
-rw-r--r--src/mod_adhoc.erl8
-rw-r--r--src/mod_adhoc_opt.erl13
-rw-r--r--src/mod_admin_extra.erl2
-rw-r--r--src/mod_admin_update_sql.erl4
-rw-r--r--src/mod_announce.erl50
-rw-r--r--src/mod_announce_mnesia.erl4
-rw-r--r--src/mod_announce_opt.erl48
-rw-r--r--src/mod_announce_sql.erl1
-rw-r--r--src/mod_avatar.erl72
-rw-r--r--src/mod_avatar_opt.erl20
-rw-r--r--src/mod_block_strangers.erl33
-rw-r--r--src/mod_block_strangers_opt.erl48
-rw-r--r--src/mod_bosh.erl105
-rw-r--r--src/mod_bosh_opt.erl83
-rw-r--r--src/mod_bosh_riak.erl2
-rw-r--r--src/mod_bosh_sql.erl1
-rw-r--r--src/mod_caps.erl39
-rw-r--r--src/mod_caps_opt.erl41
-rw-r--r--src/mod_caps_sql.erl1
-rw-r--r--src/mod_carboncopy.erl35
-rw-r--r--src/mod_client_state.erl26
-rw-r--r--src/mod_client_state_opt.erl27
-rw-r--r--src/mod_configure.erl115
-rw-r--r--src/mod_delegation.erl34
-rw-r--r--src/mod_delegation_opt.erl13
-rw-r--r--src/mod_disco.erl85
-rw-r--r--src/mod_disco_opt.erl27
-rw-r--r--src/mod_echo.erl22
-rw-r--r--src/mod_echo_opt.erl20
-rw-r--r--src/mod_fail2ban.erl27
-rw-r--r--src/mod_fail2ban_opt.erl27
-rw-r--r--src/mod_http_api.erl123
-rw-r--r--src/mod_http_api_opt.erl13
-rw-r--r--src/mod_http_fileserver.erl66
-rw-r--r--src/mod_http_fileserver_opt.erl55
-rw-r--r--src/mod_http_upload.erl152
-rw-r--r--src/mod_http_upload_opt.erl125
-rw-r--r--src/mod_http_upload_quota.erl24
-rw-r--r--src/mod_http_upload_quota_opt.erl27
-rw-r--r--src/mod_last.erl51
-rw-r--r--src/mod_last_mnesia.erl4
-rw-r--r--src/mod_last_opt.erl41
-rw-r--r--src/mod_last_sql.erl1
-rw-r--r--src/mod_legacy_auth.erl5
-rw-r--r--src/mod_mam.erl109
-rw-r--r--src/mod_mam_mnesia.erl7
-rw-r--r--src/mod_mam_opt.erl90
-rw-r--r--src/mod_mam_sql.erl7
-rw-r--r--src/mod_metrics.erl23
-rw-r--r--src/mod_metrics_opt.erl20
-rw-r--r--src/mod_mix.erl29
-rw-r--r--src/mod_mix_mnesia.erl1
-rw-r--r--src/mod_mix_opt.erl41
-rw-r--r--src/mod_mix_pam.erl39
-rw-r--r--src/mod_mix_pam_mnesia.erl6
-rw-r--r--src/mod_mix_pam_opt.erl41
-rw-r--r--src/mod_mix_pam_sql.erl6
-rw-r--r--src/mod_mix_sql.erl5
-rw-r--r--src/mod_mqtt.erl111
-rw-r--r--src/mod_mqtt_mnesia.erl4
-rw-r--r--src/mod_mqtt_opt.erl104
-rw-r--r--src/mod_mqtt_session.erl10
-rw-r--r--src/mod_mqtt_sql.erl1
-rw-r--r--src/mod_muc.erl225
-rw-r--r--src/mod_muc_admin.erl32
-rw-r--r--src/mod_muc_log.erl124
-rw-r--r--src/mod_muc_log_opt.erl76
-rw-r--r--src/mod_muc_mnesia.erl16
-rw-r--r--src/mod_muc_opt.erl202
-rw-r--r--src/mod_muc_room.erl100
-rw-r--r--src/mod_muc_sql.erl3
-rw-r--r--src/mod_multicast.erl70
-rw-r--r--src/mod_multicast_opt.erl41
-rw-r--r--src/mod_offline.erl97
-rw-r--r--src/mod_offline_mnesia.erl3
-rw-r--r--src/mod_offline_opt.erl62
-rw-r--r--src/mod_offline_sql.erl1
-rw-r--r--src/mod_ping.erl20
-rw-r--r--src/mod_ping_opt.erl34
-rw-r--r--src/mod_pres_counter.erl8
-rw-r--r--src/mod_pres_counter_opt.erl20
-rw-r--r--src/mod_privacy.erl41
-rw-r--r--src/mod_privacy_mnesia.erl4
-rw-r--r--src/mod_privacy_opt.erl41
-rw-r--r--src/mod_privacy_sql.erl1
-rw-r--r--src/mod_private.erl39
-rw-r--r--src/mod_private_mnesia.erl4
-rw-r--r--src/mod_private_opt.erl41
-rw-r--r--src/mod_private_sql.erl1
-rw-r--r--src/mod_privilege.erl22
-rw-r--r--src/mod_privilege_opt.erl27
-rw-r--r--src/mod_proxy65.erl69
-rw-r--r--src/mod_proxy65_opt.erl104
-rw-r--r--src/mod_proxy65_riak.erl2
-rw-r--r--src/mod_proxy65_service.erl45
-rw-r--r--src/mod_proxy65_sql.erl1
-rw-r--r--src/mod_proxy65_stream.erl75
-rw-r--r--src/mod_pubsub.erl191
-rw-r--r--src/mod_pubsub_mnesia.erl32
-rw-r--r--src/mod_pubsub_opt.erl104
-rw-r--r--src/mod_pubsub_riak.erl32
-rw-r--r--src/mod_pubsub_sql.erl32
-rw-r--r--src/mod_push.erl56
-rw-r--r--src/mod_push_keepalive.erl28
-rw-r--r--src/mod_push_keepalive_opt.erl27
-rw-r--r--src/mod_push_opt.erl55
-rw-r--r--src/mod_push_sql.erl1
-rw-r--r--src/mod_register.erl146
-rw-r--r--src/mod_register_opt.erl69
-rw-r--r--src/mod_register_web.erl27
-rw-r--r--src/mod_roster.erl56
-rw-r--r--src/mod_roster_mnesia.erl7
-rw-r--r--src/mod_roster_opt.erl62
-rw-r--r--src/mod_roster_sql.erl1
-rw-r--r--src/mod_s2s_dialback.erl81
-rw-r--r--src/mod_s2s_dialback_opt.erl13
-rw-r--r--src/mod_service_log.erl11
-rw-r--r--src/mod_service_log_opt.erl13
-rw-r--r--src/mod_shared_roster.erl11
-rw-r--r--src/mod_shared_roster_ldap.erl197
-rw-r--r--src/mod_shared_roster_ldap_opt.erl209
-rw-r--r--src/mod_shared_roster_mnesia.erl4
-rw-r--r--src/mod_shared_roster_opt.erl13
-rw-r--r--src/mod_shared_roster_sql.erl1
-rw-r--r--src/mod_sip.erl59
-rw-r--r--src/mod_sip_opt.erl48
-rw-r--r--src/mod_sip_proxy.erl19
-rw-r--r--src/mod_sip_registrar.erl17
-rw-r--r--src/mod_stats.erl2
-rw-r--r--src/mod_stream_mgmt.erl74
-rw-r--r--src/mod_stream_mgmt_opt.erl55
-rw-r--r--src/mod_vcard.erl68
-rw-r--r--src/mod_vcard_ldap.erl166
-rw-r--r--src/mod_vcard_ldap_opt.erl125
-rw-r--r--src/mod_vcard_mnesia.erl20
-rw-r--r--src/mod_vcard_mnesia_opt.erl13
-rw-r--r--src/mod_vcard_opt.erl83
-rw-r--r--src/mod_vcard_sql.erl1
-rw-r--r--src/mod_vcard_xupdate.erl30
-rw-r--r--src/mod_vcard_xupdate_opt.erl34
-rw-r--r--src/mod_version.erl6
-rw-r--r--src/mod_version_opt.erl13
-rw-r--r--src/node_dag.erl2
-rw-r--r--src/node_flat_sql.erl3
-rw-r--r--src/node_pep_sql.erl1
-rw-r--r--src/nodetree_dag.erl14
-rw-r--r--src/nodetree_tree.erl6
-rw-r--r--src/nodetree_tree_sql.erl15
-rw-r--r--src/prosody2ejabberd.erl4
-rw-r--r--src/pubsub_db_sql.erl1
-rw-r--r--src/pubsub_migrate.erl2
-rw-r--r--src/pubsub_subscription.erl8
-rw-r--r--src/pubsub_subscription_sql.erl8
-rw-r--r--src/rest.erl23
-rw-r--r--src/str.erl2
-rw-r--r--src/translate.erl19
234 files changed, 11031 insertions, 9191 deletions
diff --git a/src/ELDAPv3.erl b/src/ELDAPv3.erl
index 494573164..3c102e7ec 100644
--- a/src/ELDAPv3.erl
+++ b/src/ELDAPv3.erl
@@ -3,6 +3,7 @@
-module('ELDAPv3').
-compile(nowarn_unused_vars).
+-dialyzer(no_match).
-include("ELDAPv3.hrl").
-asn1_info([{vsn,'2.0.1'},
{module,'ELDAPv3'},
@@ -349,7 +350,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'enc_ExtendedRequest'(element(2,Val), [<<119>>]);
extendedResp ->
'enc_ExtendedResponse'(element(2,Val), [<<120>>]);
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_type,Else}}})
end,
@@ -361,105 +362,105 @@ Tlv1 = match_tags(Tlv, TagIn),
case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of
%% 'bindRequest'
- {65536, V1} ->
+ {65536, V1} ->
{bindRequest, 'dec_BindRequest'(V1, [])};
%% 'bindResponse'
- {65537, V1} ->
+ {65537, V1} ->
{bindResponse, 'dec_BindResponse'(V1, [])};
%% 'unbindRequest'
- {65538, V1} ->
+ {65538, V1} ->
{unbindRequest, decode_null(V1,[])};
%% 'searchRequest'
- {65539, V1} ->
+ {65539, V1} ->
{searchRequest, 'dec_SearchRequest'(V1, [])};
%% 'searchResEntry'
- {65540, V1} ->
+ {65540, V1} ->
{searchResEntry, 'dec_SearchResultEntry'(V1, [])};
%% 'searchResDone'
- {65541, V1} ->
+ {65541, V1} ->
{searchResDone, 'dec_SearchResultDone'(V1, [])};
%% 'searchResRef'
- {65555, V1} ->
+ {65555, V1} ->
{searchResRef, 'dec_SearchResultReference'(V1, [])};
%% 'modifyRequest'
- {65542, V1} ->
+ {65542, V1} ->
{modifyRequest, 'dec_ModifyRequest'(V1, [])};
%% 'modifyResponse'
- {65543, V1} ->
+ {65543, V1} ->
{modifyResponse, 'dec_ModifyResponse'(V1, [])};
%% 'addRequest'
- {65544, V1} ->
+ {65544, V1} ->
{addRequest, 'dec_AddRequest'(V1, [])};
%% 'addResponse'
- {65545, V1} ->
+ {65545, V1} ->
{addResponse, 'dec_AddResponse'(V1, [])};
%% 'delRequest'
- {65546, V1} ->
+ {65546, V1} ->
{delRequest, decode_restricted_string(V1,[])};
%% 'delResponse'
- {65547, V1} ->
+ {65547, V1} ->
{delResponse, 'dec_DelResponse'(V1, [])};
%% 'modDNRequest'
- {65548, V1} ->
+ {65548, V1} ->
{modDNRequest, 'dec_ModifyDNRequest'(V1, [])};
%% 'modDNResponse'
- {65549, V1} ->
+ {65549, V1} ->
{modDNResponse, 'dec_ModifyDNResponse'(V1, [])};
%% 'compareRequest'
- {65550, V1} ->
+ {65550, V1} ->
{compareRequest, 'dec_CompareRequest'(V1, [])};
%% 'compareResponse'
- {65551, V1} ->
+ {65551, V1} ->
{compareResponse, 'dec_CompareResponse'(V1, [])};
%% 'abandonRequest'
- {65552, V1} ->
+ {65552, V1} ->
{abandonRequest, decode_integer(V1,{0,2147483647},[])};
%% 'extendedReq'
- {65559, V1} ->
+ {65559, V1} ->
{extendedReq, 'dec_ExtendedRequest'(V1, [])};
%% 'extendedResp'
- {65560, V1} ->
+ {65560, V1} ->
{extendedResp, 'dec_ExtendedResponse'(V1, [])};
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_tag,Else}}})
end
.
@@ -470,20 +471,20 @@ case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of
'dec_LDAPMessage'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute messageID(1) with type INTEGER
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_integer(V1,{0,2147483647},[2]),
%%-------------------------------------------------
%% attribute protocolOp(2) with type CHOICE
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_LDAPMessage_protocolOp'(V2, []),
%%-------------------------------------------------
@@ -639,7 +640,7 @@ decode_restricted_string(Tlv,TagIn).
{EncBytes,EncLen} = 'enc_AttributeDescriptionList_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) ->
+'enc_AttributeDescriptionList_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_AttributeDescriptionList_components'([H|T],AccBytes, AccLen) ->
@@ -653,7 +654,7 @@ decode_restricted_string(Tlv,TagIn).
'dec_AttributeDescriptionList'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -708,20 +709,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_AttributeValueAssertion'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute attributeDesc(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute assertionValue(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
case Tlv3 of
@@ -781,7 +782,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_Attribute_vals_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_Attribute_vals_components'([], AccBytes, AccLen) ->
+'enc_Attribute_vals_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_Attribute_vals_components'([H|T],AccBytes, AccLen) ->
@@ -790,7 +791,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_Attribute_vals'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -803,20 +804,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_Attribute'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute type(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute vals(2) with type SET OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_Attribute_vals'(V2, [17]),
case Tlv3 of
@@ -928,26 +929,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_LDAPResult'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute resultCode(1) with type ENUMERATED
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]),
%%-------------------------------------------------
%% attribute matchedDN(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
%%-------------------------------------------------
%% attribute errorMessage(3) with type OCTET STRING
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_restricted_string(V3,[4]),
%%-------------------------------------------------
@@ -977,7 +978,7 @@ end,
{EncBytes,EncLen} = 'enc_Referral_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_Referral_components'([], AccBytes, AccLen) ->
+'enc_Referral_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_Referral_components'([H|T],AccBytes, AccLen) ->
@@ -991,7 +992,7 @@ end,
'dec_Referral'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -1027,7 +1028,7 @@ decode_restricted_string(Tlv,TagIn).
{EncBytes,EncLen} = 'enc_Controls_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_Controls_components'([], AccBytes, AccLen) ->
+'enc_Controls_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_Controls_components'([H|T],AccBytes, AccLen) ->
@@ -1041,7 +1042,7 @@ decode_restricted_string(Tlv,TagIn).
'dec_Controls'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_Control'(V1, [16]) || V1 <- Tlv1].
@@ -1092,14 +1093,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_Control'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute controlType(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
@@ -1163,26 +1164,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_BindRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute version(1) with type INTEGER
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_integer(V1,{1,127},[2]),
%%-------------------------------------------------
%% attribute name(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
%%-------------------------------------------------
%% attribute authentication(3) External ELDAPv3:AuthenticationChoice
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = 'dec_AuthenticationChoice'(V3, []),
case Tlv4 of
@@ -1204,7 +1205,7 @@ end,
encode_restricted_string(element(2,Val), [<<128>>]);
sasl ->
'enc_SaslCredentials'(element(2,Val), [<<163>>]);
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_type,Else}}})
end,
@@ -1221,15 +1222,15 @@ Tlv1 = match_tags(Tlv, TagIn),
case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of
%% 'simple'
- {131072, V1} ->
+ {131072, V1} ->
{simple, decode_restricted_string(V1,[])};
%% 'sasl'
- {131075, V1} ->
+ {131075, V1} ->
{sasl, 'dec_SaslCredentials'(V1, [])};
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_tag,Else}}})
end
.
@@ -1268,14 +1269,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_SaslCredentials'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute mechanism(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
@@ -1388,26 +1389,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_BindResponse'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute resultCode(1) with type ENUMERATED
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]),
%%-------------------------------------------------
%% attribute matchedDN(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
%%-------------------------------------------------
%% attribute errorMessage(3) with type OCTET STRING
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_restricted_string(V3,[4]),
%%-------------------------------------------------
@@ -1525,56 +1526,56 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_SearchRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute baseObject(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute scope(2) with type ENUMERATED
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_enumerated(V2,[{baseObject,0},{singleLevel,1},{wholeSubtree,2}],[10]),
%%-------------------------------------------------
%% attribute derefAliases(3) with type ENUMERATED
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_enumerated(V3,[{neverDerefAliases,0},{derefInSearching,1},{derefFindingBaseObj,2},{derefAlways,3}],[10]),
%%-------------------------------------------------
%% attribute sizeLimit(4) with type INTEGER
%%-------------------------------------------------
-[V4|Tlv5] = Tlv4,
+[V4|Tlv5] = Tlv4,
Term4 = decode_integer(V4,{0,2147483647},[2]),
%%-------------------------------------------------
%% attribute timeLimit(5) with type INTEGER
%%-------------------------------------------------
-[V5|Tlv6] = Tlv5,
+[V5|Tlv6] = Tlv5,
Term5 = decode_integer(V5,{0,2147483647},[2]),
%%-------------------------------------------------
%% attribute typesOnly(6) with type BOOLEAN
%%-------------------------------------------------
-[V6|Tlv7] = Tlv6,
+[V6|Tlv7] = Tlv6,
Term6 = decode_boolean(V6,[1]),
%%-------------------------------------------------
%% attribute filter(7) External ELDAPv3:Filter
%%-------------------------------------------------
-[V7|Tlv8] = Tlv7,
+[V7|Tlv8] = Tlv7,
Term7 = 'dec_Filter'(V7, []),
%%-------------------------------------------------
%% attribute attributes(8) External ELDAPv3:AttributeDescriptionList
%%-------------------------------------------------
-[V8|Tlv9] = Tlv8,
+[V8|Tlv9] = Tlv8,
Term8 = 'dec_AttributeDescriptionList'(V8, [16]),
case Tlv9 of
@@ -1612,7 +1613,7 @@ end,
'enc_AttributeValueAssertion'(element(2,Val), [<<168>>]);
extensibleMatch ->
'enc_MatchingRuleAssertion'(element(2,Val), [<<169>>]);
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_type,Else}}})
end,
@@ -1629,7 +1630,7 @@ encode_tags(TagIn, EncBytes, EncLen).
{EncBytes,EncLen} = 'enc_Filter_and_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_Filter_and_components'([], AccBytes, AccLen) ->
+'enc_Filter_and_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_Filter_and_components'([H|T],AccBytes, AccLen) ->
@@ -1638,7 +1639,7 @@ encode_tags(TagIn, EncBytes, EncLen).
'dec_Filter_and'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_Filter'(V1, []) || V1 <- Tlv1].
@@ -1654,7 +1655,7 @@ Tlv1 = match_tags(Tlv, TagIn),
{EncBytes,EncLen} = 'enc_Filter_or_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_Filter_or_components'([], AccBytes, AccLen) ->
+'enc_Filter_or_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_Filter_or_components'([H|T],AccBytes, AccLen) ->
@@ -1663,7 +1664,7 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_Filter_or'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_Filter'(V1, []) || V1 <- Tlv1].
@@ -1679,55 +1680,55 @@ Tlv1 = match_tags(Tlv, TagIn),
case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of
%% 'and'
- {131072, V1} ->
+ {131072, V1} ->
{'and', 'dec_Filter_and'(V1, [])};
%% 'or'
- {131073, V1} ->
+ {131073, V1} ->
{'or', 'dec_Filter_or'(V1, [])};
%% 'not'
- {131074, V1} ->
+ {131074, V1} ->
{'not', 'dec_Filter'(V1, [])};
%% 'equalityMatch'
- {131075, V1} ->
+ {131075, V1} ->
{equalityMatch, 'dec_AttributeValueAssertion'(V1, [])};
%% 'substrings'
- {131076, V1} ->
+ {131076, V1} ->
{substrings, 'dec_SubstringFilter'(V1, [])};
%% 'greaterOrEqual'
- {131077, V1} ->
+ {131077, V1} ->
{greaterOrEqual, 'dec_AttributeValueAssertion'(V1, [])};
%% 'lessOrEqual'
- {131078, V1} ->
+ {131078, V1} ->
{lessOrEqual, 'dec_AttributeValueAssertion'(V1, [])};
%% 'present'
- {131079, V1} ->
+ {131079, V1} ->
{present, decode_restricted_string(V1,[])};
%% 'approxMatch'
- {131080, V1} ->
+ {131080, V1} ->
{approxMatch, 'dec_AttributeValueAssertion'(V1, [])};
%% 'extensibleMatch'
- {131081, V1} ->
+ {131081, V1} ->
{extensibleMatch, 'dec_MatchingRuleAssertion'(V1, [])};
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_tag,Else}}})
end
.
@@ -1765,7 +1766,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_SubstringFilter_substrings_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) ->
+'enc_SubstringFilter_substrings_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_SubstringFilter_substrings_components'([H|T],AccBytes, AccLen) ->
@@ -1786,7 +1787,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
encode_restricted_string(element(2,Val), [<<129>>]);
final ->
encode_restricted_string(element(2,Val), [<<130>>]);
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_type,Else}}})
end,
@@ -1798,26 +1799,26 @@ Tlv1 = match_tags(Tlv, TagIn),
case (case Tlv1 of [CtempTlv1] -> CtempTlv1; _ -> Tlv1 end) of
%% 'initial'
- {131072, V1} ->
+ {131072, V1} ->
{initial, decode_restricted_string(V1,[])};
%% 'any'
- {131073, V1} ->
+ {131073, V1} ->
{any, decode_restricted_string(V1,[])};
%% 'final'
- {131074, V1} ->
+ {131074, V1} ->
{final, decode_restricted_string(V1,[])};
- Else ->
+ Else ->
exit({error,{asn1,{invalid_choice_tag,Else}}})
end
.
'dec_SubstringFilter_substrings'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_SubstringFilter_substrings_SEQOF'(V1, []) || V1 <- Tlv1].
@@ -1830,20 +1831,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_SubstringFilter'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute type(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute substrings(2) with type SEQUENCE OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_SubstringFilter_substrings'(V2, [16]),
case Tlv3 of
@@ -1905,7 +1906,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_MatchingRuleAssertion'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
@@ -1932,7 +1933,7 @@ end,
%%-------------------------------------------------
%% attribute matchValue(3) with type OCTET STRING
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_restricted_string(V3,[131075]),
%%-------------------------------------------------
@@ -1981,20 +1982,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_SearchResultEntry'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute objectName(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute attributes(2) External ELDAPv3:PartialAttributeList
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_PartialAttributeList'(V2, [16]),
case Tlv3 of
@@ -2014,7 +2015,7 @@ end,
{EncBytes,EncLen} = 'enc_PartialAttributeList_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_PartialAttributeList_components'([], AccBytes, AccLen) ->
+'enc_PartialAttributeList_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_PartialAttributeList_components'([H|T],AccBytes, AccLen) ->
@@ -2053,7 +2054,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_PartialAttributeList_SEQOF_vals_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
+'enc_PartialAttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_PartialAttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) ->
@@ -2062,7 +2063,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_PartialAttributeList_SEQOF_vals'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -2070,20 +2071,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_PartialAttributeList_SEQOF'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute type(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute vals(2) with type SET OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_PartialAttributeList_SEQOF_vals'(V2, [17]),
case Tlv3 of
@@ -2098,7 +2099,7 @@ end,
'dec_PartialAttributeList'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_PartialAttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1].
@@ -2116,7 +2117,7 @@ Tlv1 = match_tags(Tlv, TagIn),
{EncBytes,EncLen} = 'enc_SearchResultReference_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_SearchResultReference_components'([], AccBytes, AccLen) ->
+'enc_SearchResultReference_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_SearchResultReference_components'([H|T],AccBytes, AccLen) ->
@@ -2130,7 +2131,7 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_SearchResultReference'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -2188,7 +2189,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_ModifyRequest_modification_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) ->
+'enc_ModifyRequest_modification_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_ModifyRequest_modification_components'([H|T],AccBytes, AccLen) ->
@@ -2224,20 +2225,20 @@ LenSoFar = EncLen1 + EncLen2,
encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_ModifyRequest_modification_SEQOF'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute operation(1) with type ENUMERATED
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_enumerated(V1,[{add,0},{delete,1},{replace,2}],[10]),
%%-------------------------------------------------
%% attribute modification(2) External ELDAPv3:AttributeTypeAndValues
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_AttributeTypeAndValues'(V2, [16]),
case Tlv3 of
@@ -2247,7 +2248,7 @@ end,
'dec_ModifyRequest_modification'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_ModifyRequest_modification_SEQOF'(V1, [16]) || V1 <- Tlv1].
@@ -2260,20 +2261,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_ModifyRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute object(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute modification(2) with type SEQUENCE OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_ModifyRequest_modification'(V2, [16]),
case Tlv3 of
@@ -2315,7 +2316,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_AttributeTypeAndValues_vals_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) ->
+'enc_AttributeTypeAndValues_vals_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_AttributeTypeAndValues_vals_components'([H|T],AccBytes, AccLen) ->
@@ -2324,7 +2325,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_AttributeTypeAndValues_vals'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -2337,20 +2338,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_AttributeTypeAndValues'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute type(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute vals(2) with type SET OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_AttributeTypeAndValues_vals'(V2, [17]),
case Tlv3 of
@@ -2407,20 +2408,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_AddRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute entry(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute attributes(2) External ELDAPv3:AttributeList
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_AttributeList'(V2, [16]),
case Tlv3 of
@@ -2440,7 +2441,7 @@ end,
{EncBytes,EncLen} = 'enc_AttributeList_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_AttributeList_components'([], AccBytes, AccLen) ->
+'enc_AttributeList_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_AttributeList_components'([H|T],AccBytes, AccLen) ->
@@ -2479,7 +2480,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
{EncBytes,EncLen} = 'enc_AttributeList_SEQOF_vals_components'(Val,[],0),
encode_tags(TagIn, EncBytes, EncLen).
-'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
+'enc_AttributeList_SEQOF_vals_components'([], AccBytes, AccLen) ->
{lists:reverse(AccBytes),AccLen};
'enc_AttributeList_SEQOF_vals_components'([H|T],AccBytes, AccLen) ->
@@ -2488,7 +2489,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_AttributeList_SEQOF_vals'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
[decode_restricted_string(V1,[4]) || V1 <- Tlv1].
@@ -2496,20 +2497,20 @@ Tlv1 = match_tags(Tlv, TagIn),
'dec_AttributeList_SEQOF'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute type(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute vals(2) with type SET OF
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_AttributeList_SEQOF_vals'(V2, [17]),
case Tlv3 of
@@ -2524,7 +2525,7 @@ end,
'dec_AttributeList'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
['dec_AttributeList_SEQOF'(V1, [16]) || V1 <- Tlv1].
@@ -2629,26 +2630,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_ModifyDNRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute entry(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute newrdn(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
%%-------------------------------------------------
%% attribute deleteoldrdn(3) with type BOOLEAN
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_boolean(V3,[1]),
%%-------------------------------------------------
@@ -2715,20 +2716,20 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_CompareRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute entry(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[4]),
%%-------------------------------------------------
%% attribute ava(2) External ELDAPv3:AttributeValueAssertion
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = 'dec_AttributeValueAssertion'(V2, [16]),
case Tlv3 of
@@ -2807,14 +2808,14 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_ExtendedRequest'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute requestName(1) with type OCTET STRING
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_restricted_string(V1,[131072]),
%%-------------------------------------------------
@@ -2936,26 +2937,26 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_ExtendedResponse'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
%%-------------------------------------------------
%% attribute resultCode(1) with type ENUMERATED
%%-------------------------------------------------
-[V1|Tlv2] = Tlv1,
+[V1|Tlv2] = Tlv1,
Term1 = decode_enumerated(V1,[{success,0},{operationsError,1},{protocolError,2},{timeLimitExceeded,3},{sizeLimitExceeded,4},{compareFalse,5},{compareTrue,6},{authMethodNotSupported,7},{strongAuthRequired,8},{referral,10},{adminLimitExceeded,11},{unavailableCriticalExtension,12},{confidentialityRequired,13},{saslBindInProgress,14},{noSuchAttribute,16},{undefinedAttributeType,17},{inappropriateMatching,18},{constraintViolation,19},{attributeOrValueExists,20},{invalidAttributeSyntax,21},{noSuchObject,32},{aliasProblem,33},{invalidDNSyntax,34},{aliasDereferencingProblem,36},{inappropriateAuthentication,48},{invalidCredentials,49},{insufficientAccessRights,50},{busy,51},{unavailable,52},{unwillingToPerform,53},{loopDetect,54},{namingViolation,64},{objectClassViolation,65},{notAllowedOnNonLeaf,66},{notAllowedOnRDN,67},{entryAlreadyExists,68},{objectClassModsProhibited,69},{affectsMultipleDSAs,71},{other,80}],[10]),
%%-------------------------------------------------
%% attribute matchedDN(2) with type OCTET STRING
%%-------------------------------------------------
-[V2|Tlv3] = Tlv2,
+[V2|Tlv3] = Tlv2,
Term2 = decode_restricted_string(V2,[4]),
%%-------------------------------------------------
%% attribute errorMessage(3) with type OCTET STRING
%%-------------------------------------------------
-[V3|Tlv4] = Tlv3,
+[V3|Tlv4] = Tlv3,
Term3 = decode_restricted_string(V3,[4]),
%%-------------------------------------------------
@@ -3041,7 +3042,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_PasswdModifyRequestValue'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
@@ -3110,7 +3111,7 @@ encode_tags(TagIn, BytesSoFar, LenSoFar).
'dec_PasswdModifyResponseValue'(Tlv, TagIn) ->
%%-------------------------------------------------
- %% decode tag and length
+ %% decode tag and length
%%-------------------------------------------------
Tlv1 = match_tags(Tlv, TagIn),
diff --git a/src/acl.erl b/src/acl.erl
index 959faeaf0..73dd5000a 100644
--- a/src/acl.erl
+++ b/src/acl.erl
@@ -1,10 +1,4 @@
%%%----------------------------------------------------------------------
-%%% File : acl.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : ACL support
-%%% Created : 18 Jan 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
@@ -22,743 +16,347 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-
-module(acl).
-
-behaviour(gen_server).
--behaviour(ejabberd_config).
-
--author('alexey@process-one.net').
-
--export([add_access/3, clear/0]).
--export([start_link/0, add/3, add_list/3, add_local/3, add_list_local/3,
- load_from_config/0, reload_from_config/0, match_rule/3,
- any_rules_allowed/3, transform_options/1, opt_type/1,
- acl_rule_matches/3, acl_rule_verify/1, access_matches/3,
- transform_access_rules_config/1,
- parse_ip_netmask/1, ip_matches_mask/3,
- access_rules_validator/1, shaper_rules_validator/1,
- normalize_spec/1, resolve_access/2]).
+
+-export([start_link/0]).
+-export([reload_from_config/0]).
+-export([match_rule/3, match_acl/3]).
+-export([match_rules/4, match_acls/3]).
+-export([access_rules_validator/0, access_validator/0]).
+-export([validator/1, validators/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("logger.hrl").
--include("jid.hrl").
-
--record(acl, {aclname, aclspec}).
--record(access, {name :: aclname(),
- rules = [] :: [access_rule()]}).
--record(state, {}).
-
--type regexp() :: binary().
--type iprange() :: {inet:ip_address(), integer()} | binary().
--type glob() :: binary().
--type access_name() :: atom().
--type access_rule() :: {atom(), any()}.
--type host() :: binary().
--type aclname() :: {atom(), binary() | global}.
--type aclspec() :: all | none |
- {user, {binary(), host()} | binary()} |
- {server, binary()} |
- {resource, binary()} |
- {user_regexp, {regexp(), host()} | regexp()} |
- {shared_group, {binary(), host()} | binary()} |
- {user_regexp, {regexp(), host()} | regexp()} |
- {server_regexp, regexp()} |
- {resource_regexp, regexp()} |
- {node_regexp, {regexp(), regexp()}} |
- {user_glob, {glob(), host()} | glob()} |
- {server_glob, glob()} |
- {resource_glob, glob()} |
- {ip, iprange()} |
- {node_glob, {glob(), glob()}}.
-
--type acl() :: #acl{aclname :: aclname(),
- aclspec :: aclspec()}.
-
--export_type([acl/0]).
+-type state() :: #{hosts := [binary()]}.
+-type action() :: allow | deny.
+-type ip_mask() :: {inet:ip4_address(), 0..32} | {inet:ip6_address(), 0..128}.
+-type access_rule() :: {acl, atom()} | acl_rule().
+-type acl_rule() :: {user, {binary(), binary()} | binary()} |
+ {server, binary()} |
+ {resource, binary()} |
+ {user_regexp, {re:mp(), binary()} | re:mp()} |
+ {server_regexp, re:mp()} |
+ {resource_regexp, re:mp()} |
+ {node_regexp, {re:mp(), re:mp()}} |
+ {user_glob, {re:mp(), binary()} | re:mp()} |
+ {server_glob, re:mp()} |
+ {resource_glob, re:mp()} |
+ {node_glob, {re:mp(), re:mp()}} |
+ {shared_group, {binary(), binary()} | binary()} |
+ {ip, ip_mask()}.
+-type access() :: [{action(), [access_rule()]}].
+-type acl() :: atom() | access().
+-type match() :: #{ip => inet:ip_address(),
+ usr => jid:ljid(),
+ atom() => term()}.
+
+-export_type([acl/0, acl_rule/0, access/0, access_rule/0, match/0]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-init([]) ->
- ejabberd_mnesia:create(?MODULE, acl,
- [{ram_copies, [node()]}, {type, bag},
- {local_content, true},
- {attributes, record_info(fields, acl)}]),
- ejabberd_mnesia:create(?MODULE, access,
- [{ram_copies, [node()]},
- {local_content, true},
- {attributes, record_info(fields, access)}]),
- ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20),
- load_from_config(),
- {ok, #state{}}.
-
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
-handle_cast(_Msg, State) ->
- {noreply, State}.
-
-handle_info(_Info, State) ->
- {noreply, State}.
-
-terminate(_Reason, _State) ->
- ok.
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
--spec add(binary(), aclname(), aclspec()) -> ok | {error, any()}.
-
-add(Host, ACLName, ACLSpec) ->
- {ResL, BadNodes} = ejabberd_cluster:multicall(
- ?MODULE, add_local,
- [Host, ACLName, ACLSpec]),
- case lists:keyfind(aborted, 1, ResL) of
- false when BadNodes == [] ->
- ok;
- false ->
- {error, {failed_nodes, BadNodes}};
- Err ->
- {error, Err}
- end.
-
-add_local(Host, ACLName, ACLSpec) ->
- F = fun () ->
- mnesia:write(#acl{aclname = {ACLName, Host},
- aclspec = normalize_spec(ACLSpec)})
- end,
- case mnesia:transaction(F) of
- {atomic, ok} ->
- ok;
- Err ->
- Err
- end.
-
--spec add_list(binary(), [acl()], boolean()) -> ok | {error, any()}.
-
-add_list(Host, ACLs, Clear) ->
- {ResL, BadNodes} = ejabberd_cluster:multicall(
- ?MODULE, add_list_local,
- [Host, ACLs, Clear]),
- case lists:keyfind(aborted, 1, ResL) of
- false when BadNodes == [] ->
- ok;
- false ->
- {error, {failed_nodes, BadNodes}};
- Err ->
- {error, Err}
- end.
-
-add_list_local(Host, ACLs, Clear) ->
- F = fun () ->
- if Clear ->
- Ks = mnesia:select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- ['$1']}]),
- lists:foreach(fun (K) -> mnesia:delete({acl, {K, Host}})
- end,
- Ks);
- true -> ok
- end,
- lists:foreach(fun (ACL) ->
- case ACL of
- #acl{aclname = ACLName,
- aclspec = ACLSpec} ->
- mnesia:write(#acl{aclname =
- {ACLName,
- Host},
- aclspec =
- normalize_spec(ACLSpec)})
- end
- end,
- ACLs)
- end,
- mnesia:transaction(F).
-
--spec add_access(binary() | global,
- access_name(), [access_rule()]) -> ok | {error, any()}.
-
-add_access(Host, Access, Rules) ->
- Obj = #access{name = {Access, Host}, rules = Rules},
- case mnesia:transaction(fun() -> mnesia:write(Obj) end) of
- {atomic, ok} ->
- ok;
- Err ->
- {error, Err}
- end.
-
--spec load_from_config() -> ok.
-
-load_from_config() ->
- Hosts = [global|ejabberd_config:get_myhosts()],
- lists:foreach(
- fun(Host) ->
- ACLs = ejabberd_config:get_option(
- {acl, Host}, []),
- AccessRules = ejabberd_config:get_option(
- {access, Host}, []),
- AccessRulesNew = ejabberd_config:get_option(
- {access_rules, Host}, []),
- ShaperRules = ejabberd_config:get_option(
- {shaper_rules, Host}, []),
- lists:foreach(
- fun({ACLName, SpecList}) ->
- lists:foreach(
- fun({ACLType, ACLSpecs}) when is_list(ACLSpecs) ->
- lists:foreach(
- fun(ACLSpec) ->
- add(Host, ACLName,
- {ACLType, ACLSpec})
- end, lists:flatten(ACLSpecs));
- ({ACLType, ACLSpecs}) ->
- add(Host, ACLName, {ACLType, ACLSpecs})
- end, lists:flatten(SpecList))
- end, ACLs),
- lists:foreach(
- fun({Access, Rules}) ->
- NRules = lists:map(fun({ACL, Type}) ->
- {Type, [{acl, ACL}]}
- end, Rules),
- add_access(Host, Access, NRules ++ [{deny, [all]}])
- end, AccessRules),
- lists:foreach(
- fun({Access, Rules}) ->
- add_access(Host, Access, Rules)
- end, AccessRulesNew),
- lists:foreach(
- fun({Access, Rules}) ->
- add_access(Host, Access, Rules)
- end, ShaperRules)
- end, Hosts).
-
--spec reload_from_config() -> ok.
-
-reload_from_config() ->
- clear(),
- load_from_config().
-
-%% Delete all previous set ACLs and Access rules
-clear() ->
- mnesia:clear_table(acl),
- mnesia:clear_table(access),
- ok.
-
-b(S) ->
- iolist_to_binary(S).
-
-nodeprep(S) ->
- jid:nodeprep(b(S)).
-
-nameprep(S) ->
- jid:nameprep(b(S)).
-
-resourceprep(S) ->
- jid:resourceprep(b(S)).
-
-split_user_server(Str, NormFunUsr, NormFunSrv) ->
- case binary:split(Str, <<"@">>) of
- [U, S] ->
- {NormFunUsr(U), NormFunSrv(S)};
- _ ->
- NormFunUsr(Str)
- end.
-
-normalize_spec(Spec) ->
- case Spec of
- all -> all;
- none -> none;
- {acl, N} when is_atom(N) ->
- {acl, N};
- {user, {U, S}} when is_binary(U), is_binary(S) ->
- {user, {nodeprep(U), nameprep(S)}};
- {user, U} when is_binary(U) ->
- {user, split_user_server(U, fun nodeprep/1, fun nameprep/1)};
- {shared_group, {G, H}} when is_binary(G), is_binary(H) ->
- {shared_group, {b(G), nameprep(H)}};
- {shared_group, G} when is_binary(G) ->
- {shared_group, split_user_server(G, fun b/1, fun nameprep/1)};
- {user_regexp, {UR, S}} when is_binary(UR), is_binary(S) ->
- {user_regexp, {b(UR), nameprep(S)}};
- {user_regexp, UR} when is_binary(UR) ->
- {user_regexp, split_user_server(UR, fun b/1, fun nameprep/1)};
- {node_regexp, {UR, SR}} when is_binary(UR), is_binary(SR) ->
- {node_regexp, {b(UR), b(SR)}};
- {user_glob, {UR, S}} when is_binary(UR), is_binary(S) ->
- {user_glob, {b(UR), nameprep(S)}};
- {user_glob, UR} when is_binary(UR) ->
- {user_glob, split_user_server(UR, fun b/1, fun nameprep/1)};
- {node_glob, {UR, SR}} when is_binary(UR), is_binary(SR) ->
- {node_glob, {b(UR), b(SR)}};
- {server, S} when is_binary(S) ->
- {server, nameprep(S)};
- {resource, R} when is_binary(R) ->
- {resource, resourceprep(R)};
- {server_regexp, SR} when is_binary(SR) ->
- {server_regexp, b(SR)};
- {resource_regexp, R} when is_binary(R) ->
- {resource_regexp, b(R)};
- {server_glob, S} when is_binary(S) ->
- {server_glob, b(S)};
- {resource_glob, R} when is_binary(R) ->
- {resource_glob, b(R)};
- {ip, {Net, Mask}} when is_binary(Net), is_integer(Mask) ->
- {ip, {Net, Mask}};
- {ip, S} ->
- case parse_ip_netmask(b(S)) of
- {ok, Net, Mask} ->
- {ip, {Net, Mask}};
- error ->
- ?WARNING_MSG("Invalid network address: ~p", [S]),
- none
- end;
- BadVal ->
- throw({<<"Invalid acl value">>, BadVal})
- end.
-
--spec any_rules_allowed(global | binary(), [access_name()],
- jid() | ljid() | inet:ip_address()) -> boolean().
-
-any_rules_allowed(Host, Access, Entity) ->
- lists:any(fun (Rule) ->
- allow == acl:match_rule(Host, Rule, Entity)
- end,
- Access).
-
--spec match_rule(global | binary(), access_name(),
- jid() | ljid() | inet:ip_address()) -> any().
-
-match_rule(Host, Access, IP) when tuple_size(IP) == 4;
- tuple_size(IP) == 8 ->
- access_matches(Access, #{ip => IP}, Host);
+-spec match_rule(global | binary(), atom() | access(),
+ jid:jid() | jid:ljid() | inet:ip_address() | match()) -> action().
+match_rule(_, all, _) ->
+ allow;
+match_rule(_, none, _) ->
+ deny;
+match_rule(Host, Access, Match) when is_map(Match) ->
+ Rules = if is_atom(Access) -> read_access(Access, Host);
+ true -> Access
+ end,
+ match_rules(Host, Rules, Match, deny);
+match_rule(Host, Access, IP) when tuple_size(IP) == 4; tuple_size(IP) == 8 ->
+ match_rule(Host, Access, #{ip => IP});
match_rule(Host, Access, JID) ->
- access_matches(Access, #{usr => jid:tolower(JID)}, Host).
+ match_rule(Host, Access, #{usr => jid:tolower(JID)}).
--spec acl_rule_verify(aclspec()) -> boolean().
-
-acl_rule_verify(all) ->
- true;
-acl_rule_verify(none) ->
- true;
-acl_rule_verify({ip, {{A,B,C,D}, Mask}})
- when is_integer(A), is_integer(B), is_integer(C), is_integer(D),
- A >= 0, A =< 255, B >= 0, B =< 255, C >= 0, C =< 255, D >= 0, D =< 255,
- is_integer(Mask), Mask >= 0, Mask =< 32 ->
- true;
-acl_rule_verify({ip, {{A,B,C,D,E,F,G,H}, Mask}}) when
- is_integer(A), is_integer(B), is_integer(C), is_integer(D),
- is_integer(E), is_integer(F), is_integer(G), is_integer(H),
- A >= 0, A =< 65535, B >= 0, B =< 65535, C >= 0, C =< 65535, D >= 0, D =< 65535,
- E >= 0, E =< 65535, F >= 0, F =< 65535, G >= 0, G =< 65535, H >= 0, H =< 65535,
- is_integer(Mask), Mask >= 0, Mask =< 64 ->
- true;
-acl_rule_verify({user, {U, S}}) when is_binary(U), is_binary(S) ->
- true;
-acl_rule_verify({user, U}) when is_binary(U) ->
- true;
-acl_rule_verify({server, S}) when is_binary(S) ->
- true;
-acl_rule_verify({resource, R}) when is_binary(R) ->
- true;
-acl_rule_verify({shared_group, {G, H}}) when is_binary(G), is_binary(H) ->
- true;
-acl_rule_verify({shared_group, G}) when is_binary(G) ->
- true;
-acl_rule_verify({user_regexp, {UR, S}}) when is_binary(UR), is_binary(S) ->
- true;
-acl_rule_verify({user_regexp, UR}) when is_binary(UR) ->
+-spec match_acl(global | binary(), access_rule(), match()) -> boolean().
+match_acl(_Host, {acl, all}, _) ->
true;
-acl_rule_verify({server_regexp, SR}) when is_binary(SR) ->
- true;
-acl_rule_verify({resource_regexp, RR}) when is_binary(RR) ->
- true;
-acl_rule_verify({node_regexp, {UR, SR}}) when is_binary(UR), is_binary(SR) ->
- true;
-acl_rule_verify({user_glob, {UR, S}}) when is_binary(UR), is_binary(S) ->
- true;
-acl_rule_verify({user_glob, UR}) when is_binary(UR) ->
- true;
-acl_rule_verify({server_glob, SR}) when is_binary(SR) ->
+match_acl(_Host, {acl, none}, _) ->
+ false;
+match_acl(Host, {acl, ACLName}, Match) ->
+ lists:any(
+ fun(ACL) ->
+ match_acl(Host, ACL, Match)
+ end, read_acl(ACLName, Host));
+match_acl(_Host, {ip, {Net, Mask}}, #{ip := {IP, _Port}}) ->
+ misc:match_ip_mask(IP, Net, Mask);
+match_acl(_Host, {ip, {Net, Mask}}, #{ip := IP}) ->
+ misc:match_ip_mask(IP, Net, Mask);
+match_acl(_Host, {user, {U, S}}, #{usr := {U, S, _}}) ->
true;
-acl_rule_verify({resource_glob, RR}) when is_binary(RR) ->
+match_acl(_Host, {user, U}, #{usr := {U, S, _}}) ->
+ ejabberd_router:is_my_host(S);
+match_acl(_Host, {server, S}, #{usr := {_, S, _}}) ->
true;
-acl_rule_verify({node_glob, {UR, SR}}) when is_binary(UR), is_binary(SR) ->
+match_acl(_Host, {resource, R}, #{usr := {_, _, R}}) ->
true;
-acl_rule_verify(_Spec) ->
- false.
-invalid_syntax(Msg, Data) ->
- throw({invalid_syntax, (str:format(Msg, Data))}).
-
-acl_rules_verify([{acl, Name} | Rest], true) when is_atom(Name) ->
- acl_rules_verify(Rest, true);
-acl_rules_verify([{acl, Name} = Rule | _Rest], false) when is_atom(Name) ->
- invalid_syntax(<<"Using acl: rules not allowed: ~p">>, [Rule]);
-acl_rules_verify([Rule | Rest], AllowAcl) ->
- case acl_rule_verify(Rule) of
- false ->
- invalid_syntax(<<"Invalid rule: ~p">>, [Rule]);
- true ->
- acl_rules_verify(Rest, AllowAcl)
+match_acl(_Host, {shared_group, {G, H}}, #{usr := {U, S, _}}) ->
+ case loaded_shared_roster_module(H) of
+ undefined -> false;
+ Mod -> Mod:is_user_in_group({U, S}, G, H)
end;
-acl_rules_verify([], _AllowAcl) ->
- true;
-acl_rules_verify(Rules, _AllowAcl) ->
- invalid_syntax(<<"Not a acl rules list: ~p">>, [Rules]).
-
-
-
-all_acl_rules_matches([], _Data, _Host) ->
- false;
-all_acl_rules_matches(Rules, Data, Host) ->
- all_acl_rules_matches2(Rules, Data, Host).
+match_acl(Host, {shared_group, G}, Map) ->
+ match_acl(Host, {shared_group, {G, Host}}, Map);
+match_acl(_Host, {user_regexp, {UR, S}}, #{usr := {U, S, _}}) ->
+ match_regexp(U, UR);
+match_acl(_Host, {user_regexp, UR}, #{usr := {U, S, _}}) ->
+ ejabberd_router:is_my_host(S) andalso match_regexp(U, UR);
+match_acl(_Host, {server_regexp, SR}, #{usr := {_, S, _}}) ->
+ match_regexp(S, SR);
+match_acl(_Host, {resource_regexp, RR}, #{usr := {_, _, R}}) ->
+ match_regexp(R, RR);
+match_acl(_Host, {node_regexp, {UR, SR}}, #{usr := {U, S, _}}) ->
+ match_regexp(U, UR) andalso match_regexp(S, SR);
+match_acl(_Host, {user_glob, {UR, S}}, #{usr := {U, S, _}}) ->
+ match_regexp(U, UR);
+match_acl(_Host, {user_glob, UR}, #{usr := {U, S, _}}) ->
+ ejabberd_router:is_my_host(S) andalso match_regexp(U, UR);
+match_acl(_Host, {server_glob, SR}, #{usr := {_, S, _}}) ->
+ match_regexp(S, SR);
+match_acl(_Host, {resource_glob, RR}, #{usr := {_, _, R}}) ->
+ match_regexp(R, RR);
+match_acl(_Host, {node_glob, {UR, SR}}, #{usr := {U, S, _}}) ->
+ match_regexp(U, UR) andalso match_regexp(S, SR);
+match_acl(_, _, _) ->
+ false.
-all_acl_rules_matches2([Rule | Tail], Data, Host) ->
- case acl_rule_matches(Rule, Data, Host) of
- true ->
- all_acl_rules_matches2(Tail, Data, Host);
+-spec match_rules(global | binary(), [{T, [access_rule()]}], match(), T) -> T.
+match_rules(Host, [{Return, Rules} | Rest], Match, Default) ->
+ case match_acls(Host, Rules, Match) of
false ->
- false
+ match_rules(Host, Rest, Match, Default);
+ true ->
+ Return
end;
-all_acl_rules_matches2([], _Data, _Host) ->
- true.
+match_rules(_Host, [], _Match, Default) ->
+ Default.
-any_acl_rules_matches([], _Data, _Host) ->
+-spec match_acls(global | binary(), [access_rule()], match()) -> boolean().
+match_acls(_Host, [], _Match) ->
false;
-any_acl_rules_matches([Rule|Tail], Data, Host) ->
- case acl_rule_matches(Rule, Data, Host) of
- true ->
- true;
- false ->
- any_acl_rules_matches(Tail, Data, Host)
- end.
-
--spec acl_rule_matches(aclspec(), any(), global|binary()) -> boolean().
+match_acls(Host, Rules, Match) ->
+ lists:all(
+ fun(Rule) ->
+ match_acl(Host, Rule, Match)
+ end, Rules).
-acl_rule_matches(all, _Data, _Host) ->
- true;
-acl_rule_matches({acl, all}, _Data, _Host) ->
- true;
-acl_rule_matches({acl, Name}, Data, Host) ->
- ACLs = get_aclspecs(Name, Host),
- RawACLs = lists:map(fun(#acl{aclspec = R}) -> R end, ACLs),
- any_acl_rules_matches(RawACLs, Data, Host);
-acl_rule_matches({ip, {Net, Mask}}, #{ip := {IP, _Port}}, _Host) ->
- ip_matches_mask(IP, Net, Mask);
-acl_rule_matches({ip, {Net, Mask}}, #{ip := IP}, _Host) ->
- ip_matches_mask(IP, Net, Mask);
-acl_rule_matches({user, {U, S}}, #{usr := {U, S, _}}, _Host) ->
- true;
-acl_rule_matches({user, U}, #{usr := {U, S, _}}, _Host) ->
- lists:member(S, ejabberd_config:get_myhosts());
-acl_rule_matches({server, S}, #{usr := {_, S, _}}, _Host) ->
- true;
-acl_rule_matches({resource, R}, #{usr := {_, _, R}}, _Host) ->
- true;
-acl_rule_matches({shared_group, {G, H}}, #{usr := {U, S, _}}, _Host) ->
- Mod = loaded_shared_roster_module(H),
- Mod:is_user_in_group({U, S}, G, H);
-acl_rule_matches({shared_group, G}, #{usr := {U, S, _}}, Host) ->
- Mod = loaded_shared_roster_module(Host),
- Mod:is_user_in_group({U, S}, G, Host);
-acl_rule_matches({user_regexp, {UR, S}}, #{usr := {U, S, _}}, _Host) ->
- is_regexp_match(U, UR);
-acl_rule_matches({user_regexp, UR}, #{usr := {U, S, _}}, _Host) ->
- lists:member(S, ejabberd_config:get_myhosts()) andalso is_regexp_match(U, UR);
-acl_rule_matches({server_regexp, SR}, #{usr := {_, S, _}}, _Host) ->
- is_regexp_match(S, SR);
-acl_rule_matches({resource_regexp, RR}, #{usr := {_, _, R}}, _Host) ->
- is_regexp_match(R, RR);
-acl_rule_matches({node_regexp, {UR, SR}}, #{usr := {U, S, _}}, _Host) ->
- is_regexp_match(U, UR) andalso is_regexp_match(S, SR);
-acl_rule_matches({user_glob, {UR, S}}, #{usr := {U, S, _}}, _Host) ->
- is_glob_match(U, UR);
-acl_rule_matches({user_glob, UR}, #{usr := {U, S, _}}, _Host) ->
- lists:member(S, ejabberd_config:get_myhosts()) andalso is_glob_match(U, UR);
-acl_rule_matches({server_glob, SR}, #{usr := {_, S, _}}, _Host) ->
- is_glob_match(S, SR);
-acl_rule_matches({resource_glob, RR}, #{usr := {_, _, R}}, _Host) ->
- is_glob_match(R, RR);
-acl_rule_matches({node_glob, {UR, SR}}, #{usr := {U, S, _}}, _Host) ->
- is_glob_match(U, UR) andalso is_glob_match(S, SR);
-acl_rule_matches(_ACL, _Data, _Host) ->
- false.
+-spec reload_from_config() -> ok.
+reload_from_config() ->
+ gen_server:call(?MODULE, reload_from_config, timer:minutes(1)).
+
+-spec validator(access_rules | acl) -> econf:validator().
+validator(access_rules) ->
+ econf:options(
+ #{'_' => access_rules_validator()},
+ [{disallowed, [all, none]}, unique]);
+validator(acl) ->
+ econf:options(
+ #{'_' => acl_validator()},
+ [{disallowed, [all, none]}, unique]).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+-spec init([]) -> {ok, state()}.
+init([]) ->
+ create_tab(acl, bag),
+ create_tab(access, set),
+ Hosts = ejabberd_option:hosts(),
+ load_from_config([], Hosts),
+ ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20),
+ {ok, #{hosts => Hosts}}.
+
+-spec handle_call(term(), term(), state()) -> {reply, ok, state()} | {noreply, state()}.
+handle_call(reload_from_config, _, #{hosts := OldHosts} = State) ->
+ NewHosts = ejabberd_option:hosts(),
+ load_from_config(OldHosts, NewHosts),
+ {reply, ok, State#{hosts => NewHosts}};
+handle_call(Request, From, State) ->
+ ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
+ {noreply, State}.
-resolve_access(all, _Host) ->
- all;
-resolve_access(none, _Host) ->
- none;
-resolve_access(Name, Host) when is_atom(Name) ->
- GAccess = mnesia:dirty_read(access, {Name, global}),
- LAccess =
- if Host /= global -> mnesia:dirty_read(access, {Name, Host});
- true -> []
- end,
- case GAccess ++ LAccess of
- [] ->
- [];
- AccessList ->
- lists:flatmap(
- fun(#access{rules = Rs}) ->
- Rs
- end, AccessList)
- end;
-resolve_access(Rules, _Host) when is_list(Rules) ->
- Rules.
-
--spec access_matches(atom()|list(), any(), global|binary()) -> allow|deny|atom()|integer().
-access_matches(Rules, Data, Host) ->
- case resolve_access(Rules, Host) of
- all -> allow;
- none -> deny;
- RRules -> access_rules_matches(RRules, Data, Host)
- end.
+-spec handle_cast(term(), state()) -> {noreply, state()}.
+handle_cast(Msg, State) ->
+ ?WARNING_MSG("Unexpected cast: ~p", [Msg]),
+ {noreply, State}.
--spec access_rules_matches(list(), any(), global|binary()) -> any().
+-spec handle_info(term(), state()) -> {noreply, state()}.
+handle_info(Info, State) ->
+ ?WARNING_MSG("Unexpected info: ~p", [Info]),
+ {noreply, State}.
-access_rules_matches(AR, Data, Host) ->
- access_rules_matches(AR, Data, Host, deny).
+-spec terminate(any(), state()) -> ok.
+terminate(_Reason, _State) ->
+ ejabberd_hooks:delete(config_reloaded, ?MODULE, reload_from_config, 20).
-access_rules_matches([{Type, Acls} | Rest], Data, Host, Default) ->
- case all_acl_rules_matches(Acls, Data, Host) of
- false ->
- access_rules_matches(Rest, Data, Host, Default);
- true ->
- Type
- end;
-access_rules_matches([], _Data, _Host, Default) ->
- Default.
+-spec code_change(term(), state(), term()) -> {ok, state()}.
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
-get_aclspecs(ACL, Host) ->
- mnesia:dirty_read(acl, {ACL, Host}) ++ mnesia:dirty_read(acl, {ACL, global}).
-
-is_regexp_match(String, RegExp) ->
- case ejabberd_regexp:run(String, RegExp) of
- nomatch -> false;
- match -> true;
- {error, ErrDesc} ->
- ?ERROR_MSG("Wrong regexp ~p in ACL: ~p",
- [RegExp, ErrDesc]),
- false
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+%%%===================================================================
+%%% Table management
+%%%===================================================================
+-spec load_from_config([binary()], [binary()]) -> ok.
+load_from_config(OldHosts, NewHosts) ->
+ ?DEBUG("Loading access rules from config", []),
+ load_tab(acl, NewHosts, fun ejabberd_option:acl/1),
+ load_tab(access, NewHosts, fun ejabberd_option:access_rules/1),
+ lists:foreach(
+ fun(Host) ->
+ ets:match_delete(access, {{'_', Host}, '_'}),
+ ets:match_delete(acl, {{'_', Host}, '_'})
+ end, OldHosts -- NewHosts),
+ ?DEBUG("Access rules loaded successfully", []).
+
+-spec create_tab(atom(), set | bag) -> atom().
+create_tab(Tab, Type) ->
+ _ = mnesia:delete_table(Tab),
+ ets:new(Tab, [named_table, Type, {read_concurrency, true}]).
+
+-spec load_tab(atom(), [binary()], fun((global | binary()) -> {atom(), list()})) -> true.
+load_tab(Tab, Hosts, Fun) ->
+ ets:insert(
+ Tab,
+ lists:flatmap(
+ fun(Host) ->
+ [{{Name, Host}, List} || {Name, List} <- Fun(Host)]
+ end, [global|Hosts])).
+
+-spec read_access(atom(), global | binary()) -> access().
+read_access(Name, Host) ->
+ case ets:lookup(access, {Name, Host}) of
+ [{_, Access}] -> Access;
+ [] -> []
end.
-is_glob_match(String, Glob) ->
- is_regexp_match(String,
- ejabberd_regexp:sh_to_awk(Glob)).
-
-ip_matches_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
- IPInt = ip_to_integer(IP),
- NetInt = ip_to_integer(Net),
- M = bnot (1 bsl (32 - Mask) - 1),
- IPInt band M =:= NetInt band M;
-ip_matches_mask({_, _, _, _, _, _, _, _} = IP,
- {_, _, _, _, _, _, _, _} = Net, Mask) ->
- IPInt = ip_to_integer(IP),
- NetInt = ip_to_integer(Net),
- M = bnot (1 bsl (128 - Mask) - 1),
- IPInt band M =:= NetInt band M;
-ip_matches_mask({_, _, _, _} = IP,
- {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
- IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
- NetInt = ip_to_integer(Net),
- M = bnot (1 bsl (128 - Mask) - 1),
- IPInt band M =:= NetInt band M;
-ip_matches_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
- {_, _, _, _} = Net, Mask) ->
- IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
- NetInt = ip_to_integer(Net),
- M = bnot (1 bsl (32 - Mask) - 1),
- IPInt band M =:= NetInt band M;
-ip_matches_mask(_, _, _) ->
- false.
-
-ip_to_integer({IP1, IP2, IP3, IP4}) ->
- IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
-ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
- IP8}) ->
- IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
- bor IP5
- bsl 16
- bor IP6
- bsl 16
- bor IP7
- bsl 16
- bor IP8.
-
+-spec read_acl(atom(), global | binary()) -> [acl_rule()].
+read_acl(Name, Host) ->
+ lists:flatmap(
+ fun({_, ACL}) -> ACL end,
+ ets:lookup(acl, {Name, Host})).
+
+%%%===================================================================
+%%% Validators
+%%%===================================================================
+validators() ->
+ #{ip => econf:list_or_single(econf:ip_mask()),
+ user => user_validator(econf:user(), econf:domain()),
+ user_regexp => user_validator(econf:re(), econf:domain()),
+ user_glob => user_validator(econf:glob(), econf:domain()),
+ server => econf:list_or_single(econf:domain()),
+ server_regexp => econf:list_or_single(econf:re()),
+ server_glob => econf:list_or_single(econf:glob()),
+ resource => econf:list_or_single(econf:resource()),
+ resource_regexp => econf:list_or_single(econf:re()),
+ resource_glob => econf:list_or_single(econf:glob()),
+ node_regexp => node_validator(econf:re(), econf:re()),
+ node_glob => node_validator(econf:glob(), econf:glob()),
+ shared_group => user_validator(econf:binary(), econf:domain()),
+ acl => econf:atom()}.
+
+rule_validator() ->
+ rule_validator(validators()).
+
+rule_validator(RVs) ->
+ econf:and_then(
+ econf:non_empty(econf:options(RVs, [])),
+ fun(Rules) ->
+ lists:flatmap(
+ fun({Type, Rs}) when is_list(Rs) ->
+ [{Type, R} || R <- Rs];
+ (Other) ->
+ [Other]
+ end, Rules)
+ end).
+
+access_validator() ->
+ econf:and_then(
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({K, V}) -> {(econf:atom())(K), V};
+ (A) -> {acl, (econf:atom())(A)}
+ end, lists:flatten(L));
+ (A) ->
+ [{acl, (econf:atom())(A)}]
+ end,
+ rule_validator()).
+
+access_rules_validator() ->
+ econf:and_then(
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({K, V}) -> {(econf:atom())(K), V};
+ (A) -> {(econf:atom())(A), [{acl, all}]}
+ end, lists:flatten(L));
+ (Bad) ->
+ Bad
+ end,
+ econf:non_empty(
+ econf:options(
+ #{allow => access_validator(),
+ deny => access_validator()},
+ []))).
+
+acl_validator() ->
+ econf:and_then(
+ fun(L) when is_list(L) -> lists:flatten(L);
+ (Bad) -> Bad
+ end,
+ rule_validator(maps:remove(acl, validators()))).
+
+user_validator(UV, SV) ->
+ econf:and_then(
+ econf:list_or_single(
+ fun({U, S}) ->
+ {UV(U), SV(S)};
+ (M) when is_list(M) ->
+ (econf:map(UV, SV))(M);
+ (Val) ->
+ US = (econf:binary())(Val),
+ case binary:split(US, <<"@">>, [global]) of
+ [U, S] -> {UV(U), SV(S)};
+ [U] -> UV(U);
+ _ -> econf:fail({bad_user, Val})
+ end
+ end),
+ fun lists:flatten/1).
+
+node_validator(UV, SV) ->
+ econf:and_then(
+ econf:and_then(
+ econf:list(econf:any()),
+ fun lists:flatten/1),
+ econf:map(UV, SV)).
+
+%%%===================================================================
+%%% Aux
+%%%===================================================================
+-spec match_regexp(iodata(), re:mp()) -> boolean().
+match_regexp(Data, RegExp) ->
+ re:run(Data, RegExp) /= nomatch.
+
+-spec loaded_shared_roster_module(global | binary()) -> atom().
+loaded_shared_roster_module(global) ->
+ loaded_shared_roster_module(ejabberd_config:get_myname());
loaded_shared_roster_module(Host) ->
case gen_mod:is_loaded(Host, mod_shared_roster_ldap) of
- true -> mod_shared_roster_ldap;
- false -> mod_shared_roster
- end.
-
-parse_ip_netmask(S) ->
- case str:tokens(S, <<"/">>) of
- [IPStr] ->
- case inet_parse:address(binary_to_list(IPStr)) of
- {ok, {_, _, _, _} = IP} -> {ok, IP, 32};
- {ok, {_, _, _, _, _, _, _, _} = IP} -> {ok, IP, 128};
- _ -> error
- end;
- [IPStr, MaskStr] ->
- case catch binary_to_integer(MaskStr) of
- Mask when is_integer(Mask), Mask >= 0 ->
- case inet_parse:address(binary_to_list(IPStr)) of
- {ok, {_, _, _, _} = IP} when Mask =< 32 ->
- {ok, IP, Mask};
- {ok, {_, _, _, _, _, _, _, _} = IP} when Mask =< 128 ->
- {ok, IP, Mask};
- _ -> error
- end;
- _ -> error
- end;
- _ -> error
- end.
-
-transform_access_rules_config(Config) when is_list(Config) ->
- lists:map(fun transform_access_rules_config2/1, lists:flatten(Config));
-transform_access_rules_config(Config) ->
- transform_access_rules_config([Config]).
-
-transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) ->
- {Type, [all]};
-transform_access_rules_config2({Type, ACL}) when is_atom(ACL) ->
- {Type, [{acl, ACL}]};
-transform_access_rules_config2({Res, Rules}) when is_list(Rules) ->
- T = lists:map(fun({Type, Args}) when is_list(Args) ->
- normalize_spec({Type, hd(lists:flatten(Args))});
- (V) -> normalize_spec(V)
- end, lists:flatten(Rules)),
- {Res, T};
-transform_access_rules_config2({Res, Rule}) ->
- {Res, [Rule]}.
-
-access_rules_validator(Name) when is_atom(Name) ->
- Name;
-access_rules_validator(Rules0) ->
- Rules = transform_access_rules_config(Rules0),
- access_shaper_rules_validator(Rules, fun(allow) -> true;
- (deny) -> true;
- (_) -> false
- end),
- Rules.
-
-
-shaper_rules_validator(Name) when is_atom(Name) ->
- Name;
-shaper_rules_validator(Rules0) ->
- Rules = transform_access_rules_config(Rules0),
- access_shaper_rules_validator(Rules, fun(V) when is_atom(V) -> true;
- (V2) when is_integer(V2) -> true;
- (_) -> false
- end),
- Rules.
-
-access_shaper_rules_validator([{Type, Acls} = Rule | Rest], RuleTypeCheck) ->
- case RuleTypeCheck(Type) of
- true ->
- case acl_rules_verify(Acls, true) of
- true ->
- access_shaper_rules_validator(Rest, RuleTypeCheck);
- Err ->
- Err
- end;
+ true -> mod_shared_roster_ldap;
false ->
- invalid_syntax(<<"Invalid rule type: ~p in rule ~p">>, [Type, Rule])
- end;
-access_shaper_rules_validator([], _RuleTypeCheck) ->
- true;
-access_shaper_rules_validator(Value, _RuleTypeCheck) ->
- invalid_syntax(<<"Not a rule definition: ~p">>, [Value]).
-
-
-transform_options(Opts) ->
- Opts1 = lists:foldl(fun transform_options/2, [], Opts),
- {ACLOpts, Opts2} = lists:mapfoldl(
- fun({acl, Os}, Acc) ->
- {Os, Acc};
- (O, Acc) ->
- {[], [O|Acc]}
- end, [], Opts1),
- {AccessOpts, Opts3} = lists:mapfoldl(
- fun({access, Os}, Acc) ->
- {Os, Acc};
- (O, Acc) ->
- {[], [O|Acc]}
- end, [], Opts2),
- {NewAccessOpts, Opts4} = lists:mapfoldl(
- fun({access_rules, Os}, Acc) ->
- {Os, Acc};
- (O, Acc) ->
- {[], [O|Acc]}
- end, [], Opts3),
- {ShaperOpts, Opts5} = lists:mapfoldl(
- fun({shaper_rules, Os}, Acc) ->
- {Os, Acc};
- (O, Acc) ->
- {[], [O|Acc]}
- end, [], Opts4),
- ACLOpts1 = ejabberd_config:collect_options(lists:flatten(ACLOpts)),
- AccessOpts1 = case ejabberd_config:collect_options(
- lists:flatten(AccessOpts)) of
- [] -> [];
- L1 -> [{access, L1}]
- end,
- ACLOpts2 = case lists:map(
- fun({ACLName, Os}) ->
- {ACLName, ejabberd_config:collect_options(Os)}
- end, ACLOpts1) of
- [] -> [];
- L2 -> [{acl, L2}]
- end,
- NewAccessOpts1 = case lists:map(
- fun({NAName, Os}) ->
- {NAName, transform_access_rules_config(Os)}
- end, lists:flatten(NewAccessOpts)) of
- [] -> [];
- L3 -> [{access_rules, L3}]
- end,
- ShaperOpts1 = case lists:map(
- fun({SName, Ss}) ->
- {SName, transform_access_rules_config(Ss)}
- end, lists:flatten(ShaperOpts)) of
- [] -> [];
- L4 -> [{shaper_rules, L4}]
- end,
- ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5.
-
-transform_options({acl, Name, Type}, Opts) ->
- T = case Type of
- all -> all;
- none -> none;
- {user, U} -> {user, [b(U)]};
- {user, U, S} -> {user, [[{b(U), b(S)}]]};
- {shared_group, G} -> {shared_group, [b(G)]};
- {shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]};
- {user_regexp, UR} -> {user_regexp, [b(UR)]};
- {user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]};
- {node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]};
- {user_glob, UR} -> {user_glob, [b(UR)]};
- {user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]};
- {node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]};
- {server, S} -> {server, [b(S)]};
- {resource, R} -> {resource, [b(R)]};
- {server_regexp, SR} -> {server_regexp, [b(SR)]};
- {server_glob, S} -> {server_glob, [b(S)]};
- {ip, S} -> {ip, [b(S)]};
- {resource_glob, R} -> {resource_glob, [b(R)]};
- {resource_regexp, R} -> {resource_regexp, [b(R)]}
- end,
- [{acl, [{Name, [T]}]}|Opts];
-transform_options({access, Name, Rules}, Opts) ->
- NewRules = [{ACL, Action} || {Action, ACL} <- Rules],
- [{access, [{Name, NewRules}]}|Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
-opt_type(access) -> fun (V) -> V end;
-opt_type(access_rules) -> fun (V) -> V end;
-opt_type(shaper_rules) -> fun (V) -> V end;
-opt_type(acl) -> fun (V) -> V end;
-opt_type(_) -> [access, acl, access_rules, shaper_rules].
+ case gen_mod:is_loaded(Host, mod_shared_roster) of
+ true -> mod_shared_roster;
+ false -> undefined
+ end
+ end.
diff --git a/src/econf.erl b/src/econf.erl
new file mode 100644
index 000000000..ac8b9ca6d
--- /dev/null
+++ b/src/econf.erl
@@ -0,0 +1,529 @@
+%%%----------------------------------------------------------------------
+%%% File : econf.erl
+%%% Purpose : Validator for ejabberd configuration options
+%%%
+%%%
+%%% 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(econf).
+
+%% API
+-export([parse/3, validate/2, fail/1, format_error/2, replace_macros/1]).
+%% Simple types
+-export([pos_int/0, pos_int/1, non_neg_int/0, non_neg_int/1]).
+-export([int/0, int/2, number/1, octal/0]).
+-export([binary/0, binary/1]).
+-export([string/0, string/1]).
+-export([enum/1, bool/0, atom/0, any/0]).
+%% Complex types
+-export([url/0, url/1]).
+-export([file/0, file/1]).
+-export([directory/0, directory/1]).
+-export([ip/0, ipv4/0, ipv6/0, ip_mask/0, port/0]).
+-export([re/0, glob/0]).
+-export([path/0, binary_sep/1]).
+-export([beam/0, beam/1]).
+-export([timeout/1, timeout/2]).
+%% Composite types
+-export([list/1, list/2]).
+-export([list_or_single/1, list_or_single/2]).
+-export([map/2, map/3]).
+-export([either/2, and_then/2, non_empty/1]).
+-export([options/1, options/2]).
+%% Custom types
+-export([acl/0, shaper/0, url_or_file/0, lang/0]).
+-export([pem/0, queue_type/0]).
+-export([jid/0, user/0, domain/0, resource/0]).
+-export([db_type/1, ldap_filter/0, well_known/2]).
+-ifdef(SIP).
+-export([sip_uri/0]).
+-endif.
+
+-type error_reason() :: term().
+-type error_return() :: {error, error_reason(), yconf:ctx()}.
+-type validator() :: yconf:validator().
+-type validator(T) :: yconf:validator(T).
+-type validators() :: yconf:validators().
+-export_type([validator/0, validator/1, validators/0]).
+-export_type([error_reason/0, error_return/0]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+parse(File, Validators, Options) ->
+ try yconf:parse(File, Validators, Options)
+ catch _:{?MODULE, Reason, Ctx} ->
+ {error, Reason, Ctx}
+ end.
+
+validate(Validator, Y) ->
+ try yconf:validate(Validator, Y)
+ catch _:{?MODULE, Reason, Ctx} ->
+ {error, Reason, Ctx}
+ end.
+
+replace_macros(Y) ->
+ yconf:replace_macros(Y).
+
+-spec fail(error_reason()) -> no_return().
+fail(Reason) ->
+ yconf:fail(?MODULE, Reason).
+
+format_error({bad_module, Mod}, Ctx)
+ when Ctx == [listen, module];
+ Ctx == [listen, request_handlers] ->
+ Mods = ejabberd_config:beams(all),
+ format("~s: unknown ~s: ~s. Did you mean ~s?",
+ [yconf:format_ctx(Ctx),
+ format_module_type(Ctx), Mod,
+ misc:best_match(Mod, Mods)]);
+format_error({bad_module, Mod}, Ctx)
+ when Ctx == [modules] ->
+ Mods = lists:filter(
+ fun(M) ->
+ case atom_to_list(M) of
+ "mod_" ++ _ -> true;
+ _ -> false
+ end
+ end, ejabberd_config:beams(all)),
+ format("~s: unknown ~s: ~s. Did you mean ~s?",
+ [yconf:format_ctx(Ctx),
+ format_module_type(Ctx), Mod,
+ misc:best_match(Mod, Mods)]);
+format_error({bad_export, {F, A}, Mod}, Ctx)
+ when Ctx == [listen, module];
+ Ctx == [listen, request_handlers];
+ Ctx == [modules] ->
+ Type = format_module_type(Ctx),
+ Slogan = yconf:format_ctx(Ctx),
+ case lists:member(Mod, ejabberd_config:beams(local)) of
+ true ->
+ format("~s: '~s' is not a ~s", [Slogan, Mod, Type]);
+ false ->
+ case lists:member(Mod, ejabberd_config:beams(external)) of
+ true ->
+ format("~s: third-party ~s '~s' doesn't export "
+ "function ~s/~B. If it's really a ~s, "
+ "consider to upgrade it",
+ [Slogan, Type, Mod, F, A, Type]);
+ false ->
+ format("~s: '~s' doesn't match any known ~s",
+ [Slogan, Mod, Type])
+ end
+ end;
+format_error({unknown_option, [], _} = Why, Ctx) ->
+ format("~s. There are no available options",
+ [yconf:format_error(Why, Ctx)]);
+format_error({unknown_option, Known, Opt} = Why, Ctx) ->
+ format("~s. Did you mean ~s? ~s",
+ [yconf:format_error(Why, Ctx),
+ misc:best_match(Opt, Known),
+ format_known("Available options", Known)]);
+format_error({bad_enum, Known, Bad} = Why, Ctx) ->
+ format("~s. Did you mean ~s? ~s",
+ [yconf:format_error(Why, Ctx),
+ misc:best_match(Bad, Known),
+ format_known("Possible values", Known)]);
+format_error({bad_yaml, _, _} = Why, _) ->
+ format_error(Why);
+format_error(Reason, Ctx) ->
+ [H|T] = format_error(Reason),
+ yconf:format_ctx(Ctx) ++ ": " ++ [string:to_lower(H)|T].
+
+format_error({bad_db_type, _, Atom}) ->
+ format("unsupported database: ~s", [Atom]);
+format_error({bad_lang, Lang}) ->
+ format("Invalid language tag: ~s", [Lang]);
+format_error({bad_pem, Why, Path}) ->
+ format("Failed to read PEM file '~s': ~s",
+ [Path, pkix:format_error(Why)]);
+format_error({bad_cert, Why, Path}) ->
+ format_error({bad_pem, Why, Path});
+format_error({bad_jid, Bad}) ->
+ format("Invalid XMPP address: ~s", [Bad]);
+format_error({bad_user, Bad}) ->
+ format("Invalid user part: ~s", [Bad]);
+format_error({bad_domain, Bad}) ->
+ format("Invalid domain: ~s", [Bad]);
+format_error({bad_resource, Bad}) ->
+ format("Invalid resource part: ~s", [Bad]);
+format_error({bad_ldap_filter, Bad}) ->
+ format("Invalid LDAP filter: ~s", [Bad]);
+format_error({bad_sip_uri, Bad}) ->
+ format("Invalid SIP URI: ~s", [Bad]);
+format_error({route_conflict, R}) ->
+ format("Failed to reuse route '~s' because it's "
+ "already registered on a virtual host",
+ [R]);
+format_error({listener_dup, AddrPort}) ->
+ format("Overlapping listeners found at ~s",
+ [format_addr_port(AddrPort)]);
+format_error({listener_conflict, AddrPort1, AddrPort2}) ->
+ format("Overlapping listeners found at ~s and ~s",
+ [format_addr_port(AddrPort1),
+ format_addr_port(AddrPort2)]);
+format_error({invalid_syntax, Reason}) ->
+ format("~s", [Reason]);
+format_error({missing_module_dep, Mod, DepMod}) ->
+ format("module ~s depends on module ~s, "
+ "which is not found in the config",
+ [Mod, DepMod]);
+format_error(eimp_error) ->
+ format("ejabberd is built without image converter support", []);
+format_error({mqtt_codec, Reason}) ->
+ mqtt_codec:format_error(Reason);
+format_error(Reason) ->
+ yconf:format_error(Reason).
+
+format_module_type([listen, module]) ->
+ "listening module";
+format_module_type([listen, request_handlers]) ->
+ "HTTP request handler";
+format_module_type([modules]) ->
+ "ejabberd module".
+
+format_known(_, Known) when length(Known) > 20 ->
+ "";
+format_known(Prefix, Known) ->
+ [Prefix, " are: ", format_join(Known)].
+
+format_join([]) ->
+ "(empty)";
+format_join([H|_] = L) when is_atom(H) ->
+ format_join([atom_to_binary(A, utf8) || A <- L]);
+format_join(L) ->
+ str:join(lists:sort(L), <<", ">>).
+
+%%%===================================================================
+%%% Validators from yconf
+%%%===================================================================
+pos_int() ->
+ yconf:pos_int().
+
+pos_int(Inf) ->
+ yconf:pos_int(Inf).
+
+non_neg_int() ->
+ yconf:non_neg_int().
+
+non_neg_int(Inf) ->
+ yconf:non_neg_int(Inf).
+
+int() ->
+ yconf:int().
+
+int(Min, Max) ->
+ yconf:int(Min, Max).
+
+number(Min) ->
+ yconf:number(Min).
+
+octal() ->
+ yconf:octal().
+
+binary() ->
+ yconf:binary().
+
+binary(Re) ->
+ yconf:binary(Re).
+
+enum(L) ->
+ yconf:enum(L).
+
+bool() ->
+ yconf:bool().
+
+atom() ->
+ yconf:atom().
+
+string() ->
+ yconf:string().
+
+string(Re) ->
+ yconf:string(Re).
+
+any() ->
+ yconf:any().
+
+url() ->
+ yconf:url().
+
+url(Schemes) ->
+ yconf:url(Schemes).
+
+file() ->
+ yconf:file().
+
+file(Type) ->
+ yconf:file(Type).
+
+directory() ->
+ yconf:directory().
+
+directory(Type) ->
+ yconf:directory(Type).
+
+ip() ->
+ yconf:ip().
+
+ipv4() ->
+ yconf:ipv4().
+
+ipv6() ->
+ yconf:ipv6().
+
+ip_mask() ->
+ yconf:ip_mask().
+
+port() ->
+ yconf:port().
+
+re() ->
+ yconf:re().
+
+glob() ->
+ yconf:glob().
+
+path() ->
+ yconf:path().
+
+binary_sep(Sep) ->
+ yconf:binary_sep(Sep).
+
+beam() ->
+ yconf:beam().
+
+beam(Exports) ->
+ yconf:beam(Exports).
+
+timeout(Units) ->
+ yconf:timeout(Units).
+
+timeout(Units, Inf) ->
+ yconf:timeout(Units, Inf).
+
+non_empty(F) ->
+ yconf:non_empty(F).
+
+list(F) ->
+ yconf:list(F).
+
+list(F, Opts) ->
+ yconf:list(F, Opts).
+
+list_or_single(F) ->
+ yconf:list_or_single(F).
+
+list_or_single(F, Opts) ->
+ yconf:list_or_single(F, Opts).
+
+map(F1, F2) ->
+ yconf:map(F1, F2).
+
+map(F1, F2, Opts) ->
+ yconf:map(F1, F2, Opts).
+
+either(F1, F2) ->
+ yconf:either(F1, F2).
+
+and_then(F1, F2) ->
+ yconf:and_then(F1, F2).
+
+options(V) ->
+ yconf:options(V).
+
+options(V, O) ->
+ yconf:options(V, O).
+
+%%%===================================================================
+%%% Custom validators
+%%%===================================================================
+acl() ->
+ either(
+ atom(),
+ acl:access_rules_validator()).
+
+shaper() ->
+ either(
+ atom(),
+ ejabberd_shaper:shaper_rules_validator()).
+
+-spec url_or_file() -> yconf:validator({file | url, binary()}).
+url_or_file() ->
+ either(
+ and_then(url(), fun(URL) -> {url, URL} end),
+ and_then(file(), fun(File) -> {file, File} end)).
+
+-spec lang() -> yconf:validator(binary()).
+lang() ->
+ and_then(
+ binary(),
+ fun(Lang) ->
+ try xmpp_lang:check(Lang)
+ catch _:_ -> fail({bad_lang, Lang})
+ end
+ end).
+
+-spec pem() -> yconf:validator(binary()).
+pem() ->
+ and_then(
+ path(),
+ fun(Path) ->
+ case pkix:is_pem_file(Path) of
+ true -> Path;
+ {false, Reason} ->
+ fail({bad_pem, Reason, Path})
+ end
+ end).
+
+-spec jid() -> yconf:validator(jid:jid()).
+jid() ->
+ and_then(
+ binary(),
+ fun(Val) ->
+ try jid:decode(Val)
+ catch _:{bad_jid, _} = Reason -> fail(Reason)
+ end
+ end).
+
+-spec user() -> yconf:validator(binary()).
+user() ->
+ and_then(
+ binary(),
+ fun(Val) ->
+ case jid:nodeprep(Val) of
+ error -> fail({bad_user, Val});
+ U -> U
+ end
+ end).
+
+-spec domain() -> yconf:validator(binary()).
+domain() ->
+ and_then(
+ non_empty(binary()),
+ fun(Val) ->
+ try jid:tolower(jid:decode(Val)) of
+ {<<"">>, Domain, <<"">>} -> Domain;
+ _ -> fail({bad_domain, Val})
+ catch _:{bad_jid, _} ->
+ fail({bad_domain, Val})
+ end
+ end).
+
+-spec resource() -> yconf:validator(binary()).
+resource() ->
+ and_then(
+ binary(),
+ fun(Val) ->
+ case jid:resourceprep(Val) of
+ error -> fail({bad_resource, Val});
+ R -> R
+ end
+ end).
+
+-spec db_type(module()) -> yconf:validator(atom()).
+db_type(M) ->
+ and_then(
+ atom(),
+ fun(T) ->
+ case code:ensure_loaded(db_module(M, T)) of
+ {module, _} -> T;
+ {error, _} -> fail({bad_db_type, M, T})
+ end
+ end).
+
+-spec queue_type() -> yconf:validator(ram | file).
+queue_type() ->
+ enum([ram, file]).
+
+-spec ldap_filter() -> yconf:validator(binary()).
+ldap_filter() ->
+ and_then(
+ binary(),
+ fun(Val) ->
+ case eldap_filter:parse(Val) of
+ {ok, _} -> Val;
+ _ -> fail({bad_ldap_filter, Val})
+ end
+ end).
+
+well_known(queue_type, _) ->
+ queue_type();
+well_known(db_type, M) ->
+ db_type(M);
+well_known(ram_db_type, M) ->
+ db_type(M);
+well_known(cache_life_time, _) ->
+ pos_int(infinity);
+well_known(cache_size, _) ->
+ pos_int(infinity);
+well_known(use_cache, _) ->
+ bool();
+well_known(cache_missed, _) ->
+ bool();
+well_known(host, _) ->
+ host();
+well_known(hosts, _) ->
+ list(host(), [unique]).
+
+-ifdef(SIP).
+sip_uri() ->
+ and_then(
+ binary(),
+ fun(Val) ->
+ case esip:decode_uri(Val) of
+ error -> fail({bad_sip_uri, Val});
+ URI -> URI
+ end
+ end).
+-endif.
+
+-spec host() -> yconf:validator(binary()).
+host() ->
+ fun(Domain) ->
+ Host = ejabberd_config:get_myname(),
+ Hosts = ejabberd_config:get_option(hosts),
+ Domain1 = (binary())(Domain),
+ Domain2 = misc:expand_keyword(<<"@HOST@">>, Domain1, Host),
+ Domain3 = (domain())(Domain2),
+ case lists:member(Domain3, Hosts) of
+ true -> fail({route_conflict, Domain3});
+ false -> Domain3
+ end
+ end.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+-spec db_module(module(), atom()) -> module().
+db_module(M, Type) ->
+ try list_to_atom(atom_to_list(M) ++ "_" ++ atom_to_list(Type))
+ catch _:system_limit ->
+ fail({bad_length, 255})
+ end.
+
+format_addr_port({IP, Port}) ->
+ IPStr = case tuple_size(IP) of
+ 4 -> inet:ntoa(IP);
+ 8 -> "[" ++ inet:ntoa(IP) ++ "]"
+ end,
+ IPStr ++ ":" ++ integer_to_list(Port).
+
+-spec format(iolist(), list()) -> string().
+format(Fmt, Args) ->
+ lists:flatten(io_lib:format(Fmt, Args)).
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index a7de9ab11..f7157f61d 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -38,7 +38,7 @@
-protocol({xep, 270, '1.0'}).
-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
- get_pid_file/0, check_app/1, module_name/1, is_loaded/0]).
+ get_pid_file/0, check_apps/0, module_name/1, is_loaded/0]).
-include("logger.hrl").
@@ -49,8 +49,8 @@ stop() ->
application:stop(ejabberd).
halt() ->
- application:stop(lager),
- application:stop(sasl),
+ _ = application:stop(lager),
+ _ = application:stop(sasl),
erlang:halt(1, [{flush, true}]).
%% @spec () -> false | string()
@@ -71,21 +71,15 @@ start_app(App, Type) ->
StartFlag = not is_loaded(),
start_app(App, Type, StartFlag).
-check_app(App) ->
- StartFlag = not is_loaded(),
- spawn(fun() -> check_app_modules(App, StartFlag) end),
- ok.
-
is_loaded() ->
Apps = application:which_applications(),
lists:keymember(ejabberd, 1, Apps).
-start_app(App, Type, StartFlag) when not is_list(App) ->
+start_app(App, Type, StartFlag) when is_atom(App) ->
start_app([App], Type, StartFlag);
start_app([App|Apps], Type, StartFlag) ->
case application:start(App,Type) of
ok ->
- spawn(fun() -> check_app_modules(App, StartFlag) end),
start_app(Apps, Type, StartFlag);
{error, {already_started, _}} ->
start_app(Apps, Type, StartFlag);
@@ -93,23 +87,23 @@ start_app([App|Apps], Type, StartFlag) ->
case lists:member(DepApp, [App|Apps]) of
true ->
Reason = io_lib:format(
- "failed to start application '~p': "
- "circular dependency on '~p' detected",
+ "Failed to start Erlang application '~s': "
+ "circular dependency with '~s' detected",
[App, DepApp]),
exit_or_halt(Reason, StartFlag);
false ->
start_app([DepApp,App|Apps], Type, StartFlag)
end;
- Err ->
- Reason = io_lib:format("failed to start application '~p': ~p",
- [App, Err]),
+ {error, Why} ->
+ Reason = io_lib:format(
+ "Failed to start Erlang application '~s': ~s. ~s",
+ [App, format_error(Why), hint()]),
exit_or_halt(Reason, StartFlag)
end;
start_app([], _Type, _StartFlag) ->
ok.
check_app_modules(App, StartFlag) ->
- sleep(5000),
case application:get_key(App, modules) of
{ok, Mods} ->
lists:foreach(
@@ -118,12 +112,12 @@ check_app_modules(App, StartFlag) ->
non_existing ->
File = get_module_file(App, Mod),
Reason = io_lib:format(
- "couldn't find module ~s "
- "needed for application '~p'",
- [File, App]),
+ "Couldn't find file ~s needed "
+ "for Erlang application '~s'. ~s",
+ [File, App, hint()]),
exit_or_halt(Reason, StartFlag);
_ ->
- sleep(10)
+ ok
end
end, Mods);
_ ->
@@ -131,6 +125,23 @@ check_app_modules(App, StartFlag) ->
ok
end.
+check_apps() ->
+ spawn(
+ fun() ->
+ Apps = [ejabberd |
+ [App || {App, _, _} <- application:which_applications(),
+ App /= ejabberd]],
+ ?DEBUG("Checking consistency of applications: ~s",
+ [misc:join_atoms(Apps, <<", ">>)]),
+ misc:peach(
+ fun(App) ->
+ check_app_modules(App, true)
+ end, Apps),
+ ?DEBUG("All applications are intact", []),
+ lists:foreach(fun erlang:garbage_collect/1, processes())
+ end).
+
+-spec exit_or_halt(iodata(), boolean()) -> no_return().
exit_or_halt(Reason, StartFlag) ->
?CRITICAL_MSG(Reason, []),
if StartFlag ->
@@ -140,9 +151,6 @@ exit_or_halt(Reason, StartFlag) ->
erlang:error(application_start_failed)
end.
-sleep(N) ->
- timer:sleep(p1_rand:uniform(N)).
-
get_module_file(App, Mod) ->
BaseName = atom_to_list(Mod),
case code:lib_dir(App, ebin) of
@@ -177,3 +185,12 @@ erlang_name(Atom) when is_atom(Atom) ->
misc:atom_to_binary(Atom);
erlang_name(Bin) when is_binary(Bin) ->
Bin.
+
+format_error({Reason, File}) when is_list(Reason), is_list(File) ->
+ Reason ++ ": " ++ File;
+format_error(Term) ->
+ io_lib:format("~p", [Term]).
+
+hint() ->
+ "This usually means that ejabberd or Erlang "
+ "was compiled/installed incorrectly.".
diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl
index 0c53795b8..2f63cb576 100644
--- a/src/ejabberd_access_permissions.erl
+++ b/src/ejabberd_access_permissions.erl
@@ -29,17 +29,13 @@
-include("logger.hrl").
-behaviour(gen_server).
--behaviour(ejabberd_config).
%% API
-export([start_link/0,
- parse_api_permissions/1,
can_access/2,
invalidate/0,
- opt_type/1,
- show_current_definitions/0,
- register_permission_addon/2,
- unregister_permission_addon/1]).
+ validator/0,
+ show_current_definitions/0]).
%% gen_server callbacks
-export([init/1,
@@ -51,16 +47,29 @@
-define(SERVER, ?MODULE).
--record(state, {
- definitions = none,
- fragments_generators = []
-}).
+-record(state,
+ {definitions = none :: none | [definition()]}).
+
+-type state() :: #state{}.
+-type rule() :: {access, acl:access()} |
+ {acl, all | none | acl:acl_rule()}.
+-type what() :: all | none | [atom() | {tag, atom()}].
+-type who() :: rule() | {oauth, {[binary()], [rule()]}}.
+-type from() :: atom().
+-type permission() :: {binary(), {[from()], [who()], {what(), what()}}}.
+-type definition() :: {binary(), {[from()], [who()], [atom()] | all}}.
+-type caller_info() :: #{caller_module => module(),
+ caller_host => global | binary(),
+ tag => binary() | none,
+ extra_permissions => [definition()],
+ atom() => term()}.
+
+-export_type([permission/0]).
%%%===================================================================
%%% API
%%%===================================================================
-
--spec can_access(atom(), map()) -> allow | deny.
+-spec can_access(atom(), caller_info()) -> allow | deny.
can_access(Cmd, CallerInfo) ->
gen_server:call(?MODULE, {can_access, Cmd, CallerInfo}).
@@ -68,65 +77,24 @@ can_access(Cmd, CallerInfo) ->
invalidate() ->
gen_server:cast(?MODULE, invalidate).
--spec register_permission_addon(atom(), fun()) -> ok.
-register_permission_addon(Name, Fun) ->
- gen_server:call(?MODULE, {register_config_fragment_generator, Name, Fun}).
-
--spec unregister_permission_addon(atom()) -> ok.
-unregister_permission_addon(Name) ->
- gen_server:call(?MODULE, {unregister_config_fragment_generator, Name}).
-
--spec show_current_definitions() -> any().
+-spec show_current_definitions() -> [definition()].
show_current_definitions() ->
gen_server:call(?MODULE, show_current_definitions).
-%%--------------------------------------------------------------------
-%% @doc
-%% Starts the server
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Reason :: term()}.
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
-
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Initializes the server
-%%
-%% @spec init(Args) -> {ok, State} |
-%% {ok, State, Timeout} |
-%% ignore |
-%% {stop, Reason}
-%% @end
-%%--------------------------------------------------------------------
--spec init(Args :: term()) ->
- {ok, State :: #state{}} | {ok, State :: #state{}, timeout() | hibernate} |
- {stop, Reason :: term()} | ignore.
+-spec init([]) -> {ok, state()}.
init([]) ->
ejabberd_hooks:add(config_reloaded, ?MODULE, invalidate, 90),
{ok, #state{}}.
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling call messages
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec handle_call(Request :: term(), From :: {pid(), Tag :: term()},
- State :: #state{}) ->
- {reply, Reply :: term(), NewState :: #state{}} |
- {reply, Reply :: term(), NewState :: #state{}, timeout() | hibernate} |
- {noreply, NewState :: #state{}} |
- {noreply, NewState :: #state{}, timeout() | hibernate} |
- {stop, Reason :: term(), Reply :: term(), NewState :: #state{}} |
- {stop, Reason :: term(), NewState :: #state{}}.
+-spec handle_call({can_access, atom(), caller_info()} |
+ show_current_definitions | term(),
+ term(), state()) -> {reply, term(), state()}.
handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
CallerModule = maps:get(caller_module, CallerInfo, none),
Host = maps:get(caller_host, CallerInfo, global),
@@ -134,123 +102,61 @@ handle_call({can_access, Cmd, CallerInfo}, _From, State) ->
{State2, Defs0} = get_definitions(State),
Defs = maps:get(extra_permissions, CallerInfo, []) ++ Defs0,
Res = lists:foldl(
- fun({Name, _} = Def, none) ->
- case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
- true ->
- ?DEBUG("Command '~p' execution allowed by rule '~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
- allow;
- _ ->
- none
- end;
- (_, Val) ->
- Val
- end, none, Defs),
+ fun({Name, _} = Def, none) ->
+ case matches_definition(Def, Cmd, CallerModule, Tag, Host, CallerInfo) of
+ true ->
+ ?DEBUG("Command '~p' execution allowed by rule "
+ "'~s' (CallerInfo=~p)", [Cmd, Name, CallerInfo]),
+ allow;
+ _ ->
+ none
+ end;
+ (_, Val) ->
+ Val
+ end, none, Defs),
Res2 = case Res of
allow -> allow;
_ ->
- ?DEBUG("Command '~p' execution denied (CallerInfo=~p)", [Cmd, CallerInfo]),
+ ?DEBUG("Command '~p' execution denied "
+ "(CallerInfo=~p)", [Cmd, CallerInfo]),
deny
end,
{reply, Res2, State2};
handle_call(show_current_definitions, _From, State) ->
{State2, Defs} = get_definitions(State),
{reply, Defs, State2};
-handle_call({register_config_fragment_generator, Name, Fun}, _From, #state{fragments_generators = Gens} = State) ->
- NGens = lists:keystore(Name, 1, Gens, {Name, Fun}),
- {reply, ok, State#state{fragments_generators = NGens}};
-handle_call({unregister_config_fragment_generator, Name}, _From, #state{fragments_generators = Gens} = State) ->
- NGens = lists:keydelete(Name, 1, Gens),
- {reply, ok, State#state{fragments_generators = NGens}};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling cast messages
-%%
-%% @end
-%%--------------------------------------------------------------------
--spec handle_cast(Request :: term(), State :: #state{}) ->
- {noreply, NewState :: #state{}} |
- {noreply, NewState :: #state{}, timeout() | hibernate} |
- {stop, Reason :: term(), NewState :: #state{}}.
+-spec handle_cast(invalidate | term(), state()) -> {noreply, state()}.
handle_cast(invalidate, State) ->
{noreply, State#state{definitions = none}};
handle_cast(_Request, State) ->
{noreply, State}.
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Handling all non call/cast messages
-%%
-%% @spec handle_info(Info, State) -> {noreply, State} |
-%% {noreply, State, Timeout} |
-%% {stop, Reason, State}
-%% @end
-%%--------------------------------------------------------------------
--spec handle_info(Info :: timeout() | term(), State :: #state{}) ->
- {noreply, NewState :: #state{}} |
- {noreply, NewState :: #state{}, timeout() | hibernate} |
- {stop, Reason :: term(), NewState :: #state{}}.
handle_info(_Info, State) ->
{noreply, State}.
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% This function is called by a gen_server when it is about to
-%% terminate. It should be the opposite of Module:init/1 and do any
-%% necessary cleaning up. When it returns, the gen_server terminates
-%% with Reason. The return value is ignored.
-%%
-%% @spec terminate(Reason, State) -> void()
-%% @end
-%%--------------------------------------------------------------------
--spec terminate(Reason :: (normal | shutdown | {shutdown, term()} | term()),
- State :: #state{}) -> term().
terminate(_Reason, _State) ->
ejabberd_hooks:delete(config_reloaded, ?MODULE, invalidate, 90).
-%%--------------------------------------------------------------------
-%% @private
-%% @doc
-%% Convert process state when code is changed
-%%
-%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
-%% @end
-%%--------------------------------------------------------------------
--spec code_change(OldVsn :: term() | {down, term()}, State :: #state{},
- Extra :: term()) ->
- {ok, NewState :: #state{}} | {error, Reason :: term()}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-
--spec get_definitions(#state{}) -> {#state{}, any()}.
+-spec get_definitions(state()) -> {state(), [definition()]}.
get_definitions(#state{definitions = Defs} = State) when Defs /= none ->
{State, Defs};
-get_definitions(#state{definitions = none, fragments_generators = Gens} = State) ->
- DefaultOptions = [{<<"admin access">>,
- {[],
- [{acl,{acl,admin}},
- {oauth,[<<"ejabberd:admin">>],[{acl,{acl,admin}}]}],
- {all, [start, stop]}}}],
- ApiPerms = ejabberd_config:get_option(api_permissions, DefaultOptions),
+get_definitions(#state{definitions = none} = State) ->
+ ApiPerms = ejabberd_option:api_permissions(),
AllCommands = ejabberd_commands:get_commands_definition(),
- Frags = lists:foldl(
- fun({_Name, Generator}, Acc) ->
- Acc ++ Generator()
- end, [], Gens),
NDefs0 = lists:map(
fun({Name, {From, Who, {Add, Del}}}) ->
Cmds = filter_commands_with_permissions(AllCommands, Add, Del),
{Name, {From, Who, Cmds}}
- end, ApiPerms ++ Frags),
+ end, ApiPerms),
NDefs = case lists:keyfind(<<"console commands">>, 1, NDefs0) of
false ->
[{<<"console commands">>,
@@ -262,6 +168,8 @@ get_definitions(#state{definitions = none, fragments_generators = Gens} = State)
end,
{State#state{definitions = NDefs}, NDefs}.
+-spec matches_definition(definition(), atom(), module(),
+ atom(), global | binary(), caller_info()) -> boolean().
matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInfo) ->
case What == all orelse lists:member(Cmd, What) of
true ->
@@ -271,25 +179,29 @@ matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInf
true ->
Scope = maps:get(oauth_scope, CallerInfo, none),
lists:any(
- fun({access, Access}) when Scope == none ->
- acl:access_matches(Access, CallerInfo, Host) == allow;
- ({acl, Acl}) when Scope == none ->
- acl:acl_rule_matches(Acl, CallerInfo, Host);
- ({oauth, Scopes, List}) when Scope /= none ->
- case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of
- true ->
- lists:any(
- fun({access, Access}) ->
- acl:access_matches(Access, CallerInfo, Host) == allow;
- ({acl, Acl}) ->
- acl:acl_rule_matches(Acl, CallerInfo, Host)
- end, List);
- _ ->
- false
- end;
- (_) ->
- false
- end, Who);
+ fun({access, Access}) when Scope == none ->
+ acl:match_rule(Host, Access, CallerInfo) == allow;
+ ({acl, Name} = Acl) when Scope == none, is_atom(Name) ->
+ acl:match_acl(Host, Acl, CallerInfo);
+ ({acl, Acl}) when Scope == none ->
+ acl:match_acl(Host, Acl, CallerInfo);
+ ({oauth, {Scopes, List}}) when Scope /= none ->
+ case ejabberd_oauth:scope_in_scope_list(Scope, Scopes) of
+ true ->
+ lists:any(
+ fun({access, Access}) ->
+ acl:match_rule(Host, Access, CallerInfo) == allow;
+ ({acl, Name} = Acl) when is_atom(Name) ->
+ acl:match_acl(Host, Acl, CallerInfo);
+ ({acl, Acl}) ->
+ acl:match_acl(Host, Acl, CallerInfo)
+ end, List);
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, Who);
_ ->
false
end;
@@ -297,12 +209,15 @@ matches_definition({_Name, {From, Who, What}}, Cmd, Module, Tag, Host, CallerInf
false
end.
+-spec filter_commands_with_permissions([#ejabberd_commands{}], what(), what()) -> [atom()].
filter_commands_with_permissions(AllCommands, Add, Del) ->
CommandsAdd = filter_commands_with_patterns(AllCommands, Add, []),
CommandsDel = filter_commands_with_patterns(CommandsAdd, Del, []),
lists:map(fun(#ejabberd_commands{name = N}) -> N end,
CommandsAdd -- CommandsDel).
+-spec filter_commands_with_patterns([#ejabberd_commands{}], what(),
+ [#ejabberd_commands{}]) -> [#ejabberd_commands{}].
filter_commands_with_patterns([], _Patterns, Acc) ->
Acc;
filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
@@ -313,6 +228,7 @@ filter_commands_with_patterns([C | CRest], Patterns, Acc) ->
filter_commands_with_patterns(CRest, Patterns, Acc)
end.
+-spec command_matches_patterns(#ejabberd_commands{}, what()) -> boolean().
command_matches_patterns(_, all) ->
true;
command_matches_patterns(_, none) ->
@@ -332,125 +248,26 @@ command_matches_patterns(C, [_ | Tail]) ->
command_matches_patterns(C, Tail).
%%%===================================================================
-%%% Options parsing code
+%%% Validators
%%%===================================================================
-
-parse_api_permissions(Data) when is_list(Data) ->
- [parse_api_permission(Name, Args) || {Name, Args} <- Data].
-
-parse_api_permission(Name, Args0) ->
- Args = lists:flatten(Args0),
- {From, Who, What} = case key_split(Args, [{from, []}, {who, none}, {what, []}]) of
- {error, Msg} ->
- report_error(<<"~s inside api_permission '~s' section">>, [Msg, Name]);
- Val -> Val
- end,
- {Name, {parse_from(Name, From), parse_who(Name, Who, oauth), parse_what(Name, What)}}.
-
-parse_from(_Name, Module) when is_atom(Module) ->
- [Module];
-parse_from(Name, Modules) when is_list(Modules) ->
- lists:map(
- fun(Module) when is_atom(Module) ->
- Module;
- ([{tag, Tag}]) when is_binary(Tag) ->
- {tag, Tag};
- (Val) ->
- report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
- [Val, Name])
- end, Modules);
-parse_from(Name, Val) ->
- report_error(<<"Invalid value '~p' used inside 'from' section for api_permission '~s'">>,
- [Val, Name]).
-
-parse_who(Name, Atom, ParseOauth) when is_atom(Atom) ->
- parse_who(Name, [Atom], ParseOauth);
-parse_who(Name, Defs, ParseOauth) when is_list(Defs) ->
- lists:map(
- fun([Val]) ->
- [NVal] = parse_who(Name, [Val], ParseOauth),
- NVal;
- ({access, Val}) ->
- try acl:access_rules_validator(Val) of
- Rule ->
- {access, Rule}
- catch
- throw:{invalid_syntax, Msg} ->
- report_error(<<"Invalid access rule: '~s' used inside 'who' section for api_permission '~s'">>,
- [Msg, Name]);
- error:_ ->
- report_error(<<"Invalid access rule '~p' used inside 'who' section for api_permission '~s'">>,
- [Val, Name])
- end;
- ({oauth, OauthList}) when is_list(OauthList) ->
- case ParseOauth of
- oauth ->
- Nested = parse_who(Name, lists:flatten(OauthList), scope),
- {Scopes, Rest} = lists:partition(
- fun({scope, _}) -> true;
- (_) -> false
- end, Nested),
- case Scopes of
- [] ->
- report_error(<<"Oauth rule must contain at least one scope rule in 'who' section for api_permission '~s'">>,
- [Name]);
- _ ->
- {oauth, lists:foldl(fun({scope, S}, A) -> S ++ A end, [], Scopes), Rest}
- end;
- scope ->
- report_error(<<"Oauth rule can't be embedded inside other oauth rule in 'who' section for api_permission '~s'">>,
- [Name])
- end;
- ({scope, ScopeList}) ->
- case ParseOauth of
- oauth ->
- report_error(<<"Scope can be included only inside oauth rule in 'who' section for api_permission '~s'">>,
- [Name]);
- scope ->
- ScopeList2 = case ScopeList of
- V when is_binary(V) -> [V];
- V2 when is_list(V2) -> V2;
- V3 ->
- report_error(<<"Invalid value for scope '~p' in 'who' section for api_permission '~s'">>,
- [V3, Name])
- end,
- {scope, ScopeList2}
- end;
- (Atom) when is_atom(Atom) ->
- {acl, {acl, Atom}};
- (Other) ->
- try acl:normalize_spec(Other) of
- Rule2 ->
- {acl, Rule2}
- catch
- _:_ ->
- report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
- [Other, Name])
- end
- end, Defs);
-parse_who(Name, Val, _ParseOauth) ->
- report_error(<<"Invalid value '~p' used inside 'who' section for api_permission '~s'">>,
- [Val, Name]).
-
-parse_what(Name, Binary) when is_binary(Binary) ->
- parse_what(Name, [Binary]);
-parse_what(Name, Defs) when is_list(Defs) ->
- {A, D} = lists:foldl(
- fun(Def, {Add, Del}) ->
- case parse_single_what(Def) of
- {error, Err} ->
- report_error(<<"~s used in value '~p' in 'what' section for api_permission '~s'">>,
- [Err, Def, Name]);
- all ->
- {case Add of none -> none; _ -> all end, Del};
- {neg, all} ->
- {none, all};
- {neg, Value} ->
- {Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end};
- Value ->
- {case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del}
- end
- end, {[], []}, Defs),
+-spec parse_what([binary()]) -> {what(), what()}.
+parse_what(Defs) ->
+ {A, D} =
+ lists:foldl(
+ fun(Def, {Add, Del}) ->
+ case parse_single_what(Def) of
+ {error, Err} ->
+ econf:fail({invalid_syntax, [Err, ": ", Def]});
+ all ->
+ {case Add of none -> none; _ -> all end, Del};
+ {neg, all} ->
+ {none, all};
+ {neg, Value} ->
+ {Add, case Del of L when is_list(L) -> [Value | L]; L2 -> L2 end};
+ Value ->
+ {case Add of L when is_list(L) -> [Value | L]; L2 -> L2 end, Del}
+ end
+ end, {[], []}, Defs),
case {A, D} of
{[], _} ->
{none, all};
@@ -458,11 +275,9 @@ parse_what(Name, Defs) when is_list(Defs) ->
{A2, none};
V ->
V
- end;
-parse_what(Name, Val) ->
- report_error(<<"Invalid value '~p' used inside 'what' section for api_permission '~s'">>,
- [Val, Name]).
+ end.
+-spec parse_single_what(binary()) -> atom() | {neg, atom()} | {tag, atom()} | {error, string()}.
parse_single_what(<<"*">>) ->
all;
parse_single_what(<<"!*">>) ->
@@ -470,7 +285,7 @@ parse_single_what(<<"!*">>) ->
parse_single_what(<<"!", Rest/binary>>) ->
case parse_single_what(Rest) of
{neg, _} ->
- {error, <<"Double negation">>};
+ {error, "double negation"};
{error, _} = Err ->
Err;
V ->
@@ -485,71 +300,78 @@ parse_single_what(<<"[tag:", Rest/binary>>) ->
V when is_atom(V) ->
{tag, V};
_ ->
- {error, <<"Invalid tag">>}
+ {error, "invalid tag"}
end;
_ ->
- {error, <<"Invalid tag">>}
+ {error, "invalid tag"}
end;
-parse_single_what(Binary) when is_binary(Binary) ->
- case is_valid_command_name(Binary) of
- true ->
- binary_to_atom(Binary, latin1);
- _ ->
- {error, <<"Invalid value">>}
- end;
-parse_single_what(Atom) when is_atom(Atom) ->
- parse_single_what(atom_to_binary(Atom, latin1));
-parse_single_what(_) ->
- {error, <<"Invalid value">>}.
-
-is_valid_command_name(<<>>) ->
- false;
-is_valid_command_name(Val) ->
- is_valid_command_name2(Val).
-
-is_valid_command_name2(<<>>) ->
- true;
-is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
- orelse (K >= $0 andalso K =< $9)
- orelse K == $_ orelse K == $- ->
- is_valid_command_name2(Rest);
-is_valid_command_name2(_) ->
- false.
-
-key_split(Args, Fields) ->
- {_, Order1, Results1, Required1} = lists:foldl(
- fun({Field, Default}, {Idx, Order, Results, Required}) ->
- {Idx + 1, maps:put(Field, Idx, Order), [Default | Results], Required};
- (Field, {Idx, Order, Results, Required}) ->
- {Idx + 1, maps:put(Field, Idx, Order), [none | Results], maps:put(Field, 1, Required)}
- end, {1, #{}, [], #{}}, Fields),
- key_split(Args, list_to_tuple(Results1), Order1, Required1, #{}).
-
-key_split([], _Results, _Order, Required, _Duplicates) when map_size(Required) > 0 ->
- parse_error(<<"Missing fields '~s">>, [str:join(maps:keys(Required), <<", ">>)]);
-key_split([], Results, _Order, _Required, _Duplicates) ->
- Results;
-key_split([{Arg, Value} | Rest], Results, Order, Required, Duplicates) ->
- case maps:find(Arg, Order) of
- {ok, Idx} ->
- case maps:is_key(Arg, Duplicates) of
- false ->
- Results2 = setelement(Idx, Results, Value),
- key_split(Rest, Results2, Order, maps:remove(Arg, Required), maps:put(Arg, 1, Duplicates));
- true ->
- parse_error(<<"Duplicate field '~s'">>, [Arg])
- end;
- _ ->
- parse_error(<<"Unknown field '~s'">>, [Arg])
+parse_single_what(B) ->
+ case re:run(B, "^[a-z0-9_\\-]*$") of
+ nomatch -> {error, "invalid command"};
+ _ -> binary_to_atom(B, latin1)
end.
-report_error(Format, Args) ->
- throw({invalid_syntax, (str:format(Format, Args))}).
-
-parse_error(Format, Args) ->
- {error, (str:format(Format, Args))}.
-
-opt_type(api_permissions) ->
- fun parse_api_permissions/1;
-opt_type(_) ->
- [api_permissions].
+validator(Map, Opts) ->
+ econf:and_then(
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({K, V}) -> {(econf:atom())(K), V};
+ (A) -> {acl, (econf:atom())(A)}
+ end, lists:flatten(L));
+ (A) ->
+ [{acl, (econf:atom())(A)}]
+ end,
+ econf:and_then(
+ econf:options(maps:merge(acl:validators(), Map), Opts),
+ fun(Rules) ->
+ lists:flatmap(
+ fun({Type, Rs}) when is_list(Rs) ->
+ case maps:is_key(Type, acl:validators()) of
+ true -> [{acl, {Type, R}} || R <- Rs];
+ false -> [{Type, Rs}]
+ end;
+ (Other) ->
+ [Other]
+ end, Rules)
+ end)).
+
+validator(from) ->
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({K, V}) -> {(econf:enum([tag]))(K), (econf:binary())(V)};
+ (A) -> (econf:enum([ejabberd_xmlrpc, mod_http_api, ejabberd_ctl]))(A)
+ end, lists:flatten(L));
+ (A) ->
+ [(econf:enum([ejabberd_xmlrpc, mod_http_api, ejabberd_ctl]))(A)]
+ end;
+validator(what) ->
+ econf:and_then(
+ econf:list_or_single(econf:non_empty(econf:binary())),
+ fun parse_what/1);
+validator(who) ->
+ validator(#{access => econf:acl(), oauth => validator(oauth)}, []);
+validator(oauth) ->
+ econf:and_then(
+ validator(#{access => econf:acl(),
+ scope => econf:non_empty(
+ econf:list_or_single(econf:binary()))},
+ [{required, [scope]}]),
+ fun(Os) ->
+ {[Scopes], Rest} = proplists:split(Os, [scope]),
+ {lists:flatten([S || {_, S} <- Scopes]), Rest}
+ end).
+
+validator() ->
+ econf:map(
+ econf:binary(),
+ econf:and_then(
+ econf:options(
+ #{from => validator(from),
+ what => validator(what),
+ who => validator(who)}),
+ fun(Os) ->
+ {proplists:get_value(from, Os, []),
+ proplists:get_value(who, Os, none),
+ proplists:get_value(what, Os, [])}
+ end),
+ [unique]).
diff --git a/src/ejabberd_acme.erl b/src/ejabberd_acme.erl
index 9e25ed4e6..cae6b71be 100644
--- a/src/ejabberd_acme.erl
+++ b/src/ejabberd_acme.erl
@@ -1,6 +1,5 @@
-module (ejabberd_acme).
-behaviour(gen_server).
--behaviour(ejabberd_config).
%% ejabberdctl commands
-export([get_commands_spec/0,
@@ -18,7 +17,7 @@
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
--export([start_link/0, opt_type/1, register_certfiles/0]).
+-export([start_link/0, register_certfiles/0]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -188,7 +187,7 @@ get_certificates1(CAUrl, DomainString, PrivateKey) ->
Hosts = [list_to_bitstring(D) || D <- Domains],
get_certificates2(CAUrl, PrivateKey, Hosts).
--spec get_certificates2(url(), jose_jwk:key(), [bitstring()]) -> string().
+-spec get_certificates2(url(), jose_jwk:key(), [binary()]) -> string().
get_certificates2(CAUrl, PrivateKey, Hosts) ->
%% Get a certificate for each host
PemCertKeys = [get_certificate(CAUrl, Host, PrivateKey) || Host <- Hosts],
@@ -199,8 +198,8 @@ 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(), _} |
- {'error', bitstring(), _}]) ->
+-spec format_get_certificates_result([{'ok', binary(), _} |
+ {'error', binary(), _}]) ->
string().
format_get_certificates_result(Certs) ->
Cond = lists:all(fun(Cert) ->
@@ -217,21 +216,21 @@ format_get_certificates_result(Certs) ->
lists:flatten(Result)
end.
--spec format_get_certificate({'ok', bitstring(), _} |
- {'error', bitstring(), _}) ->
+-spec format_get_certificate({'ok', binary(), _} |
+ {'error', binary(), _}) ->
string().
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({error, 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()} |
- {'error', bitstring(), _}.
+-spec get_certificate(url(), binary(), jose_jwk:key()) ->
+ {'ok', binary(), pem()} |
+ {'error', binary(), _}.
get_certificate(CAUrl, DomainName, PrivateKey) ->
try
AllSubDomains = find_all_sub_domains(DomainName),
@@ -266,7 +265,7 @@ 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(), binary(), jose_jwk:key()) -> {'ok', string()} |
no_return().
create_new_account(CAUrl, Contact, PrivateKey) ->
try
@@ -287,7 +286,7 @@ create_new_account(CAUrl, Contact, PrivateKey) ->
throw({error,create_new_account})
end.
--spec create_new_authorization(url(), bitstring(), jose_jwk:key()) ->
+-spec create_new_authorization(url(), binary(), jose_jwk:key()) ->
{'ok', proplist()} | no_return().
create_new_authorization(CAUrl, DomainName, PrivateKey) ->
acme_challenge:register_hooks(DomainName),
@@ -320,12 +319,12 @@ create_new_authorization(CAUrl, DomainName, PrivateKey) ->
acme_challenge:unregister_hooks(DomainName)
end.
--spec create_new_certificate(url(), {bitstring(), [bitstring()]}, jose_jwk:key()) ->
- {ok, bitstring(), pem()}.
+-spec create_new_certificate(url(), {binary(), [binary()]}, jose_jwk:key()) ->
+ {ok, binary(), pem()}.
create_new_certificate(CAUrl, {DomainName, AllSubDomains}, PrivateKey) ->
try
{ok, Dirs, Nonce0} = ejabberd_acme_comm:directory(CAUrl),
- CSRSubject = [{commonName, bitstring_to_list(DomainName)}],
+ CSRSubject = [{?'id-at-commonName', bitstring_to_list(DomainName)}],
SANs = [{dNSName, SAN} || SAN <- AllSubDomains],
{CSR, CSRKey} = make_csr(CSRSubject, SANs),
{NotBefore, NotAfter} = not_before_not_after(),
@@ -404,9 +403,9 @@ 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(), _} |
- {'error', bitstring(), _}.
+-spec renew_certificate(url(), {binary(), data_cert()}, jose_jwk:key()) ->
+ {'ok', binary(), _} |
+ {'error', binary(), _}.
renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
case cert_to_expire(Cert) of
true ->
@@ -416,7 +415,7 @@ renew_certificate(CAUrl, {DomainName, _} = Cert, PrivateKey) ->
end.
--spec cert_to_expire({bitstring(), data_cert()}) -> boolean().
+-spec cert_to_expire({binary(), data_cert()}) -> boolean().
cert_to_expire({_DomainName, #data_cert{pem = Pem}}) ->
Certificate = pem_to_certificate(Pem),
Validity = get_utc_validity(Certificate),
@@ -494,7 +493,7 @@ format_certificate(DataCert, Verbose) ->
fail_format_certificate(DomainName)
end.
--spec format_certificate_plain(bitstring(), [string()], {expired | ok, string()}, string())
+-spec format_certificate_plain(binary(), [string()], {expired | ok, string()}, string())
-> string().
format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
Result = lists:flatten(io_lib:format(
@@ -507,7 +506,7 @@ format_certificate_plain(DomainName, SANs, NotAfter, Path) ->
format_validity(NotAfter), Path])),
Result.
--spec format_certificate_verbose(bitstring(), [string()], {expired | ok, string()}, bitstring())
+-spec format_certificate_verbose(binary(), [string()], {expired | ok, string()}, binary())
-> string().
format_certificate_verbose(DomainName, SANs, NotAfter, PemCert) ->
Result = lists:flatten(io_lib:format(
@@ -526,7 +525,7 @@ format_validity({expired, NotAfter}) ->
format_validity({ok, NotAfter}) ->
io_lib:format("Valid until: ~s UTC", [NotAfter]).
--spec fail_format_certificate(bitstring()) -> string().
+-spec fail_format_certificate(binary()) -> string().
fail_format_certificate(DomainName) ->
Result = lists:flatten(io_lib:format(
" Domain: ~s~n"
@@ -542,7 +541,7 @@ get_commonName(#'Certificate'{tbsCertificate = TbsCertificate}) ->
%% TODO: Not the best way to find the commonName
ShallowSubjectList = [Attribute || [Attribute] <- SubjectList],
- {_, _, CommonName} = lists:keyfind(attribute_oid(commonName), 2, ShallowSubjectList),
+ {_, _, CommonName} = lists:keyfind(?'id-at-commonName', 2, ShallowSubjectList),
%% TODO: Remove the length-encoding from the commonName before returning it
CommonName.
@@ -574,7 +573,7 @@ get_subjectAltNames(#'Certificate'{tbsCertificate = TbsCertificate}) ->
} = TbsCertificate,
EncodedSANs = [Val || #'Extension'{extnID = Oid, extnValue = Val} <- Exts,
- Oid =:= attribute_oid(subjectAltName)],
+ Oid == ?'id-ce-subjectAltName'],
lists:flatmap(
fun(EncSAN) ->
@@ -624,7 +623,7 @@ 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, binary()} | {file, file:filename()}) ->
{ok, deleted}.
revoke_certificate1(CAUrl, {domain, Domain}) ->
case domain_certificate_exists(Domain) of
@@ -657,13 +656,13 @@ revoke_certificate2(CAUrl, PemEncodedCert) ->
{ok, [], _Nonce1} = ejabberd_acme_comm:revoke_cert(Dirs, CertPrivateKey, Req, Nonce),
ok.
--spec parse_revoke_cert_argument(string()) -> {domain, bitstring()} | {file, file:filename()}.
+-spec parse_revoke_cert_argument(string()) -> {domain, binary()} | {file, file:filename()}.
parse_revoke_cert_argument([$f, $i, $l, $e, $:|File]) ->
{file, File};
parse_revoke_cert_argument([$d, $o, $m, $a, $i, $n, $: | Domain]) ->
{domain, list_to_bitstring(Domain)}.
--spec prepare_certificate_revoke(pem()) -> {bitstring(), jose_jwk:key()}.
+-spec prepare_certificate_revoke(pem()) -> {binary(), jose_jwk:key()}.
prepare_certificate_revoke(PemEncodedCert) ->
PemList = public_key:pem_decode(PemEncodedCert),
PemCertEnc = lists:keyfind('Certificate', 1, PemList),
@@ -674,7 +673,7 @@ prepare_certificate_revoke(PemEncodedCert) ->
{ok, Key} = find_private_key_in_pem(PemEncodedCert),
{Base64Cert, Key}.
--spec domain_certificate_exists(bitstring()) -> {bitstring(), data_cert()} | false.
+-spec domain_certificate_exists(binary()) -> {binary(), data_cert()} | false.
domain_certificate_exists(Domain) ->
Certs = read_certificates_persistent(),
lists:keyfind(Domain, 1, Certs).
@@ -688,7 +687,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, binary()}])
-> {binary(), jose_jwk:key()}.
make_csr(Attributes, SANs) ->
Key = generate_key(),
@@ -698,7 +697,7 @@ make_csr(Attributes, SANs) ->
SubPKInfoAlgo = subject_pk_info_algo(KeyPub),
{ok, RawBinPubKey} = raw_binary_public_key(KeyPub),
SubPKInfo = subject_pk_info(SubPKInfoAlgo, RawBinPubKey),
- {ok, Subject} = attributes_from_list(Attributes),
+ Subject = attributes_from_list(Attributes),
ExtensionRequest = extension_request(SANs),
CRI = certificate_request_info(SubPKInfo, Subject, ExtensionRequest),
{ok, EncodedCRI} = der_encode(
@@ -737,7 +736,7 @@ subject_pk_info(Algo, RawBinPubKey) ->
extension(SANs) ->
#'Extension'{
- extnID = attribute_oid(subjectAltName),
+ extnID = ?'id-ce-subjectAltName',
critical = false,
extnValue = public_key:der_encode('SubjectAltName', SANs)}.
@@ -791,45 +790,12 @@ der_encode(Type, Term) ->
{error, der_encode}
end.
-%%
-%% Attributes Parser
-%%
-
attributes_from_list(Attrs) ->
- ParsedAttrs = [attribute_parser_fun(Attr) || Attr <- Attrs],
- case lists:any(fun is_error/1, ParsedAttrs) of
- true ->
- {error, bad_attributes};
- false ->
- {ok, {rdnSequence, [[PAttr] || PAttr <- ParsedAttrs]}}
- end.
-
-attribute_parser_fun({AttrName, AttrVal}) ->
- try
- #'AttributeTypeAndValue'{
- type = attribute_oid(AttrName),
- %% TODO: Check if every attribute should be encoded as
- %% common name. Actually it doesn't matter in
- %% practice. Only in theory in order to have cleaner code.
- value = public_key:der_encode('X520CommonName', {printableString, AttrVal})
- %% value = length_bitstring(list_to_bitstring(AttrVal))
- }
- catch
- _:_ ->
- ?ERROR_MSG("Bad attribute: ~p~n", [{AttrName, AttrVal}]),
- {error, bad_attributes}
- end.
-
--spec attribute_oid(atom()) -> tuple() | no_return().
-attribute_oid(commonName) -> ?'id-at-commonName';
-attribute_oid(countryName) -> ?'id-at-countryName';
-attribute_oid(stateOrProvinceName) -> ?'id-at-stateOrProvinceName';
-attribute_oid(localityName) -> ?'id-at-localityName';
-attribute_oid(organizationName) -> ?'id-at-organizationName';
-attribute_oid(subjectAltName) -> ?'id-ce-subjectAltName';
-attribute_oid(_) -> error(bad_attributes).
-
-
+ {rdnSequence,
+ [[#'AttributeTypeAndValue'{
+ type = AttrName,
+ value = public_key:der_encode('X520CommonName', {printableString, AttrVal})
+ }] || {AttrName, AttrVal} <- Attrs]}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -939,7 +905,7 @@ private_key_types() ->
'DSAPrivateKey',
'ECPrivateKey'].
--spec find_all_sub_domains(bitstring()) -> [bitstring()].
+-spec find_all_sub_domains(binary()) -> [binary()].
find_all_sub_domains(DomainName) ->
AllRoutes = ejabberd_router:get_all_routes(),
DomainLen = size(DomainName),
@@ -1094,8 +1060,8 @@ remove_certificate_persistent(DataCert) ->
NewData = data_remove_certificate(Data, DataCert),
ok = write_persistent(NewData).
--spec save_certificate({ok, bitstring(), binary()} | {error, _, _}) ->
- {ok, bitstring(), saved} | {error, bitstring(), _}.
+-spec save_certificate({ok, binary(), binary()} | {error, _, _}) ->
+ {ok, binary(), saved} | {error, binary(), _}.
save_certificate({error, _, _} = Error) ->
Error;
save_certificate({ok, DomainName, Cert}) ->
@@ -1123,8 +1089,8 @@ save_certificate({ok, DomainName, Cert}) ->
{error, DomainName, saving}
end.
--spec save_renewed_certificate({ok, bitstring(), _} | {error, _, _}) ->
- {ok, bitstring(), _} | {error, bitstring(), _}.
+-spec save_renewed_certificate({ok, binary(), _} | {error, _, _}) ->
+ {ok, binary(), _} | {error, binary(), _}.
save_renewed_certificate({error, _, _} = Error) ->
Error;
save_renewed_certificate({ok, _, no_expire} = Cert) ->
@@ -1141,7 +1107,7 @@ register_certfiles() ->
ejabberd_pkix:add_certfile(Path)
end, Paths).
--spec write_cert(file:filename(), binary(), bitstring()) -> {ok, bitstring(), saved}.
+-spec write_cert(file:filename(), binary(), binary()) -> ok.
write_cert(CertificateFile, Cert, DomainName) ->
case file:write_file(CertificateFile, Cert) of
ok ->
@@ -1150,59 +1116,34 @@ write_cert(CertificateFile, Cert, DomainName) ->
{error, Why} ->
?WARNING_MSG("Failed to change mode of file ~s: ~s",
[CertificateFile, file:format_error(Why)])
- end,
- {ok, DomainName, saved};
+ end;
{error, Reason} ->
?ERROR_MSG("Error: ~p saving certificate at file: ~p",
[Reason, CertificateFile]),
throw({error, DomainName, saving})
end.
--spec get_config_acme() -> acme_config().
-get_config_acme() ->
- case ejabberd_config:get_option(acme, undefined) of
- undefined ->
- ?WARNING_MSG("No acme configuration has been specified", []),
- %% throw({error, configuration});
- [];
- Acme ->
- Acme
- end.
-
--spec get_config_contact() -> bitstring().
+-spec get_config_contact() -> binary().
get_config_contact() ->
- Acme = get_config_acme(),
- case lists:keyfind(contact, 1, Acme) of
- {contact, Contact} ->
- Contact;
- false ->
+ Acme = ejabberd_option:acme(),
+ try maps:get(contact, Acme)
+ catch _:{badkey, _} ->
?WARNING_MSG("No contact has been specified in configuration", []),
?DEFAULT_CONFIG_CONTACT
- %% throw({error, configuration_contact})
end.
-spec get_config_ca_url() -> url().
get_config_ca_url() ->
- Acme = get_config_acme(),
- case lists:keyfind(ca_url, 1, Acme) of
- {ca_url, CAUrl} ->
- CAUrl;
- false ->
+ Acme = ejabberd_option:acme(),
+ try maps:get(ca_url, Acme)
+ catch _:{badkey, _} ->
?ERROR_MSG("No CA url has been specified in configuration", []),
?DEFAULT_CONFIG_CA_URL
- %% throw({error, configuration_ca_url})
end.
-
--spec get_config_hosts() -> [bitstring()].
+-spec get_config_hosts() -> [binary()].
get_config_hosts() ->
- case ejabberd_config:get_option(hosts, undefined) of
- undefined ->
- ?ERROR_MSG("No hosts have been specified in configuration", []),
- throw({error, configuration_hosts});
- Hosts ->
- Hosts
- end.
+ ejabberd_option:hosts().
-spec acme_certs_dir() -> file:filename().
acme_certs_dir() ->
@@ -1210,23 +1151,3 @@ acme_certs_dir() ->
generate_key() ->
jose_jwk:generate_key({ec, secp256r1}).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% Option Parsing Code
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(acme) ->
- fun(L) ->
- lists:map(
- fun({ca_url, URL}) ->
- {ca_url, misc:try_url(URL)};
- ({contact, Contact}) ->
- [<<_, _/binary>>, <<_, _/binary>>] =
- binary:split(Contact, <<":">>),
- {contact, Contact}
- end, L)
- end;
-opt_type(_) ->
- [acme].
diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl
index 017586ae6..da50b013b 100644
--- a/src/ejabberd_admin.erl
+++ b/src/ejabberd_admin.erl
@@ -35,6 +35,8 @@
stop_kindly/2, send_service_message_all_mucs/2,
registered_vhosts/0,
reload_config/0,
+ dump_config/1,
+ convert_to_yaml/2,
%% Cluster
join_cluster/1, leave_cluster/1, list_cluster/0,
%% Erlang
@@ -152,7 +154,7 @@ get_commands_spec() ->
result_desc = "The type of logger module used",
result_example = lager,
args = [{loglevel, integer}],
- result = {logger, atom}},
+ result = {res, rescode}},
#ejabberd_commands{name = update_list, tags = [server],
desc = "List modified modules that can be updated",
@@ -285,11 +287,18 @@ get_commands_spec() ->
#ejabberd_commands{name = convert_to_yaml, tags = [config],
desc = "Convert the input file from Erlang to YAML format",
- module = ejabberd_config, function = convert_to_yaml,
+ module = ?MODULE, function = convert_to_yaml,
args_desc = ["Full path to the original configuration file", "And full path to final file"],
args_example = ["/etc/ejabberd/ejabberd.cfg", "/etc/ejabberd/ejabberd.yml"],
args = [{in, string}, {out, string}],
result = {res, rescode}},
+ #ejabberd_commands{name = dump_config, tags = [config],
+ desc = "Dump configuration in YAML format as seen by ejabberd",
+ module = ?MODULE, function = dump_config,
+ args_desc = ["Full path to output file"],
+ args_example = ["/tmp/ejabberd.yml"],
+ args = [{out, string}],
+ result = {res, rescode}},
#ejabberd_commands{name = delete_expired_messages, tags = [purge],
desc = "Delete expired offline messages from database",
@@ -407,9 +416,7 @@ rotate_log() ->
ejabberd_logger:rotate_log().
set_loglevel(LogLevel) ->
- {module, Module} = ejabberd_logger:set(LogLevel),
- Module.
-
+ ejabberd_logger:set(LogLevel).
%%%
%%% Stop Kindly
@@ -454,11 +461,13 @@ send_service_message_all_mucs(Subject, AnnouncementText) ->
Message = str:format("~s~n~s", [Subject, AnnouncementText]),
lists:foreach(
fun(ServerHost) ->
- MUCHost = gen_mod:get_module_opt_host(
- ServerHost, mod_muc, <<"conference.@HOST@">>),
- mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
+ MUCHosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc),
+ lists:foreach(
+ fun(MUCHost) ->
+ mod_muc:broadcast_service_message(ServerHost, MUCHost, Message)
+ end, MUCHosts)
end,
- ejabberd_config:get_myhosts()).
+ ejabberd_option:hosts()).
%%%
%%% ejabberd_update
@@ -512,10 +521,31 @@ registered_users(Host) ->
lists:map(fun({U, _S}) -> U end, SUsers).
registered_vhosts() ->
- ejabberd_config:get_myhosts().
+ ejabberd_option:hosts().
reload_config() ->
- ejabberd_config:reload_file().
+ case ejabberd_config:reload() of
+ ok -> {ok, ""};
+ Err ->
+ Reason = ejabberd_config:format_error(Err),
+ {invalid_config, Reason}
+ end.
+
+dump_config(Path) ->
+ case ejabberd_config:dump(Path) of
+ ok -> {ok, ""};
+ Err ->
+ Reason = ejabberd_config:format_error(Err),
+ {invalid_file, Reason}
+ end.
+
+convert_to_yaml(In, Out) ->
+ case ejabberd_config:convert_to_yaml(In, Out) of
+ ok -> {ok, ""};
+ Err ->
+ Reason = ejabberd_config:format_error(Err),
+ {invalid_config, Reason}
+ end.
%%%
%%% Cluster management
@@ -562,13 +592,13 @@ delete_expired_messages() ->
lists:foreach(
fun(Host) ->
{atomic, ok} = mod_offline:remove_expired_messages(Host)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
delete_old_messages(Days) ->
lists:foreach(
fun(Host) ->
{atomic, _} = mod_offline:remove_old_messages(Days, Host)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
%%%
%%% Mnesia management
@@ -602,10 +632,6 @@ restore_mnesia(Path) ->
case ejabberd_admin:restore(Path) of
{atomic, _} ->
{ok, ""};
- {error, Reason} ->
- String = io_lib:format("Can't restore backup from ~p at node ~p: ~p",
- [filename:absname(Path), node(), Reason]),
- {cannot_restore, String};
{aborted,{no_exists,Table}} ->
String = io_lib:format("Can't restore backup from ~p at node ~p: Table ~p does not exist.",
[filename:absname(Path), node(), Table]),
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 41e284de4..ebb5bbeb2 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -32,42 +32,48 @@
-export([start/2, prep_stop/1, stop/1]).
-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
%%%
%%% Application API
%%%
start(normal, _Args) ->
- {T1, _} = statistics(wall_clock),
- ejabberd_logger:start(),
- write_pid_file(),
- start_included_apps(),
- start_elixir_application(),
- ejabberd:check_app(ejabberd),
- setup_if_elixir_conf_used(),
- case ejabberd_config:start() of
- ok ->
- ejabberd_mnesia:start(),
- file_queue_init(),
- maybe_add_nameservers(),
- case ejabberd_sup:start_link() of
- {ok, SupPid} ->
- ejabberd_system_monitor:start(),
- register_elixir_config_hooks(),
- ejabberd_cluster:wait_for_sync(infinity),
- ejabberd_hooks:run(ejabberd_started, []),
- {T2, _} = statistics(wall_clock),
- ?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
- [ejabberd_config:get_version(),
- node(), (T2-T1)/1000]),
- lists:foreach(fun erlang:garbage_collect/1, processes()),
- {ok, SupPid};
- Err ->
- ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
- ejabberd:halt()
- end;
- {error, Reason} ->
- ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Reason]),
+ try
+ {T1, _} = statistics(wall_clock),
+ ejabberd_logger:start(),
+ write_pid_file(),
+ start_included_apps(),
+ start_elixir_application(),
+ setup_if_elixir_conf_used(),
+ case ejabberd_config:load() of
+ ok ->
+ ejabberd_mnesia:start(),
+ file_queue_init(),
+ maybe_add_nameservers(),
+ case ejabberd_sup:start_link() of
+ {ok, SupPid} ->
+ ejabberd_system_monitor:start(),
+ register_elixir_config_hooks(),
+ ejabberd_cluster:wait_for_sync(infinity),
+ ejabberd_hooks:run(ejabberd_started, []),
+ ejabberd:check_apps(),
+ {T2, _} = statistics(wall_clock),
+ ?INFO_MSG("ejabberd ~s is started in the node ~p in ~.2fs",
+ [ejabberd_option:version(),
+ node(), (T2-T1)/1000]),
+ {ok, SupPid};
+ Err ->
+ ?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
+ ejabberd:halt()
+ end;
+ Err ->
+ ?CRITICAL_MSG("Failed to start ejabberd application: ~s",
+ [ejabberd_config:format_error(Err)]),
+ ejabberd:halt()
+ end
+ catch throw:{?MODULE, Error} ->
+ ?DEBUG("Failed to start ejabberd application: ~p", [Error]),
ejabberd:halt()
end;
start(_, _) ->
@@ -92,18 +98,15 @@ start_included_apps() ->
prep_stop(State) ->
ejabberd_hooks:run(ejabberd_stopping, []),
ejabberd_listener:stop_listeners(),
- ejabberd_sm:stop(),
+ _ = ejabberd_sm:stop(),
gen_mod:stop_modules(),
State.
%% All the processes were killed when this function is called
stop(_State) ->
?INFO_MSG("ejabberd ~s is stopped in the node ~p",
- [ejabberd_config:get_version(), node()]),
- delete_pid_file(),
- %%ejabberd_debug:stop(),
- ok.
-
+ [ejabberd_option:version(), node()]),
+ delete_pid_file().
%%%
%%% Internal functions
@@ -134,13 +137,13 @@ write_pid_file() ->
end.
write_pid_file(Pid, PidFilename) ->
- case file:open(PidFilename, [write]) of
- {ok, Fd} ->
- io:format(Fd, "~s~n", [Pid]),
- file:close(Fd);
- {error, Reason} ->
- ?ERROR_MSG("Cannot write PID file ~s~nReason: ~p", [PidFilename, Reason]),
- throw({cannot_write_pid_file, PidFilename, Reason})
+ case file:write_file(PidFilename, io_lib:format("~s~n", [Pid])) of
+ ok ->
+ ok;
+ {error, Reason} = Err ->
+ ?CRITICAL_MSG("Cannot write PID file ~s: ~s",
+ [PidFilename, file:format_error(Reason)]),
+ throw({?MODULE, Err})
end.
delete_pid_file() ->
@@ -152,34 +155,42 @@ delete_pid_file() ->
end.
file_queue_init() ->
- QueueDir = case ejabberd_config:queue_dir() of
+ QueueDir = case ejabberd_option:queue_dir() of
undefined ->
MnesiaDir = mnesia:system_info(directory),
filename:join(MnesiaDir, "queue");
Path ->
Path
end,
- p1_queue:start(QueueDir).
+ case p1_queue:start(QueueDir) of
+ ok -> ok;
+ Err -> throw({?MODULE, Err})
+ end.
+
+-ifdef(ELIXIR_ENABLED).
+is_using_elixir_config() ->
+ Config = ejabberd_config:path(),
+ 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config).
setup_if_elixir_conf_used() ->
- case ejabberd_config:is_using_elixir_config() of
+ case is_using_elixir_config() of
true -> 'Elixir.Ejabberd.Config.Store':start_link();
false -> ok
end.
register_elixir_config_hooks() ->
- case ejabberd_config:is_using_elixir_config() of
+ case is_using_elixir_config() of
true -> 'Elixir.Ejabberd.Config':start_hooks();
false -> ok
end.
start_elixir_application() ->
- case ejabberd_config:is_elixir_enabled() of
- true ->
- case application:ensure_started(elixir) of
- ok -> ok;
- {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", [])
- end;
- _ ->
- ok
+ case application:ensure_started(elixir) of
+ ok -> ok;
+ {error, _Msg} -> ?ERROR_MSG("Elixir application not started.", [])
end.
+-else.
+setup_if_elixir_conf_used() -> ok.
+register_elixir_config_hooks() -> ok.
+start_elixir_application() -> ok.
+-endif.
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index a3256364e..82ec5393e 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -25,7 +25,6 @@
-module(ejabberd_auth).
-behaviour(gen_server).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
@@ -47,7 +46,7 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
--export([auth_modules/1, opt_type/1]).
+-export([auth_modules/1]).
-include("scram.hrl").
-include("logger.hrl").
@@ -107,7 +106,7 @@ init([]) ->
fun(Host, Acc) ->
Modules = auth_modules(Host),
maps:put(Host, Modules, Acc)
- end, #{}, ejabberd_config:get_myhosts()),
+ end, #{}, ejabberd_option:hosts()),
lists:foreach(
fun({Host, Modules}) ->
start(Host, Modules)
@@ -141,7 +140,7 @@ handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
stop(Host, OldModules -- NewModules),
reload(Host, misc:intersection(OldModules, NewModules)),
maps:put(Host, NewModules, Acc)
- end, HostModules, ejabberd_config:get_myhosts()),
+ end, HostModules, ejabberd_option:hosts()),
init_cache(NewHostModules),
{noreply, State#state{host_modules = NewHostModules}};
handle_cast(Msg, State) ->
@@ -530,7 +529,7 @@ backend_type(Mod) ->
-spec password_format(binary() | global) -> plain | scram.
password_format(LServer) ->
- ejabberd_config:get_option({auth_password_format, LServer}, plain).
+ ejabberd_option:auth_password_format(LServer).
%%%----------------------------------------------------------------------
%%% Backend calls
@@ -767,15 +766,9 @@ init_cache(HostModules) ->
-spec cache_opts() -> [proplists:property()].
cache_opts() ->
- MaxSize = ejabberd_config:get_option(
- auth_cache_size,
- ejabberd_config:cache_size(global)),
- CacheMissed = ejabberd_config:get_option(
- auth_cache_missed,
- ejabberd_config:cache_missed(global)),
- LifeTime = case ejabberd_config:get_option(
- auth_cache_life_time,
- ejabberd_config:cache_life_time(global)) of
+ MaxSize = ejabberd_option:auth_cache_size(),
+ CacheMissed = ejabberd_option:auth_cache_missed(),
+ LifeTime = case ejabberd_option:auth_cache_life_time() of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -803,9 +796,7 @@ use_cache(Mod, LServer) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(LServer);
false ->
- ejabberd_config:get_option(
- {auth_use_cache, LServer},
- ejabberd_config:use_cache(LServer))
+ ejabberd_option:auth_use_cache(LServer)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -827,13 +818,12 @@ auth_modules() ->
lists:flatmap(
fun(Host) ->
[{Host, Mod} || Mod <- auth_modules(Host)]
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec auth_modules(binary()) -> [module()].
auth_modules(Server) ->
LServer = jid:nameprep(Server),
- Default = ejabberd_config:default_db(LServer, ?MODULE),
- Methods = ejabberd_config:get_option({auth_method, LServer}, [Default]),
+ Methods = ejabberd_option:auth_method(LServer),
[ejabberd:module_name([<<"ejabberd">>, <<"auth">>,
misc:atom_to_binary(M)])
|| M <- Methods].
@@ -911,31 +901,3 @@ import(Server, {sql, _}, riak, <<"users">>, Fields) ->
ejabberd_auth_riak:import(Server, Fields);
import(_LServer, {sql, _}, sql, <<"users">>, _) ->
ok.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(auth_method) ->
- fun (V) when is_list(V) ->
- lists:map(fun(M) -> ejabberd_config:v_db(?MODULE, M) end, V);
- (V) -> [ejabberd_config:v_db(?MODULE, V)]
- end;
-opt_type(auth_password_format) ->
- fun (plain) -> plain;
- (scram) -> scram
- end;
-opt_type(auth_use_cache) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(auth_cache_missed) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(auth_cache_life_time) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
-opt_type(auth_cache_size) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
-opt_type(_) ->
- [auth_method, auth_password_format, auth_use_cache,
- auth_cache_missed, auth_cache_life_time, auth_cache_size].
diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl
index 767d99bf2..805076aff 100644
--- a/src/ejabberd_auth_anonymous.erl
+++ b/src/ejabberd_auth_anonymous.erl
@@ -25,7 +25,6 @@
-module(ejabberd_auth_anonymous).
--behaviour(ejabberd_config).
-behaviour(ejabberd_auth).
-author('mickael.remond@process-one.net').
@@ -43,7 +42,7 @@
-export([login/2, check_password/4, user_exists/2,
get_users/2, count_users/2, store_type/1,
- plain_password_required/1, opt_type/1]).
+ plain_password_required/1]).
-include("logger.hrl").
-include("jid.hrl").
@@ -98,12 +97,12 @@ is_login_anonymous_enabled(Host) ->
%% Return the anonymous protocol to use: sasl_anon|login_anon|both
%% defaults to login_anon
anonymous_protocol(Host) ->
- ejabberd_config:get_option({anonymous_protocol, Host}, sasl_anon).
+ ejabberd_option:anonymous_protocol(Host).
%% Return true if multiple connections have been allowed in the config file
%% defaults to false
allow_multiple_connections(Host) ->
- ejabberd_config:get_option({allow_multiple_connections, Host}, false).
+ ejabberd_option:allow_multiple_connections(Host).
anonymous_user_exist(User, Server) ->
lists:any(
@@ -188,14 +187,3 @@ plain_password_required(_) ->
store_type(_) ->
external.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(allow_multiple_connections) ->
- fun (V) when is_boolean(V) -> V end;
-opt_type(anonymous_protocol) ->
- fun (sasl_anon) -> sasl_anon;
- (login_anon) -> login_anon;
- (both) -> both
- end;
-opt_type(_) ->
- [allow_multiple_connections, anonymous_protocol].
diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl
index 6b2e2852e..bae540e21 100644
--- a/src/ejabberd_auth_external.erl
+++ b/src/ejabberd_auth_external.erl
@@ -25,15 +25,13 @@
-module(ejabberd_auth_external).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-behaviour(ejabberd_auth).
-export([start/1, stop/1, reload/1, set_password/3, check_password/4,
try_register/3, user_exists/2, remove_user/2,
- store_type/1, plain_password_required/1, opt_type/1]).
+ store_type/1, plain_password_required/1]).
-include("logger.hrl").
@@ -91,7 +89,7 @@ check_password_extauth(User, _AuthzId, Server, Password) ->
case extauth:check_password(User, Server, Password) of
Res when is_boolean(Res) -> Res;
{error, Reason} ->
- failure(User, Server, check_password, Reason),
+ _ = failure(User, Server, check_password, Reason),
false
end;
true ->
@@ -103,26 +101,3 @@ failure(User, Server, Fun, Reason) ->
?ERROR_MSG("External authentication program failed when calling "
"'~s' for ~s@~s: ~p", [Fun, User, Server, Reason]),
{error, db_failure}.
-
-opt_type(extauth_cache) ->
- ?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, "
- "use authentication or global cache configuration "
- "options: auth_use_cache, auth_cache_life_time, "
- "use_cache, cache_life_time, and so on", []),
- fun (false) -> false;
- (I) when is_integer(I), I >= 0 -> I
- end;
-opt_type(extauth_instances) ->
- ?WARNING_MSG("option 'extauth_instances' is deprecated and has no effect, "
- "use 'extauth_pool_size'", []),
- fun (V) when is_integer(V), V > 0 -> V end;
-opt_type(extauth_program) ->
- fun (V) -> binary_to_list(iolist_to_binary(V)) end;
-opt_type(extauth_pool_name) ->
- fun (V) -> iolist_to_binary(V) end;
-opt_type(extauth_pool_size) ->
- fun(I) when is_integer(I), I>0 -> I end;
-opt_type(_) ->
- [extauth_program, extauth_pool_size, extauth_pool_name,
- %% Deprecated:
- extauth_cache, extauth_instances].
diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl
index 06000c7f1..d52e7a1c0 100644
--- a/src/ejabberd_auth_ldap.erl
+++ b/src/ejabberd_auth_ldap.erl
@@ -25,8 +25,6 @@
-module(ejabberd_auth_ldap).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-behaviour(gen_server).
@@ -39,8 +37,7 @@
-export([start/1, stop/1, start_link/1, set_password/3,
check_password/4, user_exists/2,
get_users/2, count_users/2,
- store_type/1, plain_password_required/1,
- opt_type/1]).
+ store_type/1, plain_password_required/1]).
-include("logger.hrl").
@@ -60,7 +57,6 @@
uids = [] :: [{binary()} | {binary(), binary()}],
ufilter = <<"">> :: binary(),
sfilter = <<"">> :: binary(),
- lfilter :: {any(), any()} | undefined,
deref_aliases = never :: never | searching | finding | always,
dn_filter :: binary() | undefined,
dn_filter_attrs = [] :: [binary()]}).
@@ -85,8 +81,10 @@ start(Host) ->
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
- supervisor:terminate_child(ejabberd_backend_sup, Proc),
- supervisor:delete_child(ejabberd_backend_sup, Proc).
+ case supervisor:terminate_child(ejabberd_backend_sup, Proc) of
+ ok -> supervisor:delete_child(ejabberd_backend_sup, Proc);
+ Err -> Err
+ end.
start_link(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
@@ -246,19 +244,12 @@ find_user_dn(User, State) ->
[#eldap_entry{attributes = Attrs,
object_name = DN}
| _]} ->
- dn_filter(DN, Attrs, State);
+ is_valid_dn(DN, Attrs, State);
_ -> false
end;
_ -> false
end.
-%% apply the dn filter and the local filter:
-dn_filter(DN, Attrs, State) ->
- case check_local_filter(Attrs, State) of
- false -> false;
- true -> is_valid_dn(DN, Attrs, State)
- end.
-
%% Check that the DN is valid, based on the dn filter
is_valid_dn(DN, _, #state{dn_filter = undefined}) -> DN;
is_valid_dn(DN, Attrs, State) ->
@@ -294,30 +285,6 @@ is_valid_dn(DN, Attrs, State) ->
_ -> false
end.
-%% The local filter is used to check an attribute in ejabberd
-%% and not in LDAP to limit the load on the LDAP directory.
-%% A local rule can be either:
-%% {equal, {"accountStatus",["active"]}}
-%% {notequal, {"accountStatus",["disabled"]}}
-%% {ldap_local_filter, {notequal, {"accountStatus",["disabled"]}}}
-check_local_filter(_Attrs,
- #state{lfilter = undefined}) ->
- true;
-check_local_filter(Attrs,
- #state{lfilter = LocalFilter}) ->
- {Operation, FilterMatch} = LocalFilter,
- local_filter(Operation, Attrs, FilterMatch).
-
-local_filter(equal, Attrs, FilterMatch) ->
- {Attr, Value} = FilterMatch,
- case lists:keysearch(Attr, 1, Attrs) of
- false -> false;
- {value, {Attr, Value}} -> true;
- _ -> false
- end;
-local_filter(notequal, Attrs, FilterMatch) ->
- not local_filter(equal, Attrs, FilterMatch).
-
result_attrs(#state{uids = UIDs,
dn_filter_attrs = DNFilterAttrs}) ->
lists:foldl(fun ({UID}, Acc) -> [UID | Acc];
@@ -329,25 +296,21 @@ result_attrs(#state{uids = UIDs,
%%% Auxiliary functions
%%%----------------------------------------------------------------------
parse_options(Host) ->
- Cfg = eldap_utils:get_config(Host, []),
+ Cfg = ?eldap_config(ejabberd_option, Host),
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
Bind_Eldap_ID = misc:atom_to_binary(
gen_mod:get_module_proc(Host, bind_ejabberd_auth_ldap)),
- UIDsTemp = ejabberd_config:get_option(
- {ldap_uids, Host}, [{<<"uid">>, <<"%u">>}]),
+ UIDsTemp = ejabberd_option:ldap_uids(Host),
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
SubFilter = eldap_utils:generate_subfilter(UIDs),
- UserFilter = case ejabberd_config:get_option({ldap_filter, Host}, <<"">>) of
+ UserFilter = case ejabberd_option:ldap_filter(Host) of
<<"">> ->
SubFilter;
F ->
<<"(&", SubFilter/binary, F/binary, ")">>
end,
- SearchFilter = eldap_filter:do_sub(UserFilter,
- [{<<"%u">>, <<"*">>}]),
- {DNFilter, DNFilterAttrs} =
- ejabberd_config:get_option({ldap_dn_filter, Host}, {undefined, []}),
- LocalFilter = ejabberd_config:get_option({ldap_local_filter, Host}),
+ SearchFilter = eldap_filter:do_sub(UserFilter, [{<<"%u">>, <<"*">>}]),
+ {DNFilter, DNFilterAttrs} = ejabberd_option:ldap_dn_filter(Host),
#state{host = Host, eldap_id = Eldap_ID,
bind_eldap_id = Bind_Eldap_ID,
servers = Cfg#eldap_config.servers,
@@ -359,19 +322,5 @@ parse_options(Host) ->
base = Cfg#eldap_config.base,
deref_aliases = Cfg#eldap_config.deref_aliases,
uids = UIDs, ufilter = UserFilter,
- sfilter = SearchFilter, lfilter = LocalFilter,
+ sfilter = SearchFilter,
dn_filter = DNFilter, dn_filter_attrs = DNFilterAttrs}.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(ldap_dn_filter) ->
- fun ([{DNF, DNFA}]) ->
- NewDNFA = case DNFA of
- undefined -> [];
- _ -> [iolist_to_binary(A) || A <- DNFA]
- end,
- NewDNF = eldap_utils:check_filter(DNF),
- {NewDNF, NewDNFA}
- end;
-opt_type(ldap_local_filter) -> fun (V) -> V end;
-opt_type(_) ->
- [ldap_dn_filter, ldap_local_filter].
diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl
index 1f75f686d..5c252b277 100644
--- a/src/ejabberd_auth_mnesia.erl
+++ b/src/ejabberd_auth_mnesia.erl
@@ -25,8 +25,6 @@
-module(ejabberd_auth_mnesia).
--compile([{parse_transform, ejabberd_sql_pt}]).
-
-author('alexey@process-one.net').
-behaviour(ejabberd_auth).
@@ -77,9 +75,7 @@ update_reg_users_counter_table(Server) ->
use_cache(Host) ->
case mnesia:table_info(passwd, storage_type) of
disc_only_copies ->
- ejabberd_config:get_option(
- {auth_use_cache, Host},
- ejabberd_config:use_cache(Host));
+ ejabberd_option:auth_use_cache(Host);
_ ->
false
end.
@@ -207,7 +203,7 @@ remove_user(User, Server) ->
need_transform(#reg_users_counter{}) ->
false;
-need_transform(#passwd{us = {U, S}, password = Pass}) ->
+need_transform({passwd, {U, S}, Pass}) ->
if is_binary(Pass) ->
case store_type(S) of
scram ->
@@ -234,7 +230,7 @@ need_transform(#passwd{us = {U, S}, password = Pass}) ->
true
end.
-transform(#passwd{us = {U, S}, password = Pass} = R)
+transform({passwd, {U, S}, Pass})
when is_list(U) orelse is_list(S) orelse is_list(Pass) ->
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
NewPass = case Pass of
@@ -248,7 +244,7 @@ transform(#passwd{us = {U, S}, password = Pass} = R)
_ ->
iolist_to_binary(Pass)
end,
- transform(R#passwd{us = NewUS, password = NewPass});
+ transform(#passwd{us = NewUS, password = NewPass});
transform(#passwd{us = {U, S}, password = Password} = P)
when is_binary(Password) ->
case store_type(S) of
diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl
index ffbab1f1f..840fa9f40 100644
--- a/src/ejabberd_auth_pam.erl
+++ b/src/ejabberd_auth_pam.erl
@@ -24,15 +24,12 @@
%%%-------------------------------------------------------------------
-module(ejabberd_auth_pam).
--behaviour(ejabberd_config).
-
-author('xram@jabber.ru').
-behaviour(ejabberd_auth).
-export([start/1, stop/1, check_password/4,
- user_exists/2, store_type/1, plain_password_required/1,
- opt_type/1]).
+ user_exists/2, store_type/1, plain_password_required/1]).
start(_Host) ->
ejabberd:start_app(epam).
@@ -77,15 +74,7 @@ store_type(_) -> external.
%% Internal functions
%%====================================================================
get_pam_service(Host) ->
- ejabberd_config:get_option({pam_service, Host}, <<"ejabberd">>).
+ ejabberd_option:pam_service(Host).
get_pam_userinfotype(Host) ->
- ejabberd_config:get_option({pam_userinfotype, Host}, username).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(pam_service) -> fun iolist_to_binary/1;
-opt_type(pam_userinfotype) ->
- fun (username) -> username;
- (jid) -> jid
- end;
-opt_type(_) -> [pam_service, pam_userinfotype].
+ ejabberd_option:pam_userinfotype(Host).
diff --git a/src/ejabberd_auth_riak.erl b/src/ejabberd_auth_riak.erl
index 29da504c9..00f3f5227 100644
--- a/src/ejabberd_auth_riak.erl
+++ b/src/ejabberd_auth_riak.erl
@@ -25,8 +25,6 @@
-module(ejabberd_auth_riak).
--compile([{parse_transform, ejabberd_sql_pt}]).
-
-author('alexey@process-one.net').
-behaviour(ejabberd_auth).
diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl
index f6f4f9efb..0d7ff1b81 100644
--- a/src/ejabberd_auth_sql.erl
+++ b/src/ejabberd_auth_sql.erl
@@ -25,17 +25,15 @@
-module(ejabberd_auth_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-author('alexey@process-one.net').
-behaviour(ejabberd_auth).
--behaviour(ejabberd_config).
-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, which_users_exists/2]).
+ convert_to_scram/1, export/1, which_users_exists/2]).
-include("scram.hrl").
-include("logger.hrl").
@@ -221,8 +219,7 @@ users_number(LServer) ->
LServer,
fun(pgsql, _) ->
case
- ejabberd_config:get_option(
- {pgsql_users_number_estimate, LServer}, false) of
+ ejabberd_option:pgsql_users_number_estimate(LServer) of
true ->
ejabberd_sql:sql_query_t(
?SQL("select @(reltuples :: bigint)d from pg_class"
@@ -349,8 +346,3 @@ export(_Server) ->
(_Host, _R) ->
[]
end}].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(pgsql_users_number_estimate) ->
- fun (V) when is_boolean(V) -> V end;
-opt_type(_) -> [pgsql_users_number_estimate].
diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl
index 578466bba..d119e90b8 100644
--- a/src/ejabberd_bosh.erl
+++ b/src/ejabberd_bosh.erl
@@ -91,8 +91,8 @@
xmpp_ver = <<"">> :: binary(),
inactivity_timer :: reference() | undefined,
wait_timer :: reference() | undefined,
- wait_timeout = ?DEFAULT_WAIT :: timeout(),
- inactivity_timeout :: timeout(),
+ wait_timeout = ?DEFAULT_WAIT :: pos_integer(),
+ inactivity_timeout :: pos_integer(),
prev_rid = 0 :: non_neg_integer(),
prev_key = <<"">> :: binary(),
prev_poll :: erlang:timestamp() | undefined,
@@ -274,8 +274,7 @@ init([#body{attrs = Attrs}, IP, SID]) ->
Socket = make_socket(self(), IP),
XMPPVer = get_attr('xmpp:version', Attrs),
XMPPDomain = get_attr(to, Attrs),
- {InBuf, Opts} = case gen_mod:get_module_opt(
- XMPPDomain, mod_bosh, prebind) of
+ {InBuf, Opts} = case mod_bosh_opt:prebind(XMPPDomain) of
true ->
JID = make_random_jid(XMPPDomain),
{buf_new(XMPPDomain), [{jid, JID} | Opts2]};
@@ -287,9 +286,8 @@ init([#body{attrs = Attrs}, IP, SID]) ->
case ejabberd_c2s:start(?MODULE, Socket, [{receiver, self()}|Opts]) of
{ok, C2SPid} ->
ejabberd_c2s:accept(C2SPid),
- Inactivity = gen_mod:get_module_opt(XMPPDomain,
- mod_bosh, max_inactivity),
- MaxConcat = gen_mod:get_module_opt(XMPPDomain, mod_bosh, max_concat),
+ Inactivity = mod_bosh_opt:max_inactivity(XMPPDomain),
+ MaxConcat = mod_bosh_opt:max_concat(XMPPDomain),
ShapedReceivers = buf_new(XMPPDomain, ?MAX_SHAPED_REQUESTS_QUEUE_LEN),
State = #state{host = XMPPDomain, sid = SID, ip = IP,
xmpp_ver = XMPPVer, el_ibuf = InBuf,
@@ -298,8 +296,12 @@ init([#body{attrs = Attrs}, IP, SID]) ->
shaped_receivers = ShapedReceivers,
shaper_state = ShaperState},
NewState = restart_inactivity_timer(State),
- mod_bosh:open_session(SID, self()),
- {ok, wait_for_session, NewState};
+ case mod_bosh:open_session(SID, self()) of
+ ok ->
+ {ok, wait_for_session, NewState};
+ {error, Reason} ->
+ {stop, Reason}
+ end;
{error, Reason} ->
{stop, Reason};
ignore ->
@@ -328,8 +330,7 @@ wait_for_session(#body{attrs = Attrs} = Req, From,
Wait == 0, Hold == 0 -> erlang:timestamp();
true -> undefined
end,
- MaxPause = gen_mod:get_module_opt(State#state.host,
- mod_bosh, max_pause),
+ MaxPause = mod_bosh_opt:max_pause(State#state.host),
Resp = #body{attrs =
[{sid, State#state.sid}, {wait, Wait},
{ver, ?BOSH_VERSION}, {polling, ?DEFAULT_POLLING},
@@ -887,7 +888,9 @@ decode_body(Data, Size, Type) ->
end.
decode(Data, xml) ->
- fxml_stream:parse_element(Data).
+ fxml_stream:parse_element(Data);
+decode(Data, json) ->
+ Data.
attrs_to_body_attrs(Attrs) ->
lists:foldl(fun (_, {error, Reason}) -> {error, Reason};
@@ -991,8 +994,7 @@ buf_new(Host) ->
buf_new(Host, unlimited).
buf_new(Host, Limit) ->
- QueueType = gen_mod:get_module_opt(
- Host, mod_bosh, queue_type),
+ QueueType = mod_bosh_opt:queue_type(Host),
p1_queue:new(QueueType, Limit).
buf_in(Xs, Buf) ->
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 492e2d893..7caa109bb 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -21,15 +21,13 @@
%%%-------------------------------------------------------------------
-module(ejabberd_c2s).
-behaviour(xmpp_stream_in).
--behaviour(ejabberd_config).
-behaviour(ejabberd_listener).
-
+-dialyzer([{no_fail_call, [stop/1, process_closed/2]},
+ {no_return, process_closed/2}]).
-protocol({rfc, 6121}).
%% ejabberd_listener callbacks
-export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]).
-%% ejabberd_config callbacks
--export([opt_type/1, transform_listen_option/2]).
%% xmpp_stream_in callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
@@ -109,8 +107,7 @@ resend_presence(Pid, To) ->
close(Ref) ->
xmpp_stream_in:close(Ref).
--spec close(pid(), atom()) -> ok;
- (state(), atom()) -> state().
+-spec close(pid(), atom()) -> ok.
close(Ref, Reason) ->
xmpp_stream_in:close(Ref, Reason).
@@ -319,37 +316,34 @@ tls_options(#{lserver := LServer, tls_options := DefaultOpts,
TLSOpts1 = case {Encrypted, proplists:get_value(certfile, DefaultOpts)} of
{true, CertFile} when CertFile /= undefined -> DefaultOpts;
{_, _} ->
- case get_certfile(LServer) of
- undefined -> DefaultOpts;
- CertFile -> lists:keystore(certfile, 1, DefaultOpts,
- {certfile, CertFile})
+ case ejabberd_pkix:get_certfile(LServer) of
+ error -> DefaultOpts;
+ {ok, CertFile} ->
+ lists:keystore(certfile, 1, DefaultOpts,
+ {certfile, CertFile})
end
end,
- TLSOpts2 = case ejabberd_config:get_option(
- {c2s_ciphers, LServer}) of
+ TLSOpts2 = case ejabberd_option:c2s_ciphers(LServer) of
undefined -> TLSOpts1;
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
{ciphers, Ciphers})
end,
- TLSOpts3 = case ejabberd_config:get_option(
- {c2s_protocol_options, LServer}) of
+ TLSOpts3 = case ejabberd_option:c2s_protocol_options(LServer) of
undefined -> TLSOpts2;
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
{protocol_options, ProtoOpts})
end,
- TLSOpts4 = case ejabberd_config:get_option(
- {c2s_dhfile, LServer}) of
+ TLSOpts4 = case ejabberd_option:c2s_dhfile(LServer) of
undefined -> TLSOpts3;
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
{dhfile, DHFile})
end,
- TLSOpts5 = case ejabberd_config:get_option(
- {c2s_cafile, LServer}) of
+ TLSOpts5 = case ejabberd_option:c2s_cafile(LServer) of
undefined -> TLSOpts4;
CAFile -> lists:keystore(cafile, 1, TLSOpts4,
{cafile, CAFile})
end,
- case ejabberd_config:get_option({c2s_tls_compression, LServer}) of
+ case ejabberd_option:c2s_tls_compression(LServer) of
undefined -> TLSOpts5;
false -> [compression_none | TLSOpts5];
true -> lists:delete(compression_none, TLSOpts5)
@@ -376,7 +370,7 @@ authenticated_stream_features(#{lserver := LServer}) ->
sasl_mechanisms(Mechs, #{lserver := LServer} = State) ->
Type = ejabberd_auth:store_type(LServer),
- Mechs1 = ejabberd_config:get_option({disable_sasl_mechanisms, LServer}, []),
+ Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer),
%% I re-created it from cyrsasl ets magic, but I think it's wrong
%% TODO: need to check before 18.09 release
lists:filter(
@@ -423,9 +417,8 @@ bind(R, #{user := U, server := S, access := Access, lang := Lang,
{error, xmpp:err_conflict(), State};
{accept_resource, Resource} ->
JID = jid:make(U, S, Resource),
- case acl:access_matches(Access,
- #{usr => jid:split(JID), ip => IP},
- LServer) of
+ case acl:match_rule(LServer, Access,
+ #{usr => jid:split(JID), ip => IP}) of
allow ->
State1 = open_session(State#{resource => Resource,
sid => ejabberd_sm:make_sid()}),
@@ -538,14 +531,14 @@ init([State, Opts]) ->
TLSRequired = proplists:get_bool(starttls_required, Opts),
TLSVerify = proplists:get_bool(tls_verify, Opts),
Zlib = proplists:get_bool(zlib, Opts),
- Timeout = ejabberd_config:negotiation_timeout(),
+ Timeout = ejabberd_option:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
tls_required => TLSRequired,
tls_enabled => TLSEnabled,
tls_verify => TLSVerify,
pres_a => ?SETS:new(),
zlib => Zlib,
- lang => ejabberd_config:get_mylang(),
+ lang => ejabberd_option:language(),
server => ejabberd_config:get_myname(),
lserver => ejabberd_config:get_myname(),
access => Access,
@@ -668,36 +661,39 @@ route_probe_reply(_, _) ->
-spec process_presence_out(state(), presence()) -> state().
process_presence_out(#{lserver := LServer, jid := JID,
- lang := Lang, pres_a := PresA} = State,
+ lang := Lang, pres_a := PresA} = State0,
#presence{from = From, to = To, type = Type} = Pres) ->
- if Type == subscribe; Type == subscribed;
- Type == unsubscribe; Type == unsubscribed ->
- Access = gen_mod:get_module_opt(LServer, mod_roster, access),
- MyBareJID = jid:remove_resource(JID),
- case acl:match_rule(LServer, Access, MyBareJID) of
- deny ->
- AccessErrTxt = <<"Access denied by service policy">>,
- AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
- send_error(State, Pres, AccessErr);
- allow ->
- ejabberd_hooks:run(roster_out_subscription, LServer, [Pres])
- end;
- true -> ok
- end,
- case privacy_check_packet(State, Pres, out) of
+ State1 =
+ if Type == subscribe; Type == subscribed;
+ Type == unsubscribe; Type == unsubscribed ->
+ Access = mod_roster_opt:access(LServer),
+ MyBareJID = jid:remove_resource(JID),
+ case acl:match_rule(LServer, Access, MyBareJID) of
+ deny ->
+ AccessErrTxt = <<"Access denied by service policy">>,
+ AccessErr = xmpp:err_forbidden(AccessErrTxt, Lang),
+ send_error(State0, Pres, AccessErr);
+ allow ->
+ ejabberd_hooks:run(roster_out_subscription, LServer, [Pres]),
+ State0
+ end;
+ true ->
+ State0
+ end,
+ case privacy_check_packet(State1, Pres, out) of
deny ->
PrivErrTxt = <<"Your active privacy list has denied "
"the routing of this stanza.">>,
PrivErr = xmpp:err_not_acceptable(PrivErrTxt, Lang),
- send_error(State, Pres, PrivErr);
+ send_error(State1, Pres, PrivErr);
allow when Type == subscribe; Type == subscribed;
Type == unsubscribe; Type == unsubscribed ->
BareFrom = jid:remove_resource(From),
ejabberd_router:route(xmpp:set_from_to(Pres, BareFrom, To)),
- State;
+ State1;
allow when Type == error; Type == probe ->
ejabberd_router:route(Pres),
- State;
+ State1;
allow ->
ejabberd_router:route(Pres),
LTo = jid:tolower(To),
@@ -710,12 +706,12 @@ process_presence_out(#{lserver := LServer, jid := JID,
available -> ?SETS:add_element(LTo, PresA);
unavailable -> ?SETS:del_element(LTo, PresA)
end,
- State#{pres_a => A};
+ State1#{pres_a => A};
true ->
- State
+ State1
end;
true ->
- State
+ State1
end
end.
@@ -724,7 +720,7 @@ 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),
- ejabberd_sm:unset_presence(SID, U, S, R, Status),
+ _ = 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),
@@ -732,7 +728,7 @@ process_self_presence(#{lserver := LServer, sid := SID,
process_self_presence(#{lserver := LServer} = State,
#presence{type = available} = Pres) ->
PreviousPres = maps:get(pres_last, State, undefined),
- update_priority(State, Pres),
+ _ = update_priority(State, Pres),
{Pres1, State1} = ejabberd_hooks:run_fold(
c2s_self_presence, LServer, {Pres, State}, []),
State2 = State1#{pres_last => Pres1,
@@ -867,8 +863,7 @@ get_subscription(#jid{luser = LUser, lserver = LServer}, JID) ->
resource_conflict_action(U, S, R) ->
OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
true ->
- ejabberd_config:get_option(
- {resource_conflict, S}, acceptnew);
+ ejabberd_option:resource_conflict(S);
false ->
acceptnew
end,
@@ -934,9 +929,8 @@ fix_from_to(Pkt, _State) ->
change_shaper(#{shaper := ShaperName, ip := IP, lserver := LServer,
user := U, server := S, resource := R} = State) ->
JID = jid:make(U, S, R),
- Shaper = acl:access_matches(ShaperName,
- #{usr => jid:split(JID), ip => IP},
- LServer),
+ Shaper = ejabberd_shaper:match(LServer, ShaperName,
+ #{usr => jid:split(JID), ip => IP}),
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
-spec format_reason(state(), term()) -> binary().
@@ -951,84 +945,24 @@ format_reason(_, {shutdown, _}) ->
format_reason(_, _) ->
<<"internal server error">>.
--spec get_certfile(binary()) -> file:filename_all() | undefined.
-get_certfile(LServer) ->
- case ejabberd_pkix:get_certfile(LServer) of
- {ok, CertFile} ->
- CertFile;
- error ->
- ejabberd_config:get_option(
- {domain_certfile, LServer},
- ejabberd_config:get_option({c2s_certfile, LServer}))
- end.
-
-transform_listen_option(Opt, Opts) ->
- [Opt|Opts].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(c2s_ciphers) -> fun iolist_to_binary/1;
-opt_type(c2s_dhfile) -> fun misc:try_read_file/1;
-opt_type(c2s_cafile) -> fun misc:try_read_file/1;
-opt_type(c2s_protocol_options) ->
- fun (Options) -> str:join(Options, <<"|">>) end;
-opt_type(c2s_tls_compression) ->
- fun (true) -> true;
- (false) -> false
- end;
-opt_type(resource_conflict) ->
- fun (setresource) -> setresource;
- (closeold) -> closeold;
- (closenew) -> closenew;
- (acceptnew) -> acceptnew
- end;
-opt_type(disable_sasl_mechanisms) ->
- fun (V) when is_list(V) ->
- lists:map(fun (M) -> str:to_upper(M) end, V);
- (V) -> [str:to_upper(V)]
- end;
-opt_type(_) ->
- [c2s_ciphers, c2s_cafile, c2s_dhfile,
- c2s_protocol_options, c2s_tls_compression, resource_conflict,
- disable_sasl_mechanisms].
-
-listen_opt_type(certfile = Opt) ->
- fun(S) ->
- ?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
- "'certfiles' global option instead", [Opt, ?MODULE]),
- {ok, File} = ejabberd_pkix:add_certfile(S),
- File
- end;
-listen_opt_type(starttls) -> fun(B) when is_boolean(B) -> B end;
-listen_opt_type(starttls_required) -> fun(B) when is_boolean(B) -> B end;
-listen_opt_type(tls_verify) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(starttls) ->
+ econf:bool();
+listen_opt_type(starttls_required) ->
+ econf:bool();
+listen_opt_type(tls_verify) ->
+ econf:bool();
listen_opt_type(zlib) ->
- fun(true) ->
- ejabberd:start_app(ezlib),
- true;
- (false) ->
- false
- end;
-listen_opt_type(stream_management) ->
- fun(B) when is_boolean(B) ->
- ?ERROR_MSG("Listening option 'stream_management' is ignored: "
- "use mod_stream_mgmt module", []),
- B
- end;
-listen_opt_type(O) ->
- MgmtOpts = mod_stream_mgmt:mod_options(ejabberd_config:get_myname()),
- case lists:keymember(O, 1, MgmtOpts) of
- true ->
- fun(V) ->
- ?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
- "option from mod_stream_mgmt module", [O, O]),
- (mod_stream_mgmt:mod_opt_type(O))(V)
- end
- end.
+ econf:and_then(
+ econf:bool(),
+ fun(false) -> false;
+ (true) ->
+ ejabberd:start_app(ezlib),
+ true
+ end).
listen_options() ->
[{access, all},
{shaper, none},
- {certfile, undefined},
{ciphers, undefined},
{dhfile, undefined},
{cafile, undefined},
@@ -1040,5 +974,4 @@ listen_options() ->
{tls_verify, false},
{zlib, false},
{max_stanza_size, infinity},
- {max_fsm_queue, 5000}|
- mod_stream_mgmt:mod_options(ejabberd_config:get_myname())].
+ {max_fsm_queue, 5000}].
diff --git a/src/ejabberd_c2s_config.erl b/src/ejabberd_c2s_config.erl
index d6782de4f..e3f982ebf 100644
--- a/src/ejabberd_c2s_config.erl
+++ b/src/ejabberd_c2s_config.erl
@@ -33,7 +33,7 @@
%% Get first c2s configuration limitations to apply it to other c2s
%% connectors.
get_c2s_limits() ->
- C2SFirstListen = ejabberd_config:get_option(listen, []),
+ C2SFirstListen = ejabberd_option:listen(),
case lists:keysearch(ejabberd_c2s, 2, C2SFirstListen) of
false -> [];
{value, {_Port, ejabberd_c2s, Opts}} ->
@@ -41,23 +41,12 @@ get_c2s_limits() ->
end.
%% Only get access, shaper and max_stanza_size values
-
select_opts_values(Opts) ->
- select_opts_values(Opts, []).
-
-select_opts_values([], SelectedValues) ->
- SelectedValues;
-select_opts_values([{access, Value} | Opts],
- SelectedValues) ->
- select_opts_values(Opts,
- [{access, Value} | SelectedValues]);
-select_opts_values([{shaper, Value} | Opts],
- SelectedValues) ->
- select_opts_values(Opts,
- [{shaper, Value} | SelectedValues]);
-select_opts_values([{max_stanza_size, Value} | Opts],
- SelectedValues) ->
- select_opts_values(Opts,
- [{max_stanza_size, Value} | SelectedValues]);
-select_opts_values([_Opt | Opts], SelectedValues) ->
- select_opts_values(Opts, SelectedValues).
+ maps:fold(
+ fun(Opt, Val, Acc) when Opt == access;
+ Opt == shaper;
+ Opt == max_stanza_size ->
+ [{Opt, Val}|Acc];
+ (_, _, Acc) ->
+ Acc
+ end, [], Opts).
diff --git a/src/ejabberd_captcha.erl b/src/ejabberd_captcha.erl
index 84317eb55..2d9a97fe3 100644
--- a/src/ejabberd_captcha.erl
+++ b/src/ejabberd_captcha.erl
@@ -25,8 +25,6 @@
-module(ejabberd_captcha).
--behaviour(ejabberd_config).
-
-protocol({xep, 158, '1.0'}).
-behaviour(gen_server).
@@ -41,7 +39,7 @@
-export([create_captcha/6, build_captcha_html/2,
check_captcha/2, process_reply/1, process/2,
is_feature_available/0, create_captcha_x/5,
- opt_type/1, host_up/1, host_down/1,
+ host_up/1, host_down/1,
config_reloaded/0, process_iq/1]).
-include("xmpp.hrl").
@@ -52,6 +50,7 @@
-define(LIMIT_PERIOD, 60*1000*1000).
-type image_error() :: efbig | enodata | limit | malformed_image | timeout.
+-type priority() :: neg_integer().
-record(state, {limits = treap:empty() :: treap:treap(),
enabled = false :: boolean()}).
@@ -66,11 +65,11 @@ start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [],
[]).
--spec captcha_text(undefined | binary()) -> binary().
+-spec captcha_text(binary()) -> binary().
captcha_text(Lang) ->
translate:translate(Lang, <<"Enter the text you see">>).
--spec mk_ocr_field(binary() | undefined, binary(), binary()) -> xdata_field().
+-spec mk_ocr_field(binary(), binary(), binary()) -> xdata_field().
mk_ocr_field(Lang, CID, Type) ->
URI = #media_uri{type = Type, uri = <<"cid:", CID/binary>>},
#xdata_field{var = <<"ocr">>,
@@ -79,13 +78,13 @@ mk_ocr_field(Lang, CID, Type) ->
required = true,
sub_els = [#media{uri = [URI]}]}.
+-spec mk_field(_, binary(), binary()) -> xdata_field().
mk_field(Type, Var, Value) ->
#xdata_field{type = Type, var = Var, values = [Value]}.
-spec create_captcha(binary(), jid(), jid(),
binary(), any(), any()) -> {error, image_error()} |
- {ok, binary(), [text()], [xmlel()]}.
-
+ {ok, binary(), [text()], [xmpp_element()]}.
create_captcha(SID, From, To, Lang, Limiter, Args) ->
case create_image(Limiter) of
{ok, Type, Key, Image} ->
@@ -116,8 +115,7 @@ create_captcha(SID, From, To, Lang, Limiter, Args) ->
end.
-spec create_captcha_x(binary(), jid(), binary(), any(), xdata()) ->
- {ok, xdata()} | {error, image_error()}.
-
+ {ok, [xmpp_element()]} | {error, image_error()}.
create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
case create_image(Limiter) of
{ok, Type, Key, Image} ->
@@ -151,7 +149,7 @@ create_captcha_x(SID, To, Lang, Limiter, #xdata{fields = Fs} = X) ->
-spec build_captcha_html(binary(), binary()) -> captcha_not_found |
{xmlel(),
- {xmlel(), xmlel(),
+ {xmlel(), cdata(),
xmlel(), xmlel()}}.
build_captcha_html(Id, Lang) ->
@@ -161,7 +159,7 @@ build_captcha_html(Id, Lang) ->
attrs =
[{<<"src">>, get_url(<<Id/binary, "/image">>)}],
children = []},
- TextEl = {xmlcdata, captcha_text(Lang)},
+ Text = {xmlcdata, captcha_text(Lang)},
IdEl = #xmlel{name = <<"input">>,
attrs =
[{<<"type">>, <<"hidden">>}, {<<"name">>, <<"id">>},
@@ -181,7 +179,7 @@ build_captcha_html(Id, Lang) ->
[ImgEl,
#xmlel{name = <<"br">>, attrs = [],
children = []},
- TextEl,
+ Text,
#xmlel{name = <<"br">>, attrs = [],
children = []},
IdEl, KeyEl,
@@ -193,7 +191,7 @@ build_captcha_html(Id, Lang) ->
{<<"name">>, <<"enter">>},
{<<"value">>, <<"OK">>}],
children = []}]},
- {FormEl, {ImgEl, TextEl, IdEl, KeyEl}};
+ {FormEl, {ImgEl, Text, IdEl, KeyEl}};
_ -> captcha_not_found
end.
@@ -216,6 +214,7 @@ process_reply(#xcaptcha{xdata = #xdata{} = X}) ->
process_reply(_) ->
{error, malformed}.
+-spec process_iq(iq()) -> iq().
process_iq(#iq{type = set, lang = Lang, sub_els = [#xcaptcha{} = El]} = IQ) ->
case process_reply(El) of
ok ->
@@ -238,7 +237,7 @@ process(_Handlers,
#request{method = 'GET', lang = Lang,
path = [_, Id]}) ->
case build_captcha_html(Id, Lang) of
- {FormEl, _} when is_tuple(FormEl) ->
+ {FormEl, _} ->
Form = #xmlel{name = <<"div">>,
attrs = [{<<"align">>, <<"center">>}],
children = [FormEl]},
@@ -292,8 +291,8 @@ config_reloaded() ->
gen_server:call(?MODULE, config_reloaded, timer:minutes(1)).
init([]) ->
- mnesia:delete_table(captcha),
- ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
+ _ = mnesia:delete_table(captcha),
+ _ = ets:new(captcha, [named_table, public, {keypos, #captcha.id}]),
case check_captcha_setup() of
true ->
register_handlers(),
@@ -364,27 +363,36 @@ terminate(_Reason, #state{enabled = Enabled}) ->
register_handlers() ->
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:add(host_down, ?MODULE, host_down, 50),
- lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()).
+ lists:foreach(fun host_up/1, ejabberd_option:hosts()).
unregister_handlers() ->
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 50),
- lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()).
+ lists:foreach(fun host_down/1, ejabberd_option:hosts()).
code_change(_OldVsn, State, _Extra) -> {ok, State}.
-create_image() -> create_image(undefined).
+-spec create_image() -> {ok, binary(), binary(), binary()} |
+ {error, image_error()}.
+create_image() ->
+ create_image(undefined).
+-spec create_image(term()) -> {ok, binary(), binary(), binary()} |
+ {error, image_error()}.
create_image(Limiter) ->
Key = str:substr(p1_rand:get_string(), 1, 6),
create_image(Limiter, Key).
+-spec create_image(term(), binary()) -> {ok, binary(), binary(), binary()} |
+ {error, image_error()}.
create_image(Limiter, Key) ->
case is_limited(Limiter) of
- true -> {error, limit};
- false -> do_create_image(Key)
+ true -> {error, limit};
+ false -> do_create_image(Key)
end.
+-spec do_create_image(binary()) -> {ok, binary(), binary(), binary()} |
+ {error, image_error()}.
do_create_image(Key) ->
FileName = get_prog_name(),
Cmd = lists:flatten(io_lib:format("~s ~s", [FileName, Key])),
@@ -416,7 +424,7 @@ do_create_image(Key) ->
end.
get_prog_name() ->
- case ejabberd_config:get_option(captcha_cmd) of
+ case ejabberd_option:captcha_cmd() of
undefined ->
?DEBUG("The option captcha_cmd is not configured, "
"but some module wants to use the CAPTCHA "
@@ -427,8 +435,9 @@ get_prog_name() ->
FileName
end.
+-spec get_url(binary()) -> binary().
get_url(Str) ->
- CaptchaHost = ejabberd_config:get_option(captcha_host, <<"">>),
+ CaptchaHost = ejabberd_option:captcha_host(),
case str:tokens(CaptchaHost, <<":">>) of
[Host] ->
<<"http://", Host/binary, "/captcha/", Str/binary>>;
@@ -453,7 +462,7 @@ get_transfer_protocol(PortString) ->
get_captcha_transfer_protocol(PortListeners).
get_port_listeners(PortNumber) ->
- AllListeners = ejabberd_config:get_option(listen, []),
+ AllListeners = ejabberd_option:listen(),
lists:filter(
fun({{Port, _IP, _Transport}, _Module, _Opts}) ->
Port == PortNumber
@@ -465,21 +474,26 @@ get_captcha_transfer_protocol([]) ->
"'captcha' option. Change the port number "
"or specify http:// in that option.">>);
get_captcha_transfer_protocol([{_, ejabberd_http, Opts} | Listeners]) ->
- case proplists:get_bool(captcha, Opts) of
- true ->
- case proplists:get_bool(tls, Opts) of
+ Handlers = maps:get(request_handlers, Opts, []),
+ case lists:any(
+ fun({_, ?MODULE}) -> true;
+ ({_, _}) -> false
+ end, Handlers) of
+ true ->
+ case maps:get(tls, Opts) of
true -> https;
false -> http
end;
- false -> get_captcha_transfer_protocol(Listeners)
+ false ->
+ get_captcha_transfer_protocol(Listeners)
end;
get_captcha_transfer_protocol([_ | Listeners]) ->
get_captcha_transfer_protocol(Listeners).
is_limited(undefined) -> false;
is_limited(Limiter) ->
- case ejabberd_config:get_option(captcha_limit) of
- undefined -> false;
+ case ejabberd_option:captcha_limit() of
+ infinity -> false;
Int ->
case catch gen_server:call(?MODULE,
{is_limited, Limiter, Int}, 5000)
@@ -494,12 +508,14 @@ is_limited(Limiter) ->
-define(MAX_FILE_SIZE, 64 * 1024).
+-spec cmd(string()) -> {ok, binary()} | {error, image_error()}.
cmd(Cmd) ->
Port = open_port({spawn, Cmd}, [stream, eof, binary]),
TRef = erlang:start_timer(?CMD_TIMEOUT, self(),
timeout),
recv_data(Port, TRef, <<>>).
+-spec recv_data(port(), reference(), binary()) -> {ok, binary()} | {error, image_error()}.
recv_data(Port, TRef, Buf) ->
receive
{Port, {data, Bytes}} ->
@@ -516,6 +532,8 @@ recv_data(Port, TRef, Buf) ->
return(Port, TRef, {error, timeout})
end.
+-spec return(port(), reference(), {ok, binary()} | {error, image_error()}) ->
+ {ok, binary()} | {error, image_error()}.
return(Port, TRef, Result) ->
misc:cancel_timer(TRef),
catch port_close(Port),
@@ -543,10 +561,11 @@ check_captcha_setup() ->
false
end.
+-spec lookup_captcha(binary()) -> {ok, #captcha{}} | {error, enoent}.
lookup_captcha(Id) ->
case ets:lookup(captcha, Id) of
[C] -> {ok, C};
- _ -> {error, enoent}
+ [] -> {error, enoent}
end.
-spec check_captcha(binary(), binary()) -> captcha_not_found |
@@ -554,8 +573,8 @@ lookup_captcha(Id) ->
captcha_non_valid.
check_captcha(Id, ProvidedKey) ->
- case ets:lookup(captcha, Id) of
- [#captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}] ->
+ case lookup_captcha(Id) of
+ {ok, #captcha{pid = Pid, args = Args, key = ValidKey, tref = Tref}} ->
ets:delete(captcha, Id),
misc:cancel_timer(Tref),
if ValidKey == ProvidedKey ->
@@ -565,10 +584,11 @@ check_captcha(Id, ProvidedKey) ->
callback(captcha_failed, Pid, Args),
captcha_non_valid
end;
- _ ->
+ {error, _} ->
captcha_not_found
end.
+-spec clean_treap(treap:treap(), priority()) -> treap:treap().
clean_treap(Treap, CleanPriority) ->
case treap:is_empty(Treap) of
true -> Treap;
@@ -588,16 +608,6 @@ callback(Result, Pid, Args) when is_pid(Pid) ->
callback(_, _, _) ->
ok.
+-spec now_priority() -> priority().
now_priority() ->
-erlang:system_time(microsecond).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(captcha_cmd) ->
- fun (FileName) ->
- F = iolist_to_binary(FileName), if F /= <<"">> -> F end
- end;
-opt_type(captcha_host) -> fun iolist_to_binary/1;
-opt_type(captcha_limit) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(_) ->
- [captcha_cmd, captcha_host, captcha_limit].
diff --git a/src/ejabberd_cluster.erl b/src/ejabberd_cluster.erl
index 10f945fe7..bf420e0f7 100644
--- a/src/ejabberd_cluster.erl
+++ b/src/ejabberd_cluster.erl
@@ -21,7 +21,6 @@
%%%
%%%-------------------------------------------------------------------
-module(ejabberd_cluster).
--behaviour(ejabberd_config).
-behaviour(gen_server).
%% API
@@ -33,7 +32,6 @@
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
--export([opt_type/1]).
-include("logger.hrl").
@@ -154,9 +152,9 @@ subscribe(Proc) ->
%%% gen_server API
%%%===================================================================
init([]) ->
- Ticktime = ejabberd_config:get_option(net_ticktime, 60),
- Nodes = ejabberd_config:get_option(cluster_nodes, []),
- net_kernel:set_net_ticktime(Ticktime),
+ Ticktime = ejabberd_option:net_ticktime(),
+ Nodes = ejabberd_option:cluster_nodes(),
+ _ = net_kernel:set_net_ticktime(Ticktime),
lists:foreach(fun(Node) ->
net_kernel:connect_node(Node)
end, Nodes),
@@ -195,19 +193,8 @@ code_change(_OldVsn, State, _Extra) ->
%%% Internal functions
%%%===================================================================
get_mod() ->
- Backend = ejabberd_config:get_option(cluster_backend, mnesia),
+ Backend = ejabberd_option:cluster_backend(),
list_to_atom("ejabberd_cluster_" ++ atom_to_list(Backend)).
rpc_timeout() ->
- timer:seconds(ejabberd_config:get_option(rpc_timeout, 5)).
-
-opt_type(net_ticktime) ->
- fun (P) when is_integer(P), P > 0 -> P end;
-opt_type(cluster_nodes) ->
- fun (Ns) -> true = lists:all(fun is_atom/1, Ns), Ns end;
-opt_type(rpc_timeout) ->
- fun (T) when is_integer(T), T > 0 -> T end;
-opt_type(cluster_backend) ->
- fun (T) -> ejabberd_config:v_db(?MODULE, T) end;
-opt_type(_) ->
- [rpc_timeout, cluster_backend, cluster_nodes, net_ticktime].
+ ejabberd_option:rpc_timeout().
diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl
index 921ed25c7..921047e9f 100644
--- a/src/ejabberd_commands.erl
+++ b/src/ejabberd_commands.erl
@@ -211,7 +211,6 @@
-author('badlop@process-one.net').
-behaviour(gen_server).
--behaviour(ejabberd_config).
-define(DEFAULT_VERSION, 1000000).
@@ -225,11 +224,8 @@
get_command_definition/2,
get_tags_commands/0,
get_tags_commands/1,
- get_exposed_commands/0,
register_commands/1,
unregister_commands/1,
- expose_commands/1,
- opt_type/1,
get_commands_spec/0,
get_commands_definition/0,
get_commands_definition/1,
@@ -245,6 +241,8 @@
-define(POLICY_ACCESS, '$policy').
+-type auth() :: {binary(), binary(), binary() | {oauth, binary()}, boolean()} | map().
+
-record(state, {}).
get_commands_spec() ->
@@ -292,7 +290,6 @@ init([]) ->
{attributes, record_info(fields, ejabberd_commands)},
{type, bag}]),
register_commands(get_commands_spec()),
- ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0),
{ok, #state{}}.
handle_call(_Request, _From, State) ->
@@ -338,29 +335,7 @@ unregister_commands(Commands) ->
mnesia:dirty_delete_object(Command)
end,
Commands),
- ejabberd_access_permissions:invalidate(),
- ok.
-
-%% @doc Expose command through ejabberd ReST API.
-%% Pass a list of command names or policy to expose.
--spec expose_commands([ejabberd_commands()|atom()|open|user|admin|restricted]) -> ok | {error, atom()}.
-
-expose_commands(Commands) ->
- Names = lists:map(fun(#ejabberd_commands{name = Name}) ->
- Name;
- (Name) when is_atom(Name) ->
- Name
- end,
- Commands),
-
- case ejabberd_config:add_option(commands, [{add_commands, Names}]) of
- ok ->
- ok;
- {aborted, Reason} ->
- {error, Reason};
- {atomic, Result} ->
- Result
- end.
+ ejabberd_access_permissions:invalidate().
-spec list_commands() -> [{atom(), [aterm()], string()}].
@@ -378,20 +353,6 @@ list_commands(Version) ->
args = Args,
desc = Desc} <- Commands].
-
--spec list_commands_policy(integer()) ->
- [{atom(), [aterm()], string(), atom()}].
-
-%% @doc Get a list of all the available commands, arguments,
-%% description, and policy in a given API version.
-list_commands_policy(Version) ->
- Commands = get_commands_definition(Version),
- [{Name, Args, Desc, Policy} ||
- #ejabberd_commands{name = Name,
- args = Args,
- desc = Desc,
- policy = Policy} <- Commands].
-
-spec get_command_format(atom()) -> {[aterm()], rterm()}.
%% @doc Get the format of arguments and result of a command.
@@ -402,12 +363,7 @@ get_command_format(Name, Version) when is_integer(Version) ->
get_command_format(Name, Auth) ->
get_command_format(Name, Auth, ?DEFAULT_VERSION).
--spec get_command_format(atom(),
- {binary(), binary(), binary(), boolean()} |
- noauth | admin,
- integer()) ->
- {[aterm()], rterm()}.
-
+-spec get_command_format(atom(), noauth | admin | auth(), integer()) -> {[aterm()], rterm()}.
get_command_format(Name, Auth, Version) ->
Admin = is_admin(Name, Auth, #{}),
#ejabberd_commands{args = Args,
@@ -422,12 +378,6 @@ get_command_format(Name, Auth, Version) ->
{Args, Result}
end.
-%% The oauth scopes for a command are the command name itself,
-%% also might include either 'ejabberd:user' or 'ejabberd:admin'
-cmd_scope(#ejabberd_commands{policy = Policy, name = Name}) ->
- [erlang:atom_to_binary(Name,utf8)] ++ [<<"ejabberd:user">> || Policy == user] ++ [<<"ejabberd:admin">> || Policy == admin].
-
-
-spec get_command_definition(atom()) -> ejabberd_commands().
%% @doc Get the definition record of a command.
@@ -533,95 +483,12 @@ get_tags_commands(Version) ->
%% -----------------------------
%% Access verification
%% -----------------------------
-
--spec check_auth(ejabberd_commands(), noauth) -> noauth_provided;
- (ejabberd_commands(),
- {binary(), binary(), binary(), boolean()}) ->
- {ok, binary(), binary()}.
-
-check_auth(_Command, noauth) ->
- no_auth_provided;
-check_auth(Command, {User, Server, {oauth, Token}, _}) ->
- ScopeList = cmd_scope(Command),
- case ejabberd_oauth:check_token(User, Server, ScopeList, Token) of
- true ->
- {ok, User, Server};
- _ ->
- throw({error, invalid_account_data})
- end;
-check_auth(_Command, {User, Server, Password, _}) when is_binary(Password) ->
- %% Check the account exists and password is valid
- case ejabberd_auth:check_password(User, <<"">>, Server, Password) of
- true -> {ok, User, Server};
- _ -> throw({error, invalid_account_data})
- end.
-
-get_exposed_commands() ->
- get_exposed_commands(?DEFAULT_VERSION).
-get_exposed_commands(Version) ->
- Opts0 = ejabberd_config:get_option(commands, []),
- Opts = lists:map(fun(V) when is_tuple(V) -> [V]; (V) -> V end, Opts0),
- CommandsList = list_commands_policy(Version),
- OpenCmds = [N || {N, _, _, open} <- CommandsList],
- RestrictedCmds = [N || {N, _, _, restricted} <- CommandsList],
- AdminCmds = [N || {N, _, _, admin} <- CommandsList],
- UserCmds = [N || {N, _, _, user} <- CommandsList],
- Cmds =
- lists:foldl(
- fun([{add_commands, L}], Acc) ->
- Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
- lists:usort(Cmds ++ Acc);
- ([{remove_commands, L}], Acc) ->
- Cmds = expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds),
- Acc -- Cmds;
- (_, Acc) -> Acc
- end, [], Opts),
- Cmds.
-
-%% This is used to allow mixing command policy (like open, user, admin, restricted), with command entry
-expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_atom(L) ->
- expand_commands([L], OpenCmds, UserCmds, AdminCmds, RestrictedCmds);
-expand_commands(L, OpenCmds, UserCmds, AdminCmds, RestrictedCmds) when is_list(L) ->
- lists:foldl(fun(open, Acc) -> OpenCmds ++ Acc;
- (user, Acc) -> UserCmds ++ Acc;
- (admin, Acc) -> AdminCmds ++ Acc;
- (restricted, Acc) -> RestrictedCmds ++ Acc;
- (Command, Acc) when is_atom(Command) ->
- [Command|Acc]
- end, [], L).
-
+-spec is_admin(atom(), admin | noauth | auth(), map()) -> boolean().
is_admin(_Name, admin, _Extra) ->
true;
is_admin(_Name, {_User, _Server, _, false}, _Extra) ->
false;
is_admin(_Name, Map, _extra) when is_map(Map) ->
true;
-is_admin(Name, Auth, Extra) ->
- {ACLInfo, Server} = case Auth of
- {U, S, _, _} ->
- {Extra#{usr=>jid:split(jid:make(U, S))}, S};
- _ ->
- {Extra, global}
- end,
- AdminAccess = ejabberd_config:get_option(commands_admin_access, none),
- case acl:access_matches(AdminAccess, ACLInfo, Server) of
- allow ->
- case catch check_auth(get_command_definition(Name), Auth) of
- {ok, _, _} -> true;
- no_auth_provided -> true;
- _ -> false
- end;
- deny -> false
- end.
-
-permission_addon() ->
- [{<<"'commands' option compatibility shim">>,
- {[],
- [{access, ejabberd_config:get_option(commands_admin_access, none)}],
- {get_exposed_commands(), []}}}].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(commands_admin_access) -> fun acl:access_rules_validator/1;
-opt_type(commands) ->
- fun(V) when is_list(V) -> V end;
-opt_type(_) -> [commands, commands_admin_access].
+is_admin(_Name, _Auth, _Extra) ->
+ false.
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index f4abf8d55..84fd13394 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -22,992 +22,209 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-
-module(ejabberd_config).
--author('alexey@process-one.net').
-
--export([start/0, load_file/1, reload_file/0, read_file/1,
- get_option/1, get_option/2, add_option/2, has_option/1,
- get_version/0, get_myhosts/0, get_myname/0,
- get_mylang/0, get_lang/1, get_uri/0, get_copyright/0,
- get_ejabberd_config_path/0, is_using_elixir_config/0,
- prepare_opt_val/4, transform_options/1, collect_options/1,
- convert_to_yaml/1, convert_to_yaml/2, v_db/2,
- env_binary_to_list/2, opt_type/1, may_hide_data/1,
- is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1, v_host/1, v_hosts/1,
- default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
- default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
- use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
- codec_options/1, get_plain_terms_file/2, negotiation_timeout/0,
- get_modules/0]).
-
--export([start/2]).
-
-%% The following functions are deprecated.
--export([add_global_option/2, add_local_option/2,
- get_global_option/2, get_local_option/2,
- get_global_option/3, get_local_option/3,
- get_option/3]).
--export([is_file_readable/1]).
-
--deprecated([{add_global_option, 2}, {add_local_option, 2},
- {get_global_option, 2}, {get_local_option, 2},
- {get_global_option, 3}, {get_local_option, 3},
- {get_option, 3}, {is_file_readable, 1}]).
-
--include("logger.hrl").
--include("ejabberd_config.hrl").
--include_lib("kernel/include/file.hrl").
--include_lib("kernel/include/inet.hrl").
--include_lib("stdlib/include/ms_transform.hrl").
--callback opt_type(atom()) -> fun((any()) -> any()) | [atom()].
--type bad_option() :: invalid_option | unknown_option.
+%% API
+-export([get_option/1]).
+-export([load/0, reload/0, format_error/1, path/0]).
+-export([env_binary_to_list/2]).
+-export([get_myname/0, get_uri/0, get_copyright/0]).
+-export([get_shared_key/0, get_node_start/0]).
+-export([fsm_limit_opts/1]).
+-export([codec_options/1]).
+-export([default_db/1, default_db/2, default_ram_db/1, default_ram_db/2]).
+-export([beams/1, validators/1, globals/0, may_hide_data/1]).
+-export([dump/0, dump/1, convert_to_yaml/1, convert_to_yaml/2]).
+
+%% Deprecated functions
+-export([get_option/2, set_option/2]).
+-export([get_version/0, get_myhosts/0]).
+-export([get_mylang/0, get_lang/1]).
+-deprecated([{get_option, 2},
+ {set_option, 2},
+ {get_version, 0},
+ {get_myhosts, 0},
+ {get_mylang, 0},
+ {get_lang, 1}]).
--spec start() -> ok | {error, bad_option()}.
-start() ->
- ConfigFile = get_ejabberd_config_path(),
+-include("logger.hrl").
+-include("ejabberd_stacktrace.hrl").
+
+-type option() :: atom() | {atom(), global | binary()}.
+-type error_reason() :: {merge_conflict, atom(), binary()} |
+ {old_config, file:filename_all(), term()} |
+ {write_file, file:filename_all(), term()} |
+ {exception, term(), term(), term()}.
+-type error_return() :: {error, econf:error_reason(), term()} |
+ {error, error_reason()}.
+
+-callback opt_type(atom()) -> econf:validator().
+-callback options() -> [atom() | {atom(), term()}].
+-callback globals() -> [atom()].
+
+-optional_callbacks([globals/0, opt_type/1]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+-spec load() -> ok | error_return().
+load() ->
+ load(path()).
+
+-spec load(file:filename()) -> ok | error_return().
+load(ConfigFile) ->
+ UnixTime = erlang:monotonic_time(second),
?INFO_MSG("Loading configuration from ~s", [ConfigFile]),
- catch ets:new(ejabberd_options,
- [named_table, public, {read_concurrency, true}]),
- catch ets:new(ejabberd_db_modules,
- [named_table, public, {read_concurrency, true}]),
- ext_mod:add_paths(),
+ _ = ets:new(ejabberd_options,
+ [named_table, public, {read_concurrency, true}]),
case load_file(ConfigFile) of
- {ok, State1} ->
- UnixTime = erlang:system_time(second),
- SharedKey = case erlang:get_cookie() of
- nocookie ->
- str:sha(p1_rand:get_string());
- Cookie ->
- str:sha(misc:atom_to_binary(Cookie))
- end,
- State2 = set_option({node_start, global}, UnixTime, State1),
- State3 = set_option({shared_key, global}, SharedKey, State2),
- set_opts(State3),
- ok;
- {error, _} = Err ->
- ?ERROR_MSG("Failed to load configuration file ~s", [ConfigFile]),
+ ok ->
+ set_shared_key(),
+ set_node_start(UnixTime),
+ ?INFO_MSG("Configuration loaded successfully", []);
+ Err ->
Err
end.
-%% When starting ejabberd for testing, we sometimes want to start a
-%% subset of hosts from the one define in the config file.
-%% This function override the host list read from config file by the
-%% one we provide.
-%% Hosts to start are defined in an ejabberd application environment
-%% variable 'hosts' to make it easy to ignore some host in config
-%% file.
-hosts_to_start(State) ->
- case application:get_env(ejabberd, hosts) of
- undefined ->
- %% Start all hosts as defined in config file
- State;
- {ok, Hosts} ->
- set_hosts_in_options(Hosts, State)
- end.
-
-%% @private
-%% At the moment, these functions are mainly used to setup unit tests.
--spec start(Hosts :: [binary()], Opts :: [acl:acl() | local_config()]) -> ok.
-start(Hosts, Opts) ->
- catch ets:new(ejabberd_options,
- [named_table, public, {read_concurrency, true}]),
- catch ets:new(ejabberd_db_modules,
- [named_table, public, {read_concurrency, true}]),
- UnixTime = erlang:system_time(second),
- SharedKey = case erlang:get_cookie() of
- nocookie ->
- str:sha(p1_rand:get_string());
- Cookie ->
- str:sha(misc:atom_to_binary(Cookie))
- end,
- State1 = #state{opts = Opts},
- State2 = set_option({node_start, global}, UnixTime, State1),
- State3 = set_option({shared_key, global}, SharedKey, State2),
- set_opts(set_hosts_in_options(Hosts, State3)),
- ok.
-
-%% @doc Get the filename of the ejabberd configuration file.
-%% The filename can be specified with: erl -config "/path/to/ejabberd.yml".
-%% It can also be specified with the environtment variable EJABBERD_CONFIG_PATH.
-%% If not specified, the default value 'ejabberd.yml' is assumed.
-%% @spec () -> string()
-get_ejabberd_config_path() ->
- case get_env_config() of
- {ok, Path} -> Path;
- undefined ->
- case os:getenv("EJABBERD_CONFIG_PATH") of
- false ->
- "ejabberd.yml";
- Path ->
- Path
- end
- end.
-
--spec get_env_config() -> {ok, string()} | undefined.
-get_env_config() ->
- %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml".
- case application:get_env(ejabberd, config) of
- R = {ok, _Path} -> R;
- undefined ->
- %% Second case for embbeding ejabberd in another app, for example for Elixir:
- %% config :ejabberd,
- %% file: "config/ejabberd.yml"
- application:get_env(ejabberd, file)
- end.
-
-%% @doc Read the ejabberd configuration file.
-%% It also includes additional configuration files and replaces macros.
-%% This function will crash if finds some error in the configuration file.
-%% @spec (File::string()) -> #state{}
-read_file(File) ->
- read_file(File, [{replace_macros, true},
- {include_files, true},
- {include_modules_configs, true}]).
-
-read_file(File, Opts) ->
- Terms1 = case is_elixir_enabled() of
- true ->
- case 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(File) of
- true ->
- 'Elixir.Ejabberd.Config':init(File),
- 'Elixir.Ejabberd.Config':get_ejabberd_opts();
- false ->
- get_plain_terms_file(File, Opts)
- end;
- false ->
- get_plain_terms_file(File, Opts)
- end,
- Terms_macros = case proplists:get_bool(replace_macros, Opts) of
- true -> replace_macros(Terms1);
- false -> Terms1
- end,
- Terms = transform_terms(Terms_macros),
- State = lists:foldl(fun search_hosts/2, #state{}, Terms),
- {Head, Tail} = lists:partition(
- fun({host_config, _}) -> false;
- ({append_host_config, _}) -> false;
- (_) -> true
- end, Terms),
- State1 = lists:foldl(fun process_term/2, State, Head ++ Tail),
- State1#state{opts = compact(State1#state.opts)}.
-
--spec load_file(string()) -> {ok, #state{}} | {error, bad_option()}.
-
-load_file(File) ->
- State0 = read_file(File),
- State1 = hosts_to_start(State0),
- AllMods = get_modules(),
- init_module_db_table(AllMods),
- ModOpts = get_modules_with_options(AllMods),
- validate_opts(State1, ModOpts).
-
--spec reload_file() -> ok | {error, bad_option()}.
-
-reload_file() ->
- Config = get_ejabberd_config_path(),
+-spec reload() -> ok | error_return().
+reload() ->
+ ConfigFile = path(),
+ ?INFO_MSG("Reloading configuration from ~s", [ConfigFile]),
OldHosts = get_myhosts(),
- case load_file(Config) of
- {error, _} = Err ->
- Err;
- {ok, State} ->
- set_opts(State),
+ case load_file(ConfigFile) of
+ ok ->
NewHosts = get_myhosts(),
+ AddHosts = NewHosts -- OldHosts,
+ DelHosts = OldHosts -- NewHosts,
lists:foreach(
fun(Host) ->
ejabberd_hooks:run(host_up, [Host])
- end, NewHosts -- OldHosts),
+ end, AddHosts),
lists:foreach(
fun(Host) ->
ejabberd_hooks:run(host_down, [Host])
- end, OldHosts -- NewHosts),
- ejabberd_hooks:run(config_reloaded, [])
+ end, DelHosts),
+ ejabberd_hooks:run(config_reloaded, []),
+ delete_host_options(DelHosts),
+ ?INFO_MSG("Configuration reloaded successfully", []);
+ Err ->
+ ?ERROR_MSG("Configuration reload aborted: ~s",
+ [format_error(Err)]),
+ Err
end.
--spec convert_to_yaml(file:filename()) -> ok | {error, any()}.
-
-convert_to_yaml(File) ->
- convert_to_yaml(File, stdout).
+-spec dump() -> ok | error_return().
+dump() ->
+ dump(stdout).
--spec convert_to_yaml(file:filename(),
- stdout | file:filename()) -> ok | {error, any()}.
+-spec dump(stdout | file:filename_all()) -> ok | error_return().
+dump(Output) ->
+ Y = get_option(yaml_config),
+ dump(Y, Output).
-convert_to_yaml(File, Output) ->
- State = read_file(File, [{include_files, false}]),
- Opts = [{K, V} || #local_config{key = K, value = V} <- State#state.opts],
- {GOpts, HOpts} = split_by_hosts(Opts),
- NewOpts = GOpts ++ lists:map(
- fun({Host, Opts1}) ->
- {host_config, [{Host, Opts1}]}
- end, HOpts),
- Data = fast_yaml:encode(lists:reverse(NewOpts)),
+-spec dump(term(), stdout | file:filename_all()) -> ok | error_return().
+dump(Y, Output) ->
+ Data = fast_yaml:encode(Y),
case Output of
- stdout ->
- io:format("~s~n", [Data]);
- FileName ->
- file:write_file(FileName, Data)
- end.
-
-%% Some Erlang apps expects env parameters to be list and not binary.
-%% For example, Mnesia is not able to start if mnesia dir is passed as a binary.
-%% However, binary is most common on Elixir, so it is easy to make a setup mistake.
--spec env_binary_to_list(atom(), atom()) -> {ok, any()}|undefined.
-env_binary_to_list(Application, Parameter) ->
- %% Application need to be loaded to allow setting parameters
- application:load(Application),
- case application:get_env(Application, Parameter) of
- {ok, Val} when is_binary(Val) ->
- BVal = binary_to_list(Val),
- application:set_env(Application, Parameter, BVal),
- {ok, BVal};
- Other ->
- Other
- end.
-
-%% @doc Read an ejabberd configuration file and return the terms.
-%% Input is an absolute or relative path to an ejabberd config file.
-%% Returns a list of plain terms,
-%% in which the options 'include_config_file' were parsed
-%% and the terms in those files were included.
-%% @spec(iolist()) -> [term()]
-get_plain_terms_file(File, Opts) when is_binary(File) ->
- get_plain_terms_file(binary_to_list(File), Opts);
-get_plain_terms_file(File1, Opts) ->
- File = get_absolute_path(File1),
- DontStopOnError = lists:member(dont_halt_on_error, Opts),
- case consult(File) of
- {ok, Terms} ->
- BinTerms1 = strings_to_binary(Terms),
- ModInc = case proplists:get_bool(include_modules_configs, Opts) of
- true ->
- Files = [{filename:rootname(filename:basename(F)), F}
- || F <- filelib:wildcard(ext_mod:config_dir() ++ "/*.{yml,yaml}")
- ++ filelib:wildcard(ext_mod:modules_dir() ++ "/*/conf/*.{yml,yaml}")],
- [proplists:get_value(F,Files) || F <- proplists:get_keys(Files)];
- _ ->
- []
- end,
- BinTerms = BinTerms1 ++ [{include_config_file, list_to_binary(V)} || V <- ModInc],
- case proplists:get_bool(include_files, Opts) of
- true ->
- include_config_files(BinTerms);
- false ->
- BinTerms
- end;
- {error, enoent, Reason} ->
- case DontStopOnError of
- true ->
- ?WARNING_MSG(Reason, []),
- [];
- _ ->
- ?ERROR_MSG(Reason, []),
- exit_or_halt(Reason)
- end;
- {error, Reason} ->
- ?ERROR_MSG(Reason, []),
- case DontStopOnError of
- true -> [];
- _ -> exit_or_halt(Reason)
- end
- end.
-
-consult(File) ->
- case filename:extension(File) of
- Ex when (Ex == ".yml") or (Ex == ".yaml") ->
- case fast_yaml:decode_from_file(File, [plain_as_atom]) of
- {ok, []} ->
- {ok, []};
- {ok, [Document|_]} ->
- {ok, parserl(Document)};
- {error, Err} ->
- Msg1 = "Cannot load " ++ File ++ ": ",
- Msg2 = fast_yaml:format_error(Err),
- case Err of
- enoent ->
- {error, enoent, Msg1 ++ Msg2};
- _ ->
- {error, Msg1 ++ Msg2}
- end
- end;
- _ ->
- case file:consult(File) of
- {ok, Terms} ->
- {ok, Terms};
- {error, {LineNumber, erl_parse, _ParseMessage} = Reason} ->
- {error, describe_config_problem(File, Reason, LineNumber)};
- {error, Reason} ->
- case Reason of
- enoent ->
- {error, enoent, describe_config_problem(File, Reason)};
- _ ->
- {error, describe_config_problem(File, Reason)}
- end
- end
- end.
-
-parserl(<<"> ", Term/binary>>) ->
- {ok, A2, _} = erl_scan:string(binary_to_list(Term)),
- {ok, A3} = erl_parse:parse_term(A2),
- A3;
-parserl({A, B}) ->
- {parserl(A), parserl(B)};
-parserl([El|Tail]) ->
- [parserl(El) | parserl(Tail)];
-parserl(Other) ->
- Other.
-
-%% @doc Convert configuration filename to absolute path.
-%% Input is an absolute or relative path to an ejabberd configuration file.
-%% And returns an absolute path to the configuration file.
-%% @spec (string()) -> string()
-get_absolute_path(File) ->
- case filename:pathtype(File) of
- absolute ->
- File;
- relative ->
- {ok, Dir} = file:get_cwd(),
- filename:absname_join(Dir, File);
- volumerelative ->
- filename:absname(File)
- end.
-
-search_hosts(Term, State) ->
- case Term of
- {host, Host} ->
- if
- State#state.hosts == [] ->
- set_hosts_in_options([Host], State);
- true ->
- ?ERROR_MSG("Can't load config file: "
- "too many hosts definitions", []),
- exit("too many hosts definitions")
- end;
- {hosts, Hosts} ->
- if
- State#state.hosts == [] ->
- set_hosts_in_options(Hosts, State);
- true ->
- ?ERROR_MSG("Can't load config file: "
- "too many hosts definitions", []),
- exit("too many hosts definitions")
- end;
- _ ->
- State
- end.
-
-set_hosts_in_options(Hosts, State) ->
- PrepHosts = normalize_hosts(Hosts),
- NewOpts = lists:filter(fun({local_config,{hosts,global},_}) -> false;
- (_) -> true
- end, State#state.opts),
- set_option({hosts, global}, PrepHosts, State#state{hosts = PrepHosts, opts = NewOpts}).
-
-normalize_hosts(Hosts) ->
- normalize_hosts(Hosts,[]).
-normalize_hosts([], PrepHosts) ->
- lists:reverse(PrepHosts);
-normalize_hosts([Host|Hosts], PrepHosts) ->
- case jid:nodeprep(iolist_to_binary(Host)) of
- error ->
- ?ERROR_MSG("Can't load config file: "
- "invalid host name [~p]", [Host]),
- exit("invalid hostname");
- PrepHost ->
- normalize_hosts(Hosts, [PrepHost|PrepHosts])
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% Errors reading the config file
-
-describe_config_problem(Filename, Reason) ->
- Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename),
- Text2 = lists:flatten(" : " ++ file:format_error(Reason)),
- ExitText = Text1 ++ Text2,
- ExitText.
-
-describe_config_problem(Filename, Reason, LineNumber) ->
- Text1 = lists:flatten("Problem loading ejabberd config file " ++ Filename),
- Text2 = lists:flatten(" approximately in the line "
- ++ file:format_error(Reason)),
- ExitText = Text1 ++ Text2,
- Lines = get_config_lines(Filename, LineNumber, 10, 3),
- ?ERROR_MSG("The following lines from your configuration file might be"
- " relevant to the error: ~n~s", [Lines]),
- ExitText.
-
-get_config_lines(Filename, TargetNumber, PreContext, PostContext) ->
- {ok, Fd} = file:open(Filename, [read]),
- LNumbers = lists:seq(TargetNumber-PreContext, TargetNumber+PostContext),
- NextL = io:get_line(Fd, no_prompt),
- R = get_config_lines2(Fd, NextL, 1, LNumbers, []),
- file:close(Fd),
- R.
-
-get_config_lines2(_Fd, eof, _CurrLine, _LNumbers, R) ->
- lists:reverse(R);
-get_config_lines2(_Fd, _NewLine, _CurrLine, [], R) ->
- lists:reverse(R);
-get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(Data) ->
- NextL = io:get_line(Fd, no_prompt),
- if
- CurrLine >= NextWanted ->
- Line2 = [integer_to_list(CurrLine), ": " | Data],
- get_config_lines2(Fd, NextL, CurrLine+1, LNumbers, [Line2 | R]);
- true ->
- get_config_lines2(Fd, NextL, CurrLine+1, [NextWanted | LNumbers], R)
- end.
-
-%% If ejabberd isn't yet running in this node, then halt the node
-exit_or_halt(ExitText) ->
- case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
- [] ->
- ejabberd:halt();
- [_] ->
- exit(ExitText)
- end.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% Support for 'include_config_file'
-
-get_config_option_key(Name, Val) ->
- if Name == listen ->
- case Val of
- {{Port, IP, Trans}, _Mod, _Opts} ->
- {Port, IP, Trans};
- {{Port, Trans}, _Mod, _Opts} when Trans == tcp; Trans == udp ->
- {Port, {0,0,0,0}, Trans};
- {{Port, IP}, _Mod, _Opts} ->
- {Port, IP, tcp};
- {Port, _Mod, _Opts} ->
- {Port, {0,0,0,0}, tcp};
- V when is_list(V) ->
- lists:foldl(
- fun({port, Port}, {_, IP, T}) ->
- {Port, IP, T};
- ({ip, IP}, {Port, _, T}) ->
- {Port, IP, T};
- ({transport, T}, {Port, IP, _}) ->
- {Port, IP, T};
- (_, Res) ->
- Res
- end, {5222, {0,0,0,0}, tcp}, Val)
- end;
- is_tuple(Val) ->
- element(1, Val);
- true ->
- Val
- end.
-
-maps_to_lists(IMap) ->
- maps:fold(fun(Name, Map, Res) when Name == host_config orelse Name == append_host_config ->
- [{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) ->
- [{Name, Val} | Res]
- end, [], IMap).
-
-merge_configs(Terms, ResMap) ->
- lists:foldl(fun({Name, Val}, Map) when is_list(Val), Name =/= auth_method ->
- Old = maps:get(Name, Map, #{}),
- New = lists:foldl(fun(SVal, OMap) ->
- NVal = if Name == host_config orelse Name == append_host_config ->
- {Host, Opts} = SVal,
- HostNP = jid:nameprep(Host),
- {_, SubMap} = maps:get(HostNP, OMap, {HostNP, #{}}),
- {HostNP, merge_configs(Opts, SubMap)};
- true ->
- SVal
- end,
- maps:put(get_config_option_key(Name, SVal), NVal, OMap)
- end, Old, Val),
- maps:put(Name, New, Map);
- ({Name, Val}, Map) ->
- maps:put(Name, Val, Map)
- end, ResMap, Terms).
-
-%% @doc Include additional configuration files in the list of terms.
-%% @spec ([term()]) -> [term()]
-include_config_files(Terms) ->
- {FileOpts, Terms1} =
- lists:mapfoldl(
- fun({include_config_file, _} = T, Ts) ->
- {[transform_include_option(T)], Ts};
- ({include_config_file, _, _} = T, Ts) ->
- {[transform_include_option(T)], Ts};
- (T, Ts) ->
- {[], [T|Ts]}
- end, [], Terms),
- Terms2 = lists:flatmap(
- fun({File, Opts}) ->
- include_config_file(File, Opts)
- end, lists:flatten(FileOpts)),
-
- M1 = merge_configs(Terms1, #{}),
- M2 = merge_configs(Terms2, M1),
- maps_to_lists(M2).
-
-transform_include_option({include_config_file, File}) when is_list(File) ->
- case is_string(File) of
- true -> {File, []};
- false -> File
- end;
-transform_include_option({include_config_file, Filename}) ->
- {Filename, []};
-transform_include_option({include_config_file, Filename, Options}) ->
- {Filename, Options}.
-
-include_config_file(Filename, Options) ->
- Included_terms = get_plain_terms_file(Filename, [{include_files, true}, dont_halt_on_error]),
- Disallow = proplists:get_value(disallow, Options, []),
- Included_terms2 = delete_disallowed(Disallow, Included_terms),
- Allow_only = proplists:get_value(allow_only, Options, all),
- keep_only_allowed(Allow_only, Included_terms2).
-
-%% @doc Filter from the list of terms the disallowed.
-%% Returns a sublist of Terms without the ones which first element is
-%% included in Disallowed.
-%% @spec (Disallowed::[atom()], Terms::[term()]) -> [term()]
-delete_disallowed(Disallowed, Terms) ->
- lists:foldl(
- fun(Dis, Ldis) ->
- delete_disallowed2(Dis, Ldis)
- end,
- Terms,
- Disallowed).
-
-delete_disallowed2(Disallowed, [H|T]) ->
- case element(1, H) of
- Disallowed ->
- ?WARNING_MSG("The option '~p' is disallowed, "
- "and will not be accepted", [Disallowed]),
- delete_disallowed2(Disallowed, T);
- _ ->
- [H|delete_disallowed2(Disallowed, T)]
- end;
-delete_disallowed2(_, []) ->
- [].
-
-%% @doc Keep from the list only the allowed terms.
-%% Returns a sublist of Terms with only the ones which first element is
-%% included in Allowed.
-%% @spec (Allowed::[atom()], Terms::[term()]) -> [term()]
-keep_only_allowed(all, Terms) ->
- Terms;
-keep_only_allowed(Allowed, Terms) ->
- {As, NAs} = lists:partition(
- fun(Term) ->
- lists:member(element(1, Term), Allowed)
- end,
- Terms),
- [?WARNING_MSG("This option is not allowed, "
- "and will not be accepted:~n~p", [NA])
- || NA <- NAs],
- As.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% Support for Macro
-
-%% @doc Replace the macros with their defined values.
-%% @spec (Terms::[term()]) -> [term()]
-replace_macros(Terms) ->
- {TermsOthers, Macros} = split_terms_macros(Terms),
- replace(TermsOthers, Macros).
-
-%% @doc Split Terms into normal terms and macro definitions.
-%% @spec (Terms) -> {Terms, Macros}
-%% Terms = [term()]
-%% Macros = [macro()]
-split_terms_macros(Terms) ->
- lists:foldl(
- fun(Term, {TOs, Ms}) ->
- case Term of
- {define_macro, Key, Value} ->
- case is_correct_macro({Key, Value}) of
- true ->
- {TOs, Ms++[{Key, Value}]};
- false ->
- exit({macro_not_properly_defined, Term})
- end;
- {define_macro, KeyVals} ->
- case lists:all(fun is_correct_macro/1, KeyVals) of
- true ->
- {TOs, Ms ++ KeyVals};
- false ->
- exit({macros_not_properly_defined, Term})
- end;
- Term ->
- {TOs ++ [Term], Ms}
- end
- end,
- {[], []},
- Terms).
-
-is_correct_macro({Key, _Val}) ->
- is_atom(Key) and is_all_uppercase(Key);
-is_correct_macro(_) ->
- false.
-
-%% @doc Recursively replace in Terms macro usages with the defined value.
-%% @spec (Terms, Macros) -> Terms
-%% Terms = [term()]
-%% Macros = [macro()]
-replace([], _) ->
- [];
-replace([Term|Terms], Macros) ->
- [replace_term(Term, Macros) | replace(Terms, Macros)];
-replace(Term, Macros) ->
- replace_term(Term, Macros).
-
-replace_term(Key, Macros) when is_atom(Key) ->
- case is_all_uppercase(Key) of
- true ->
- case proplists:get_value(Key, Macros) of
- undefined -> exit({undefined_macro, Key});
- Value -> Value
- end;
- false ->
- Key
- end;
-replace_term({use_macro, Key, Value}, Macros) ->
- proplists:get_value(Key, Macros, Value);
-replace_term(Term, Macros) when is_list(Term) ->
- replace(Term, Macros);
-replace_term(Term, Macros) when is_tuple(Term) ->
- List = tuple_to_list(Term),
- List2 = replace(List, Macros),
- list_to_tuple(List2);
-replace_term(Term, _) ->
- Term.
-
-is_all_uppercase(Atom) ->
- String = erlang:atom_to_list(Atom),
- lists:all(fun(C) when C >= $a, C =< $z -> false;
- (_) -> true
- end, String).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%% Process terms
-
-process_term(Term, State) ->
- case Term of
- {host_config, HostTerms} ->
- lists:foldl(
- fun({Host, Terms}, AccState) ->
- lists:foldl(fun(T, S) ->
- process_host_term(T, Host, S, set)
- end, AccState, Terms)
- end, State, HostTerms);
- {append_host_config, HostTerms} ->
- lists:foldl(
- fun({Host, Terms}, AccState) ->
- lists:foldl(fun(T, S) ->
- process_host_term(T, Host, S, append)
- end, AccState, Terms)
- end, State, HostTerms);
- _ ->
- process_host_term(Term, global, State, set)
- end.
-
-process_host_term(Term, Host, State, Action) ->
- case Term of
- {modules, Modules} ->
- Modules1 = try (gen_mod:opt_type(modules))(Modules) of
- _ -> replace_modules(Modules)
- catch _:_ -> Modules
- end,
- if Action == set ->
- set_option({modules, Host}, Modules1, State);
- Action == append ->
- append_option({modules, Host}, Modules1, State)
- end;
- {host, _} ->
- State;
- {hosts, _} ->
- State;
- {Opt, Val} when Action == set ->
- set_option({rename_option(Opt), Host}, change_val(Opt, Val), State);
- {Opt, Val} when Action == append ->
- append_option({rename_option(Opt), Host}, change_val(Opt, Val), State);
- Opt ->
- ?WARNING_MSG("Ignore invalid (outdated?) option ~p", [Opt]),
- State
+ stdout ->
+ io:format("~s~n", [Data]);
+ FileName ->
+ try
+ ok = filelib:ensure_dir(FileName),
+ ok = file:write_file(FileName, Data)
+ catch _:{badmatch, {error, Reason}} ->
+ {error, {write_file, FileName, Reason}}
+ end
end.
-rename_option(Option) when is_atom(Option) ->
- case atom_to_list(Option) of
- "odbc_" ++ T ->
- NewOption = list_to_atom("sql_" ++ T),
- ?WARNING_MSG("Option '~s' is obsoleted, use '~s' instead",
- [Option, NewOption]),
- NewOption;
- _ ->
- Option
- end;
-rename_option(Option) ->
- Option.
-
-change_val(auth_method, Val) ->
- prepare_opt_val(auth_method, Val,
- fun(V) ->
- L = if is_list(V) -> V;
- true -> [V]
- end,
- lists:map(
- fun(odbc) -> sql;
- (internal) -> mnesia;
- (A) when is_atom(A) -> A
- end, L)
- end, [mnesia]);
-change_val(_Opt, Val) ->
- Val.
-
-set_option(Opt, Val, State) ->
- State#state{opts = [#local_config{key = Opt, value = Val} |
- State#state.opts]}.
-
-append_option({Opt, Host}, Val, State) ->
- GlobalVals = lists:flatmap(
- fun(#local_config{key = {O, global}, value = V})
- when O == Opt ->
- if is_list(V) -> V;
- true -> [V]
- end;
- (_) ->
- []
- end, State#state.opts),
- NewVal = if is_list(Val) -> Val ++ GlobalVals;
- true -> [Val|GlobalVals]
- end,
- set_option({Opt, Host}, NewVal, State).
-
-set_opts(State) ->
- Opts = State#state.opts,
- ets:insert(
- ejabberd_options,
- lists:map(
- fun(#local_config{key = Key, value = Val}) ->
- {Key, Val}
- end, Opts)),
- set_fqdn(),
- set_log_level().
-
-set_fqdn() ->
- FQDNs = case get_option(fqdn, []) of
- [] ->
- {ok, Hostname} = inet:gethostname(),
- case inet:gethostbyname(Hostname) of
- {ok, #hostent{h_name = FQDN}} ->
- [iolist_to_binary(FQDN)];
- {error, _} ->
- []
- end;
- Domains ->
- Domains
- end,
- xmpp:set_config([{fqdn, FQDNs}]).
-
-set_log_level() ->
- Level = get_option(loglevel, 4),
- ejabberd_logger:set(Level).
-
-add_global_option(Opt, Val) ->
- add_option(Opt, Val).
-
-add_local_option(Opt, Val) ->
- add_option(Opt, Val).
-
-add_option(Opt, Val) when is_atom(Opt) ->
- add_option({Opt, global}, Val);
-add_option({Opt, Host}, Val) ->
- ets:insert(ejabberd_options, {{Opt, Host}, Val}),
- ok.
+-spec get_option(option()) -> term().
+get_option(Opt) ->
+ get_option(Opt, undefined).
--spec prepare_opt_val(any(), any(), check_fun(), any()) -> any().
-
-prepare_opt_val(Opt, Val, F, Default) ->
- Call = case F of
- {Mod, Fun} ->
- fun() -> Mod:Fun(Val) end;
- _ ->
- fun() -> F(Val) end
- end,
- try Call() of
- Res ->
- Res
- catch {replace_with, NewRes} ->
- NewRes;
- {invalid_syntax, Error} ->
- ?WARNING_MSG("incorrect value '~s' of option '~s', "
- "using '~s' as fallback: ~s",
- [format_term(Val),
- format_term(Opt),
- format_term(Default),
- Error]),
- Default;
- _:_ ->
- ?WARNING_MSG("incorrect value '~s' of option '~s', "
- "using '~s' as fallback",
- [format_term(Val),
- format_term(Opt),
- format_term(Default)]),
+-spec get_option(option(), term()) -> term().
+get_option(Opt, Default) when is_atom(Opt) ->
+ get_option({Opt, global}, Default);
+get_option(Opt, Default) ->
+ Tab = case get_tmp_config() of
+ undefined -> ejabberd_options;
+ T -> T
+ end,
+ try ets:lookup_element(Tab, Opt, 2)
+ catch ?EX_RULE(Class, badarg, St) ->
+ Stack = ?EX_STACK(St),
+ ?WARNING_MSG("Attempt to read unspecified option:~n"
+ "** Option: ~p~n"
+ "** ~s",
+ [Opt, misc:format_exception(2, Class, badarg, Stack)]),
Default
end.
--type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
-
--spec get_global_option(any(), check_fun()) -> any().
-
-get_global_option(Opt, _) ->
- get_option(Opt, undefined).
-
--spec get_global_option(any(), check_fun(), any()) -> any().
+-spec set_option(option(), term()) -> ok.
+set_option(Opt, Val) when is_atom(Opt) ->
+ set_option({Opt, global}, Val);
+set_option(Opt, Val) ->
+ Tab = case get_tmp_config() of
+ undefined -> ejabberd_options;
+ T -> T
+ end,
+ ets:insert(Tab, {Opt, Val}),
+ ok.
-get_global_option(Opt, _, Default) ->
- get_option(Opt, Default).
+-spec get_version() -> binary().
+get_version() ->
+ get_option(version).
--spec get_local_option(any(), check_fun()) -> any().
+-spec get_myhosts() -> [binary(), ...].
+get_myhosts() ->
+ get_option(hosts).
-get_local_option(Opt, _) ->
- get_option(Opt, undefined).
+-spec get_myname() -> binary().
+get_myname() ->
+ get_option(host).
--spec get_local_option(any(), check_fun(), any()) -> any().
+-spec get_mylang() -> binary().
+get_mylang() ->
+ get_lang(global).
-get_local_option(Opt, _, Default) ->
- get_option(Opt, Default).
+-spec get_lang(global | binary()) -> binary().
+get_lang(Host) ->
+ get_option({language, Host}).
--spec get_option(any()) -> any().
-get_option(Opt) ->
- get_option(Opt, undefined).
+-spec get_uri() -> binary().
+get_uri() ->
+ <<"http://www.process-one.net/en/ejabberd/">>.
--spec get_option(any(), check_fun(), any()) -> any().
-get_option(Opt, _, Default) ->
- get_option(Opt, Default).
+-spec get_copyright() -> binary().
+get_copyright() ->
+ <<"Copyright (c) ProcessOne">>.
--spec get_option(any(), check_fun() | any()) -> any().
-get_option(Opt, F) when is_function(F) ->
- get_option(Opt, undefined);
-get_option(Opt, Default) when is_atom(Opt) ->
- get_option({Opt, global}, Default);
-get_option(Opt, Default) ->
- {Key, Host} = case Opt of
- {O, global} when is_atom(O) -> Opt;
- {O, H} when is_atom(O), is_binary(H) -> Opt;
- _ ->
- ?WARNING_MSG("Option ~p has invalid (outdated?) "
- "format. This is likely a bug", [Opt]),
- {undefined, global}
- end,
- try ets:lookup_element(ejabberd_options, {Key, Host}, 2)
- catch _:badarg when Host /= global ->
- try ets:lookup_element(ejabberd_options, {Key, global}, 2)
- catch _:badarg -> Default
- end;
- _:badarg ->
- Default
- end.
+-spec get_shared_key() -> binary().
+get_shared_key() ->
+ get_option(shared_key).
--spec has_option(atom() | {atom(), global | binary()}) -> any().
-has_option(Opt) ->
- get_option(Opt) /= undefined.
+-spec get_node_start() -> integer().
+get_node_start() ->
+ get_option(node_start).
-init_module_db_table(Modules) ->
- %% Dirty hack for mod_pubsub
- ets:insert(ejabberd_db_modules, {{mod_pubsub, mnesia}, true}),
- ets:insert(ejabberd_db_modules, {{mod_pubsub, sql}, true}),
- lists:foreach(
- fun(M) ->
- case re:split(atom_to_list(M), "_", [{return, list}]) of
- [_] ->
- ok;
- Parts ->
- [H|T] = lists:reverse(Parts),
- Suffix = list_to_atom(H),
- BareMod = list_to_atom(string:join(lists:reverse(T), "_")),
- case is_behaviour(BareMod, M) of
- true ->
- ets:insert(ejabberd_db_modules,
- {{BareMod, Suffix}, true});
- false ->
- ok
- end
- end
- end, Modules).
-
-is_behaviour(Behav, Mod) ->
- try Mod:module_info(attributes) of
- [] ->
- %% Stripped module?
- true;
- Attrs ->
- lists:any(
- fun({behaviour, L}) -> lists:member(Behav, L);
- ({behavior, L}) -> lists:member(Behav, L);
- (_) -> false
- end, Attrs)
- catch _:_ ->
- true
+-spec fsm_limit_opts([proplists:property()]) -> [{max_queue, pos_integer()}].
+fsm_limit_opts(Opts) ->
+ case lists:keyfind(max_fsm_queue, 1, Opts) of
+ {_, I} when is_integer(I), I>0 ->
+ [{max_queue, I}];
+ false ->
+ case get_option(max_fsm_queue) of
+ undefined -> [];
+ N -> [{max_queue, N}]
+ end
end.
--spec v_db(module(), atom()) -> atom().
-
-v_db(Mod, internal) -> v_db(Mod, mnesia);
-v_db(Mod, odbc) -> v_db(Mod, sql);
-v_db(Mod, Type) ->
- case ets:member(ejabberd_db_modules, {Mod, Type}) of
- true -> Type;
- false -> erlang:error(badarg)
+-spec codec_options(binary() | global) -> [xmpp:decode_option()].
+codec_options(Host) ->
+ case get_option({validate_stream, Host}) of
+ true -> [];
+ false -> [ignore_els]
end.
--spec v_dbs(module()) -> [atom()].
-
-v_dbs(Mod) ->
- ets:select(
- ejabberd_db_modules,
- ets:fun2ms(
- fun({{M, Type}, _}) when M == Mod ->
- Type
- end)).
-
--spec v_dbs_mods(module()) -> [module()].
-
-v_dbs_mods(Mod) ->
- lists:map(fun(M) ->
- binary_to_atom(<<(atom_to_binary(Mod, utf8))/binary, "_",
- (atom_to_binary(M, utf8))/binary>>, utf8)
- end, v_dbs(Mod)).
-
--spec v_host(binary()) -> binary().
-v_host(Host) ->
- hd(v_hosts([Host])).
-
--spec v_hosts([binary()]) -> [binary()].
-v_hosts(Hosts) ->
- ServerHosts = get_myhosts(),
- lists:foldr(
- fun(Host, Acc) ->
- case lists:member(Host, ServerHosts) of
- true ->
- ?ERROR_MSG("Failed to reuse route ~s because it's "
- "already registered on a virtual host",
- [Host]),
- erlang:error(badarg);
- false ->
- case lists:member(Host, Acc) of
- true ->
- ?ERROR_MSG("Host ~s is defined multiple times",
- [Host]),
- erlang:error(badarg);
- false ->
- [Host|Acc]
- end
- end
- end, [], Hosts).
-
-spec default_db(module()) -> atom().
default_db(Module) ->
default_db(global, Module).
@@ -1025,540 +242,485 @@ default_ram_db(Host, Module) ->
default_db(default_ram_db, Host, Module).
-spec default_db(default_db | default_ram_db, binary() | global, module()) -> atom().
-default_db(Opt, Host, Module) ->
- case get_option({Opt, Host}) of
- undefined ->
- mnesia;
- DBType ->
- try
- v_db(Module, DBType)
- catch error:badarg ->
- ?WARNING_MSG("Module '~s' doesn't support database '~s' "
- "defined in option '~s', using "
- "'mnesia' as fallback", [Module, DBType, Opt]),
- mnesia
- end
- end.
-
-get_modules() ->
+default_db(Opt, Host, Mod) ->
+ Type = get_option({Opt, Host}),
+ DBMod = list_to_atom(atom_to_list(Mod) ++ "_" ++ atom_to_list(Type)),
+ case code:ensure_loaded(DBMod) of
+ {module, _} -> Type;
+ {error, _} ->
+ ?WARNING_MSG("Module ~s doesn't support database '~s' "
+ "defined in option '~s', using "
+ "Mnesia as fallback", [Mod, Type, Opt]),
+ mnesia
+ end.
+
+-spec beams(local | external | all) -> [module()].
+beams(local) ->
{ok, Mods} = application:get_key(ejabberd, modules),
+ Mods;
+beams(external) ->
ExtMods = [Name || {Name, _Details} <- ext_mod:installed()],
case application:get_env(ejabberd, external_beams) of
- {ok, Path} ->
- case lists:member(Path, code:get_path()) of
- true -> ok;
- false -> code:add_patha(Path)
- end,
- Beams = filelib:wildcard(filename:join(Path, "*\.beam")),
- CustMods = [list_to_atom(filename:rootname(filename:basename(Beam)))
- || Beam <- Beams],
- CustMods ++ ExtMods ++ Mods;
- _ ->
- ExtMods ++ Mods
- end.
-
-get_modules_with_options(Modules) ->
- lists:foldl(
- fun(Mod, D) ->
- case is_behaviour(?MODULE, Mod) orelse Mod == ?MODULE of
- true ->
- try Mod:opt_type('') of
- Opts when is_list(Opts) ->
- lists:foldl(
- fun(Opt, Acc) ->
- dict:append(Opt, Mod, Acc)
- end, D, Opts)
- catch _:undef ->
- D
- end;
- false ->
- D
- end
- end, dict:new(), Modules).
-
--spec validate_opts(#state{}, dict:dict()) -> {ok, #state{}} | {error, bad_option()}.
-validate_opts(#state{opts = Opts} = State, ModOpts) ->
- try
- NewOpts = lists:map(
- fun(#local_config{key = {Opt, _Host}, value = Val} = In) ->
- case dict:find(Opt, ModOpts) of
- {ok, [Mod|_]} ->
- VFun = Mod:opt_type(Opt),
- try VFun(Val) of
- NewVal ->
- In#local_config{value = NewVal}
- catch {invalid_syntax, Error} ->
- ?ERROR_MSG("Invalid value for "
- "option '~s' (~s): ~s",
- [Opt, Error,
- misc:format_val({yaml, Val})]),
- erlang:error(invalid_option);
- _:R when R /= undef ->
- ?ERROR_MSG("Invalid value for "
- "option '~s': ~s",
- [Opt, misc:format_val({yaml, Val})]),
- erlang:error(invalid_option)
- end;
- _ ->
- KnownOpts = dict:fetch_keys(ModOpts),
- ?ERROR_MSG("Unknown option '~s', did you mean '~s'?",
- [Opt, misc:best_match(Opt, KnownOpts)]),
- erlang:error(unknown_option)
- end
- end, Opts),
- {ok, State#state{opts = NewOpts}}
- catch _:invalid_option ->
- {error, invalid_option};
- _:unknown_option ->
- {error, unknown_option}
- end.
+ {ok, Path} ->
+ case lists:member(Path, code:get_path()) of
+ true -> ok;
+ false -> code:add_patha(Path)
+ end,
+ Beams = filelib:wildcard(filename:join(Path, "*\.beam")),
+ CustMods = [list_to_atom(filename:rootname(filename:basename(Beam)))
+ || Beam <- Beams],
+ CustMods ++ ExtMods;
+ _ ->
+ ExtMods
+ end;
+beams(all) ->
+ beams(local) ++ beams(external).
-%% @spec (Path::string()) -> true | false
-is_file_readable(Path) ->
- case file:read_file_info(Path) of
- {ok, FileInfo} ->
- case {FileInfo#file_info.type, FileInfo#file_info.access} of
- {regular, read} -> true;
- {regular, read_write} -> true;
- _ -> false
- end;
- {error, _Reason} ->
- false
+-spec may_hide_data(term()) -> term().
+may_hide_data(Data) ->
+ case get_option(hide_sensitive_log_data) of
+ false -> Data;
+ true -> "hidden_by_ejabberd"
end.
-get_version() ->
- case application:get_env(ejabberd, custom_vsn) of
- {ok, Vsn0} when is_list(Vsn0) ->
- list_to_binary(Vsn0);
- {ok, Vsn1} when is_binary(Vsn1) ->
- Vsn1;
- _ ->
- case application:get_key(ejabberd, vsn) of
- undefined -> "";
- {ok, Vsn} -> list_to_binary(Vsn)
- end
+%% Some Erlang apps expects env parameters to be list and not binary.
+%% For example, Mnesia is not able to start if mnesia dir is passed as a binary.
+%% However, binary is most common on Elixir, so it is easy to make a setup mistake.
+-spec env_binary_to_list(atom(), atom()) -> {ok, any()} | undefined.
+env_binary_to_list(Application, Parameter) ->
+ %% Application need to be loaded to allow setting parameters
+ application:load(Application),
+ case application:get_env(Application, Parameter) of
+ {ok, Val} when is_binary(Val) ->
+ BVal = binary_to_list(Val),
+ application:set_env(Application, Parameter, BVal),
+ {ok, BVal};
+ Other ->
+ Other
end.
--spec get_myhosts() -> [binary()].
-
-get_myhosts() ->
- get_option(hosts, [<<"localhost">>]).
-
--spec get_myname() -> binary().
-
-get_myname() ->
- hd(get_myhosts()).
-
--spec get_mylang() -> binary().
-
-get_mylang() ->
- get_lang(global).
-
--spec get_lang(global | binary()) -> binary().
-get_lang(Host) ->
- get_option({language, Host}, <<"en">>).
-
--spec get_uri() -> binary().
-get_uri() ->
- <<"http://www.process-one.net/en/ejabberd/">>.
-
--spec get_copyright() -> binary().
-get_copyright() ->
- <<"Copyright (c) ProcessOne">>.
-
-replace_module(mod_announce_odbc) -> {mod_announce, sql};
-replace_module(mod_blocking_odbc) -> {mod_blocking, sql};
-replace_module(mod_caps_odbc) -> {mod_caps, sql};
-replace_module(mod_last_odbc) -> {mod_last, sql};
-replace_module(mod_muc_odbc) -> {mod_muc, sql};
-replace_module(mod_offline_odbc) -> {mod_offline, sql};
-replace_module(mod_privacy_odbc) -> {mod_privacy, sql};
-replace_module(mod_private_odbc) -> {mod_private, sql};
-replace_module(mod_roster_odbc) -> {mod_roster, sql};
-replace_module(mod_shared_roster_odbc) -> {mod_shared_roster, sql};
-replace_module(mod_vcard_odbc) -> {mod_vcard, sql};
-replace_module(mod_vcard_ldap) -> {mod_vcard, ldap};
-replace_module(mod_vcard_xupdate_odbc) -> mod_vcard_xupdate;
-replace_module(mod_pubsub_odbc) -> {mod_pubsub, sql};
-replace_module(mod_http_bind) -> mod_bosh;
-replace_module(Module) ->
- case is_elixir_module(Module) of
- true -> expand_elixir_module(Module);
- false -> Module
- end.
+-spec validators([atom()]) -> {econf:validators(), [atom()]}.
+validators(Disallowed) ->
+ Modules = callback_modules(all),
+ Validators = lists:foldl(
+ fun(M, Vs) ->
+ maps:merge(Vs, validators(M, Disallowed))
+ end, #{}, Modules),
+ Required = lists:flatmap(
+ fun(M) ->
+ [O || O <- M:options(), is_atom(O)]
+ end, Modules),
+ {Validators, Required}.
+
+-spec convert_to_yaml(file:filename()) -> ok | error_return().
+convert_to_yaml(File) ->
+ convert_to_yaml(File, stdout).
-replace_modules(Modules) ->
- lists:map(
- fun({Module, Opts}) ->
- case replace_module(Module) of
- {NewModule, DBType} ->
- emit_deprecation_warning(Module, NewModule, DBType),
- NewOpts = [{db_type, DBType} |
- lists:keydelete(db_type, 1, Opts)],
- {NewModule, transform_module_options(Module, NewOpts)};
- NewModule ->
- if Module /= NewModule ->
- emit_deprecation_warning(Module, NewModule);
- true ->
- ok
- end,
- {NewModule, transform_module_options(Module, Opts)}
- end
- end, Modules).
-
-%% Elixir module naming
-%% ====================
-
--ifdef(ELIXIR_ENABLED).
-is_elixir_enabled() ->
- true.
--else.
-is_elixir_enabled() ->
- false.
--endif.
-
-is_using_elixir_config() ->
- case is_elixir_enabled() of
- true ->
- Config = get_ejabberd_config_path(),
- 'Elixir.Ejabberd.ConfigUtil':is_elixir_config(Config);
- false ->
- false
+-spec convert_to_yaml(file:filename(),
+ stdout | file:filename()) -> ok | error_return().
+convert_to_yaml(File, Output) ->
+ case read_erlang_file(File, []) of
+ {ok, Y} ->
+ dump(Y, Output);
+ Err ->
+ Err
end.
-%% If module name start with uppercase letter, this is an Elixir module:
-is_elixir_module(Module) ->
- case atom_to_list(Module) of
- [H|_] when H >= 65, H =< 90 -> true;
- _ ->false
+-spec format_error(error_return()) -> string().
+format_error({error, Reason, Ctx}) ->
+ econf:format_error(Reason, Ctx);
+format_error({error, {merge_conflict, Opt, Host}}) ->
+ lists:flatten(
+ io_lib:format(
+ "Cannot merge value of option '~s' defined in append_host_config "
+ "for virtual host ~s: only options of type list or map are allowed "
+ "in append_host_config. Hint: specify the option in host_config",
+ [Opt, Host]));
+format_error({error, {old_config, Path, Reason}}) ->
+ lists:flatten(
+ io_lib:format(
+ "Failed to read configuration from '~s': ~s~s",
+ [Path,
+ case Reason of
+ {_, _, _} -> "at line ";
+ _ -> ""
+ end, file:format_error(Reason)]));
+format_error({error, {write_file, Path, Reason}}) ->
+ lists:flatten(
+ io_lib:format(
+ "Failed to write to '~s': ~s",
+ [Path, file:format_error(Reason)]));
+format_error({error, {exception, Class, Reason, St}}) ->
+ lists:flatten(
+ io_lib:format(
+ "Exception occured during configuration processing. "
+ "This is most likely due to faulty/incompatible validator in "
+ "third-party code. If you are not running any third-party "
+ "code, please report the bug with ejabberd configuration "
+ "file attached and the following stacktrace included:~n** ~s",
+ [misc:format_exception(2, Class, Reason, St)])).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+-spec path() -> string().
+path() ->
+ case get_env_config() of
+ {ok, Path} ->
+ Path;
+ undefined ->
+ case os:getenv("EJABBERD_CONFIG_PATH") of
+ false ->
+ "ejabberd.yml";
+ Path ->
+ Path
+ end
end.
-%% We assume we know this is an elixir module
-expand_elixir_module(Module) ->
- case atom_to_list(Module) of
- %% Module name already specified as an Elixir from Erlang module name
- "Elixir." ++ _ -> Module;
- %% if start with uppercase letter, this is an Elixir module: Append 'Elixir.' to module name.
- ModuleString ->
- list_to_atom("Elixir." ++ ModuleString)
+-spec get_env_config() -> {ok, string()} | undefined.
+get_env_config() ->
+ %% First case: the filename can be specified with: erl -config "/path/to/ejabberd.yml".
+ case application:get_env(ejabberd, config) of
+ R = {ok, _Path} -> R;
+ undefined ->
+ %% Second case for embbeding ejabberd in another app, for example for Elixir:
+ %% config :ejabberd,
+ %% file: "config/ejabberd.yml"
+ application:get_env(ejabberd, file)
end.
-strings_to_binary([]) ->
- [];
-strings_to_binary(L) when is_list(L) ->
- case is_string(L) of
- true ->
- list_to_binary(L);
- false ->
- strings_to_binary1(L)
- end;
-strings_to_binary({A, B, C, D}) when
- is_integer(A), is_integer(B), is_integer(C), is_integer(D) ->
- {A, B, C ,D};
-strings_to_binary(T) when is_tuple(T) ->
- list_to_tuple(strings_to_binary1(tuple_to_list(T)));
-strings_to_binary(X) ->
- X.
-
-strings_to_binary1([El|L]) ->
- [strings_to_binary(El)|strings_to_binary1(L)];
-strings_to_binary1([]) ->
- [];
-strings_to_binary1(T) ->
- T.
-
-is_string([C|T]) when (C >= 0) and (C =< 255) ->
- is_string(T);
-is_string([]) ->
- true;
-is_string(_) ->
- false.
-
-binary_to_strings(B) when is_binary(B) ->
- binary_to_list(B);
-binary_to_strings([H|T]) ->
- [binary_to_strings(H)|binary_to_strings(T)];
-binary_to_strings(T) when is_tuple(T) ->
- list_to_tuple(binary_to_strings(tuple_to_list(T)));
-binary_to_strings(T) ->
- T.
-
-format_term(Bin) when is_binary(Bin) ->
- io_lib:format("\"~s\"", [Bin]);
-format_term(S) when is_list(S), S /= [] ->
- case lists:all(fun(C) -> (C>=0) and (C=<255) end, S) of
- true ->
- io_lib:format("\"~s\"", [S]);
- false ->
- io_lib:format("~p", [binary_to_strings(S)])
- end;
-format_term(T) ->
- io_lib:format("~p", [binary_to_strings(T)]).
-
-transform_terms(Terms) ->
- %% We could check all ejabberd beams, but this
- %% slows down start-up procedure :(
- Mods = [mod_register,
- ejabberd_s2s,
- ejabberd_listener,
- ejabberd_sql_sup,
- ejabberd_shaper,
- ejabberd_s2s_out,
- acl,
- ejabberd_config],
- collect_options(transform_terms(Mods, Terms)).
-
-transform_terms([Mod|Mods], Terms) ->
- case catch Mod:transform_options(Terms) of
- {'EXIT', _} = Err ->
- ?ERROR_MSG("Failed to transform terms by ~p: ~p", [Mod, Err]),
- transform_terms(Mods, Terms);
- NewTerms ->
- transform_terms(Mods, NewTerms)
- end;
-transform_terms([], NewTerms) ->
- NewTerms.
-
-transform_module_options(Module, Opts) ->
- Opts1 = gen_iq_handler:transform_module_options(Opts),
- try
- Module:transform_module_options(Opts1)
- catch error:undef ->
- Opts1
- end.
+-spec create_tmp_config() -> ok.
+create_tmp_config() ->
+ T = ets:new(options, [private]),
+ put(ejabberd_options, T),
+ ok.
-compact(Cfg) ->
- Opts = [{K, V} || #local_config{key = K, value = V} <- Cfg],
- {GOpts, HOpts} = split_by_hosts(Opts),
- [#local_config{key = {O, global}, value = V} || {O, V} <- GOpts] ++
- lists:flatmap(
- fun({Host, OptVal}) ->
- case lists:member(OptVal, GOpts) of
- true ->
- [];
- false ->
- [#local_config{key = {Opt, Host}, value = Val}
- || {Opt, Val} <- OptVal]
- end
- end, lists:flatten(HOpts)).
-
-split_by_hosts(Opts) ->
- Opts1 = orddict:to_list(
- lists:foldl(
- fun({{Opt, Host}, Val}, D) ->
- orddict:append(Host, {Opt, Val}, D)
- end, orddict:new(), Opts)),
- case lists:keytake(global, 1, Opts1) of
- {value, {global, GlobalOpts}, HostOpts} ->
- {GlobalOpts, HostOpts};
- _ ->
- {[], Opts1}
- end.
+-spec get_tmp_config() -> ets:tid() | undefined.
+get_tmp_config() ->
+ get(ejabberd_options).
-collect_options(Opts) ->
- {D, InvalidOpts} =
- lists:foldl(
- fun({K, V}, {D, Os}) when is_list(V) ->
- {orddict:append_list(K, V, D), Os};
- ({K, V}, {D, Os}) ->
- {orddict:store(K, V, D), Os};
- (Opt, {D, Os}) ->
- {D, [Opt|Os]}
- end, {orddict:new(), []}, Opts),
- InvalidOpts ++ orddict:to_list(D).
-
-transform_options(Opts) ->
- Opts1 = lists:foldl(fun transform_options/2, [], Opts),
- {HOpts, Opts2} = lists:mapfoldl(
- fun({host_config, O}, Os) ->
- {[O], Os};
- (O, Os) ->
- {[], [O|Os]}
- end, [], Opts1),
- {AHOpts, Opts3} = lists:mapfoldl(
- fun({append_host_config, O}, Os) ->
- {[O], Os};
- (O, Os) ->
- {[], [O|Os]}
- end, [], Opts2),
- HOpts1 = case collect_options(lists:flatten(HOpts)) of
- [] ->
- [];
- HOs ->
- [{host_config,
- [{H, transform_terms(O)} || {H, O} <- HOs]}]
- end,
- AHOpts1 = case collect_options(lists:flatten(AHOpts)) of
- [] ->
- [];
- AHOs ->
- [{append_host_config,
- [{H, transform_terms(O)} || {H, O} <- AHOs]}]
- end,
- HOpts1 ++ AHOpts1 ++ Opts3.
-
-transform_options({domain_certfile, Domain, CertFile}, Opts) ->
- ?WARNING_MSG("Option 'domain_certfile' now should be defined "
- "per virtual host or globally. The old format is "
- "still supported but it is better to fix your config", []),
- [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts];
-transform_options(Opt, Opts) when Opt == override_global;
- Opt == override_local;
- Opt == override_acls ->
- ?WARNING_MSG("Ignoring '~s' option which has no effect anymore", [Opt]),
- Opts;
-transform_options({node_start, {_, _, _} = Now}, Opts) ->
- ?WARNING_MSG("Old 'node_start' format detected. This is still supported "
- "but it is better to fix your config.", []),
- [{node_start, now_to_seconds(Now)}|Opts];
-transform_options({host_config, Host, HOpts}, Opts) ->
- {AddOpts, HOpts1} =
- lists:mapfoldl(
- fun({{add, Opt}, Val}, Os) ->
- ?WARNING_MSG("Option 'add' is deprecated. "
- "The option is still supported "
- "but it is better to fix your config: "
- "use 'append_host_config' instead.", []),
- {[{Opt, Val}], Os};
- (O, Os) ->
- {[], [O|Os]}
- end, [], HOpts),
- [{append_host_config, [{Host, lists:flatten(AddOpts)}]},
- {host_config, [{Host, HOpts1}]}|Opts];
-transform_options({define_macro, Macro, Val}, Opts) ->
- [{define_macro, [{Macro, Val}]}|Opts];
-transform_options({include_config_file, _} = Opt, Opts) ->
- [{include_config_file, [transform_include_option(Opt)]} | Opts];
-transform_options({include_config_file, _, _} = Opt, Opts) ->
- [{include_config_file, [transform_include_option(Opt)]} | Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
-emit_deprecation_warning(Module, NewModule, DBType) ->
- ?WARNING_MSG("Module ~s is deprecated, use ~s with 'db_type: ~s'"
- " instead", [Module, NewModule, DBType]).
-
-emit_deprecation_warning(Module, NewModule) ->
- case is_elixir_module(NewModule) of
- %% Do not emit deprecation warning for Elixir
- true -> ok;
- false ->
- ?WARNING_MSG("Module ~s is deprecated, use ~s instead",
- [Module, NewModule])
+-spec delete_tmp_config() -> ok.
+delete_tmp_config() ->
+ case get_tmp_config() of
+ undefined ->
+ ok;
+ T ->
+ erase(ejabberd_options),
+ ets:delete(T),
+ ok
end.
--spec now_to_seconds(erlang:timestamp()) -> non_neg_integer().
-now_to_seconds({MegaSecs, Secs, _MicroSecs}) ->
- MegaSecs * 1000000 + Secs.
+-spec callback_modules(local | external | all) -> [module()].
+callback_modules(local) ->
+ [ejabberd_options];
+callback_modules(external) ->
+ lists:filter(
+ fun(M) ->
+ case code:ensure_loaded(M) of
+ {module, _} ->
+ erlang:function_exported(M, options, 0);
+ {error, _} ->
+ false
+ end
+ end, beams(external));
+callback_modules(all) ->
+ callback_modules(local) ++ callback_modules(external).
+
+-spec validators(module(), [atom()]) -> econf:validators().
+validators(Mod, Disallowed) ->
+ maps:from_list(
+ lists:filtermap(
+ fun(O) ->
+ case lists:member(O, Disallowed) of
+ true -> false;
+ false ->
+ {true,
+ try {O, Mod:opt_type(O)}
+ catch _:_ ->
+ {O, ejabberd_options:opt_type(O)}
+ end}
+ end
+ end, proplists:get_keys(Mod:options()))).
+
+-spec get_modules_configs() -> [file:filename_all()].
+get_modules_configs() ->
+ Fs = [{filename:rootname(filename:basename(F)), F}
+ || F <- filelib:wildcard(ext_mod:config_dir() ++ "/*.{yml,yaml}")
+ ++ filelib:wildcard(ext_mod:modules_dir() ++ "/*/conf/*.{yml,yaml}")],
+ [proplists:get_value(F, Fs) || F <- proplists:get_keys(Fs)].
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(hide_sensitive_log_data) ->
- fun (H) when is_boolean(H) -> H end;
-opt_type(hosts) ->
- fun(L) ->
- [iolist_to_binary(H) || H <- L]
- end;
-opt_type(language) ->
- fun xmpp_lang:check/1;
-opt_type(max_fsm_queue) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(default_db) ->
- fun(T) when is_atom(T) -> T end;
-opt_type(default_ram_db) ->
- fun(T) when is_atom(T) -> T end;
-opt_type(loglevel) ->
- fun (P) when P >= 0, P =< 5 -> P end;
-opt_type(queue_dir) ->
- fun iolist_to_binary/1;
-opt_type(queue_type) ->
- fun(ram) -> ram; (file) -> file end;
-opt_type(use_cache) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(cache_size) ->
- fun(I) when is_integer(I), I>0 -> I;
- (infinity) -> infinity;
- (unlimited) -> infinity
- end;
-opt_type(cache_missed) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(cache_life_time) ->
- fun(I) when is_integer(I), I>0 -> I;
- (infinity) -> infinity;
- (unlimited) -> infinity
- end;
-opt_type(negotiation_timeout) ->
- fun(T) when T > 0 -> T end;
-opt_type(shared_key) ->
- fun iolist_to_binary/1;
-opt_type(node_start) ->
- fun(I) when is_integer(I), I>=0 -> I end;
-opt_type(validate_stream) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(fqdn) ->
- fun(Domain) when is_binary(Domain) ->
- [Domain];
- (Domains) ->
- [iolist_to_binary(Domain) || Domain <- Domains]
- end;
-opt_type(_) ->
- [hide_sensitive_log_data, hosts, language, max_fsm_queue,
- default_db, default_ram_db, queue_type, queue_dir, loglevel,
- use_cache, cache_size, cache_missed, cache_life_time, fqdn,
- shared_key, node_start, validate_stream, negotiation_timeout].
+read_file(File) ->
+ read_file(File, [replace_macros, include_files, include_modules_configs]).
--spec may_hide_data(any()) -> any().
-may_hide_data(Data) ->
- case get_option(hide_sensitive_log_data, false) of
- false ->
- Data;
- true ->
- "hidden_by_ejabberd"
+read_file(File, Opts) ->
+ {Opts1, Opts2} = proplists:split(Opts, [replace_macros, include_files]),
+ Ret = case filename:extension(File) of
+ Ex when Ex == ".yml" orelse Ex == ".yaml" ->
+ Files = case proplists:get_bool(include_modules_configs, Opts2) of
+ true -> get_modules_configs();
+ false -> []
+ end,
+ read_yaml_files([File|Files], lists:flatten(Opts1));
+ _ ->
+ read_erlang_file(File, lists:flatten(Opts1))
+ end,
+ case Ret of
+ {ok, Y} ->
+ validate(Y);
+ Err ->
+ Err
end.
--spec fsm_limit_opts([proplists:property()]) -> [{max_queue, pos_integer()}].
-fsm_limit_opts(Opts) ->
- case lists:keyfind(max_fsm_queue, 1, Opts) of
- {_, I} when is_integer(I), I>0 ->
- [{max_queue, I}];
- false ->
- case get_option(max_fsm_queue) of
- undefined -> [];
- N -> [{max_queue, N}]
- end
+read_yaml_files(Files, Opts) ->
+ ParseOpts = [plain_as_atom | lists:flatten(Opts)],
+ lists:foldl(
+ fun(File, {ok, Y1}) ->
+ case econf:parse(File, #{'_' => econf:any()}, ParseOpts) of
+ {ok, Y2} -> {ok, Y1 ++ Y2};
+ Err -> Err
+ end;
+ (_, Err) ->
+ Err
+ end, {ok, []}, Files).
+
+read_erlang_file(File, _) ->
+ case ejabberd_old_config:read_file(File) of
+ {ok, Y} ->
+ econf:replace_macros(Y);
+ Err ->
+ Err
end.
--spec queue_dir() -> binary() | undefined.
-queue_dir() ->
- get_option(queue_dir).
-
--spec default_queue_type(binary()) -> ram | file.
-default_queue_type(Host) ->
- get_option({queue_type, Host}, ram).
-
--spec use_cache(binary() | global) -> boolean().
-use_cache(Host) ->
- get_option({use_cache, Host}, true).
-
--spec cache_size(binary() | global) -> pos_integer() | infinity.
-cache_size(Host) ->
- get_option({cache_size, Host}, 1000).
-
--spec cache_missed(binary() | global) -> boolean().
-cache_missed(Host) ->
- get_option({cache_missed, Host}, true).
+-spec validate(term()) -> {ok, term()} | error_return().
+validate(Y1) ->
+ case econf:validate(
+ econf:options(
+ #{hosts => ejabberd_options:opt_type(hosts),
+ loglevel => ejabberd_options:opt_type(loglevel),
+ '_' => econf:any()},
+ [{required, [hosts]}, unique]),
+ Y1) of
+ {ok, Y2} ->
+ set_loglevel(proplists:get_value(loglevel, Y2, 4)),
+ case ejabberd_config_transformer:map_reduce(Y2) of
+ {ok, Y3} ->
+ Hosts = proplists:get_value(hosts, Y3),
+ create_tmp_config(),
+ set_option(hosts, Hosts),
+ set_option(host, hd(Hosts)),
+ set_option(yaml_config, Y3),
+ {Validators, Required} = validators([]),
+ Validator = econf:options(Validators,
+ [{required, Required},
+ unique]),
+ econf:validate(Validator, Y3);
+ Err ->
+ Err
+ end;
+ Err ->
+ Err
+ end.
--spec cache_life_time(binary() | global) -> pos_integer() | infinity.
-%% NOTE: the integer value returned is in *seconds*
-cache_life_time(Host) ->
- get_option({cache_life_time, Host}, 3600).
+-spec load_file(file:filename_all()) -> ok | error_return().
+load_file(File) ->
+ try
+ case read_file(File) of
+ {ok, Terms} ->
+ case set_host_config(Terms) of
+ {ok, Map} ->
+ T = get_tmp_config(),
+ Hosts = get_myhosts(),
+ apply_defaults(T, Hosts, Map),
+ case validate_modules(Hosts) of
+ {ok, ModOpts} ->
+ ets:insert(T, ModOpts),
+ set_option(host, hd(Hosts)),
+ commit(),
+ set_fqdn();
+ Err ->
+ abort(Err)
+ end;
+ Err ->
+ abort(Err)
+ end;
+ Err ->
+ abort(Err)
+ end
+ catch ?EX_RULE(Class, Reason, St) ->
+ {error, {exception, Class, Reason, ?EX_STACK(St)}}
+ end.
+
+-spec commit() -> ok.
+commit() ->
+ T = get_tmp_config(),
+ NewOpts = ets:tab2list(T),
+ ets:insert(ejabberd_options, NewOpts),
+ delete_tmp_config().
+
+-spec abort(error_return()) -> error_return().
+abort(Err) ->
+ delete_tmp_config(),
+ try ets:lookup_element(ejabberd_options, {loglevel, global}, 2) of
+ Level -> set_loglevel(Level)
+ catch _:badarg ->
+ ok
+ end,
+ Err.
+
+-spec set_host_config([{atom(), term()}]) -> {ok, map()} | error_return().
+set_host_config(Opts) ->
+ Map1 = lists:foldl(
+ fun({Opt, Val}, M) when Opt /= host_config,
+ Opt /= append_host_config ->
+ maps:put({Opt, global}, Val, M);
+ (_, M) ->
+ M
+ end, #{}, Opts),
+ HostOpts = proplists:get_value(host_config, Opts, []),
+ AppendHostOpts = proplists:get_value(append_host_config, Opts, []),
+ Map2 = lists:foldl(
+ fun({Host, Opts1}, M1) ->
+ lists:foldl(
+ fun({Opt, Val}, M2) ->
+ maps:put({Opt, Host}, Val, M2)
+ end, M1, Opts1)
+ end, Map1, HostOpts),
+ Map3 = lists:foldl(
+ fun(_, {error, _} = Err) ->
+ Err;
+ ({Host, Opts1}, M1) ->
+ lists:foldl(
+ fun(_, {error, _} = Err) ->
+ Err;
+ ({Opt, L1}, M2) when is_list(L1) ->
+ L2 = try maps:get({Opt, Host}, M2)
+ catch _:{badkey, _} ->
+ maps:get({Opt, global}, M2, [])
+ end,
+ L3 = L2 ++ L1,
+ maps:put({Opt, Host}, L3, M2);
+ ({Opt, _}, _) ->
+ {error, {merge_conflict, Opt, Host}}
+ end, M1, Opts1)
+ end, Map2, AppendHostOpts),
+ case Map3 of
+ {error, _} -> Map3;
+ _ -> {ok, Map3}
+ end.
+
+-spec apply_defaults(ets:tid(), [binary()], map()) -> ok.
+apply_defaults(Tab, Hosts, Map) ->
+ Defaults1 = defaults(),
+ apply_defaults(Tab, global, Map, Defaults1),
+ {_, Defaults2} = proplists:split(Defaults1, globals()),
+ lists:foreach(
+ fun(Host) ->
+ set_option(host, Host),
+ apply_defaults(Tab, Host, Map, Defaults2)
+ end, Hosts).
+
+-spec apply_defaults(ets:tid(), global | binary(), map(),
+ [atom() | {atom(), term()}]) -> ok.
+apply_defaults(Tab, Host, Map, Defaults) ->
+ lists:foreach(
+ fun({Opt, Default}) ->
+ try maps:get({Opt, Host}, Map) of
+ Val ->
+ ets:insert(Tab, {{Opt, Host}, Val})
+ catch _:{badkey, _} when Host == global ->
+ Default1 = compute_default(Default, Host),
+ ets:insert(Tab, {{Opt, Host}, Default1});
+ _:{badkey, _} ->
+ try maps:get({Opt, global}, Map) of
+ V -> ets:insert(Tab, {{Opt, Host}, V})
+ catch _:{badkey, _} ->
+ Default1 = compute_default(Default, Host),
+ ets:insert(Tab, {{Opt, Host}, Default1})
+ end
+ end;
+ (Opt) when Host == global ->
+ Val = maps:get({Opt, Host}, Map),
+ ets:insert(Tab, {{Opt, Host}, Val});
+ (_) ->
+ ok
+ end, Defaults).
+
+-spec defaults() -> [atom() | {atom(), term()}].
+defaults() ->
+ lists:foldl(
+ fun(Mod, Acc) ->
+ lists:foldl(
+ fun({Opt, Val}, Acc1) ->
+ lists:keystore(Opt, 1, Acc1, {Opt, Val});
+ (Opt, Acc1) ->
+ case lists:member(Opt, Acc1) of
+ true -> Acc1;
+ false -> [Opt|Acc1]
+ end
+ end, Acc, Mod:options())
+ end, ejabberd_options:options(), callback_modules(external)).
+
+-spec globals() -> [atom()].
+globals() ->
+ lists:usort(
+ lists:flatmap(
+ fun(Mod) ->
+ case erlang:function_exported(Mod, globals, 0) of
+ true -> Mod:globals();
+ false -> []
+ end
+ end, callback_modules(all))).
+
+%% The module validator depends on virtual host, so we have to
+%% validate modules in this separate function.
+-spec validate_modules([binary()]) -> {ok, list()} | error_return().
+validate_modules(Hosts) ->
+ lists:foldl(
+ fun(Host, {ok, Acc}) ->
+ set_option(host, Host),
+ ModOpts = get_option({modules, Host}),
+ case gen_mod:validate(Host, ModOpts) of
+ {ok, ModOpts1} ->
+ {ok, [{{modules, Host}, ModOpts1}|Acc]};
+ Err ->
+ Err
+ end;
+ (_, Err) ->
+ Err
+ end, {ok, []}, Hosts).
+
+-spec delete_host_options([binary()]) -> ok.
+delete_host_options(Hosts) ->
+ lists:foreach(
+ fun(Host) ->
+ ets:match_delete(ejabberd_options, {{'_', Host}, '_'})
+ end, Hosts).
+
+-spec compute_default(fun((global | binary()) -> T) | T, global | binary()) -> T.
+compute_default(F, Host) when is_function(F, 1) ->
+ F(Host);
+compute_default(Val, _) ->
+ Val.
--spec codec_options(binary() | global) -> [xmpp:decode_option()].
-codec_options(Host) ->
- case get_option({validate_stream, Host}, false) of
- true -> [];
- false -> [ignore_els]
- end.
+-spec set_fqdn() -> ok.
+set_fqdn() ->
+ FQDNs = get_option(fqdn),
+ xmpp:set_config([{fqdn, FQDNs}]).
--spec negotiation_timeout() -> pos_integer().
-negotiation_timeout() ->
- timer:seconds(get_option(negotiation_timeout, 30)).
+-spec set_shared_key() -> ok.
+set_shared_key() ->
+ Key = case erlang:get_cookie() of
+ nocookie ->
+ str:sha(p1_rand:get_string());
+ Cookie ->
+ str:sha(erlang:atom_to_binary(Cookie, latin1))
+ end,
+ set_option(shared_key, Key).
+
+-spec set_node_start(integer()) -> ok.
+set_node_start(UnixTime) ->
+ set_option(node_start, UnixTime).
+
+-spec set_loglevel(0..5) -> ok.
+set_loglevel(Level) ->
+ ejabberd_logger:set(Level).
diff --git a/src/ejabberd_config_transformer.erl b/src/ejabberd_config_transformer.erl
new file mode 100644
index 000000000..fc16207f3
--- /dev/null
+++ b/src/ejabberd_config_transformer.erl
@@ -0,0 +1,517 @@
+%%%----------------------------------------------------------------------
+%%% 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(ejabberd_config_transformer).
+
+%% API
+-export([map_reduce/1]).
+
+-include("logger.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+map_reduce(Y) ->
+ F = fun(Y1) ->
+ Y2 = (validator())(Y1),
+ Y3 = transform(Y2),
+ if Y2 /= Y3 ->
+ ?DEBUG("Transformed configuration:~s~n",
+ [misc:format_val({yaml, Y3})]);
+ true ->
+ ok
+ end,
+ Y3
+ end,
+ econf:validate(F, Y).
+
+%%%===================================================================
+%%% Transformer
+%%%===================================================================
+transform(Y) ->
+ {Y1, Acc1} = transform(global, Y, #{}),
+ {Y2, Acc2} = update(Y1, Acc1),
+ filter(global, Y2, Acc2).
+
+transform(Host, Y, Acc) ->
+ filtermapfoldr(
+ fun({Opt, HostOpts}, Acc1) when (Opt == host_config orelse
+ Opt == append_host_config)
+ andalso Host == global ->
+ case filtermapfoldr(
+ fun({Host1, Opts}, Acc2) ->
+ case transform(Host1, Opts, Acc2) of
+ {[], Acc3} ->
+ {false, Acc3};
+ {Opts1, Acc3} ->
+ {{true, {Host1, Opts1}}, Acc3}
+ end
+ end, Acc1, HostOpts) of
+ {[], Acc4} ->
+ {false, Acc4};
+ {HostOpts1, Acc4} ->
+ {{true, {Opt, HostOpts1}}, Acc4}
+ end;
+ ({Opt, Val}, Acc1) ->
+ transform(Host, Opt, Val, Acc1)
+ end, Acc, Y).
+
+transform(Host, modules, ModOpts, Acc) ->
+ {ModOpts1, Acc2} =
+ lists:mapfoldr(
+ fun({Mod, Opts}, Acc1) ->
+ Opts1 = transform_module_options(Opts),
+ transform_module(Host, Mod, Opts1, Acc1)
+ end, Acc, ModOpts),
+ {{true, {modules, ModOpts1}}, Acc2};
+transform(global, listen, Listeners, Acc) ->
+ {Listeners1, Acc2} =
+ lists:mapfoldr(
+ fun(Opts, Acc1) ->
+ transform_listener(Opts, Acc1)
+ end, Acc, Listeners),
+ {{true, {listen, Listeners1}}, Acc2};
+transform(_Host, Opt, CertFile, Acc) when (Opt == domain_certfile) orelse
+ (Opt == c2s_certfile) orelse
+ (Opt == s2s_certfile) ->
+ ?WARNING_MSG("Option '~s' is deprecated and was automatically "
+ "appended to 'certfiles' option. ~s",
+ [Opt, adjust_hint()]),
+ CertFiles = maps:get(certfiles, Acc, []),
+ Acc1 = maps:put(certfiles, CertFiles ++ [CertFile], Acc),
+ {false, Acc1};
+transform(_Host, certfiles, CertFiles1, Acc) ->
+ CertFiles2 = maps:get(certfiles, Acc, []),
+ Acc1 = maps:put(certfiles, CertFiles1 ++ CertFiles2, Acc),
+ {true, Acc1};
+transform(Host, s2s_use_starttls, required_trusted, Acc) ->
+ ?WARNING_MSG("The value 'required_trusted' of option "
+ "'s2s_use_starttls' is deprecated and was "
+ "automatically replaced with value 'required'. "
+ "The module 'mod_s2s_dialback' has also "
+ "been automatically removed from the configuration. ~s",
+ [adjust_hint()]),
+ Hosts = maps:get(remove_s2s_dialback, Acc, []),
+ Acc1 = maps:put(remove_s2s_dialback, [Host|Hosts], Acc),
+ {{true, {s2s_use_starttls, required}}, Acc1};
+transform(_Host, _Opt, _Val, Acc) ->
+ {true, Acc}.
+
+update(Y, Acc) ->
+ set_certfiles(Y, Acc).
+
+filter(Host, Y, Acc) ->
+ lists:filtermap(
+ fun({Opt, HostOpts}) when (Opt == host_config orelse
+ Opt == append_host_config)
+ andalso Host == global ->
+ HostOpts1 = lists:map(
+ fun({Host1, Opts1}) ->
+ {Host1, filter(Host1, Opts1, Acc)}
+ end, HostOpts),
+ {true, {Opt, HostOpts1}};
+ ({Opt, Val}) ->
+ filter(Host, Opt, Val, Acc)
+ end, Y).
+
+filter(_Host, ca_path, _, _) ->
+ warn_removed_option(ca_path, ca_file),
+ false;
+filter(_Host, iqdisc, _, _) ->
+ warn_removed_option(iqdisc),
+ false;
+filter(_Host, access, _, _) ->
+ warn_removed_option(access, access_rules),
+ false;
+filter(_Host, commands, _, _) ->
+ warn_removed_option(commands, api_permissions),
+ false;
+filter(_Host, ejabberdctl_access_commands, _, _) ->
+ warn_removed_option(ejabberdctl_access_commands, api_permissions),
+ false;
+filter(_Host, commands_admin_access, _, _) ->
+ warn_removed_option(commands_admin_access, api_permissions),
+ false;
+filter(_Host, ldap_group_cache_size, _, _) ->
+ warn_removed_option(ldap_group_cache_size, cache_size),
+ false;
+filter(_Host, ldap_user_cache_size, _, _) ->
+ warn_removed_option(ldap_user_cache_size, cache_size),
+ false;
+filter(_Host, ldap_group_cache_validity, _, _) ->
+ warn_removed_option(ldap_group_cache_validity, cache_life_time),
+ false;
+filter(_Host, ldap_user_cache_validity, _, _) ->
+ warn_removed_option(ldap_user_cache_validity, cache_life_time),
+ false;
+filter(_Host, ldap_local_filter, _, _) ->
+ warn_removed_option(ldap_local_filter),
+ false;
+filter(_Host, deref_aliases, Val, _) ->
+ warn_replaced_option(deref_aliases, ldap_deref_aliases),
+ {true, {ldap_deref_aliases, Val}};
+filter(_Host, default_db, internal, _) ->
+ {true, {default_db, mnesia}};
+filter(_Host, default_db, odbc, _) ->
+ {true, {default_db, sql}};
+filter(_Host, auth_method, Ms, _) ->
+ Ms1 = lists:map(
+ fun(internal) -> mnesia;
+ (odbc) -> sql;
+ (M) -> M
+ end, Ms),
+ {true, {auth_method, Ms1}};
+filter(_Host, default_ram_db, internal, _) ->
+ {true, {default_ram_db, mnesia}};
+filter(_Host, default_ram_db, odbc, _) ->
+ {true, {default_ram_db, sql}};
+filter(_Host, extauth_cache, _, _) ->
+ ?WARNING_MSG("Option 'extauth_cache' is deprecated "
+ "and has no effect, use authentication "
+ "or global cache configuration options: "
+ "auth_use_cache, auth_cache_life_time, "
+ "use_cache, cache_life_time, and so on", []),
+ false;
+filter(_Host, extauth_instances, Val, _) ->
+ warn_replaced_option(extauth_instances, extauth_pool_size),
+ {true, {extauth_pool_size, Val}};
+filter(_Host, Opt, Val, _) when Opt == outgoing_s2s_timeout;
+ Opt == s2s_dns_timeout ->
+ warn_huge_timeout(Opt, Val),
+ true;
+filter(Host, modules, ModOpts, #{remove_s2s_dialback := Hosts}) ->
+ ModOpts1 = case lists:member(Host, Hosts) of
+ true ->
+ lists:filter(
+ fun({mod_s2s_dialback, _}) -> false;
+ (_) -> true
+ end, ModOpts);
+ false ->
+ ModOpts
+ end,
+ {true, {modules, ModOpts1}};
+filter(_, _, _, _) ->
+ true.
+
+%%%===================================================================
+%%% Listener transformers
+%%%===================================================================
+transform_listener(Opts, Acc) ->
+ Opts1 = transform_request_handlers(Opts),
+ Opts2 = remove_inet_options(Opts1),
+ collect_listener_certfiles(Opts2, Acc).
+
+transform_request_handlers(Opts) ->
+ case lists:keyfind(module, 1, Opts) of
+ {_, ejabberd_http} ->
+ replace_request_handlers(Opts);
+ _ ->
+ Opts
+ end.
+
+replace_request_handlers(Opts) ->
+ Handlers = proplists:get_value(request_handlers, Opts, []),
+ Handlers1 =
+ lists:foldl(
+ fun({captcha, true}, Acc) ->
+ Handler = {<<"/captcha">>, ejabberd_captcha},
+ warn_replaced_handler(captcha, Handler),
+ [Handler|Acc];
+ ({register, true}, Acc) ->
+ Handler = {<<"/register">>, mod_register_web},
+ warn_replaced_handler(register, Handler),
+ [Handler|Acc];
+ ({web_admin, true}, Acc) ->
+ Handler = {<<"/admin">>, ejabberd_web_admin},
+ warn_replaced_handler(web_admin, Handler),
+ [Handler|Acc];
+ ({http_bind, true}, Acc) ->
+ Handler = {<<"/bosh">>, mod_bosh},
+ warn_replaced_handler(http_bind, Handler),
+ [Handler|Acc];
+ ({xmlrpc, true}, Acc) ->
+ Handler = {<<"/">>, ejabberd_xmlrpc},
+ warn_replaced_handler(xmlrpc, Handler),
+ Acc ++ [Handler];
+ (_, Acc) ->
+ Acc
+ end, Handlers, Opts),
+ Handlers2 = lists:map(
+ fun({Path, mod_http_bind}) ->
+ warn_replaced_module(mod_http_bind, mod_bosh),
+ {Path, mod_bosh};
+ (PathMod) ->
+ PathMod
+ end, Handlers1),
+ lists:filtermap(
+ fun({captcha, _}) -> false;
+ ({register, _}) -> false;
+ ({web_admin, _}) -> false;
+ ({http_bind, _}) -> false;
+ ({xmlrpc, _}) -> false;
+ ({http_poll, _}) ->
+ ?WARNING_MSG("Listening option 'http_poll' is "
+ "ignored: HTTP Polling support was "
+ "removed in ejabberd 15.04. ~s",
+ [adjust_hint()]),
+ false;
+ ({request_handlers, _}) ->
+ {true, {request_handlers, Handlers2}};
+ (_) -> true
+ end, Opts).
+
+remove_inet_options(Opts) ->
+ lists:filter(
+ fun({Opt, _}) when Opt == inet; Opt == inet6 ->
+ warn_removed_option(Opt, ip),
+ false;
+ (_) ->
+ true
+ end, Opts).
+
+collect_listener_certfiles(Opts, Acc) ->
+ Mod = proplists:get_value(module, Opts),
+ if Mod == ejabberd_http;
+ Mod == ejabberd_c2s;
+ Mod == ejabberd_s2s_in ->
+ case lists:keyfind(certfile, 1, Opts) of
+ {_, CertFile} ->
+ ?WARNING_MSG("Listening option 'certfile' of module ~s "
+ "is deprecated and was automatically "
+ "appended to global 'certfiles' option. ~s",
+ [Mod, adjust_hint()]),
+ CertFiles = maps:get(certfiles, Acc, []),
+ {proplists:delete(certfile, Opts),
+ maps:put(certfiles, [CertFile|CertFiles], Acc)};
+ false ->
+ {Opts, Acc}
+ end;
+ true ->
+ {Opts, Acc}
+ end.
+
+%%%===================================================================
+%%% Module transformers
+%%% NOTE: transform_module_options/1 is called before transform_module/4
+%%%===================================================================
+transform_module_options(Opts) ->
+ lists:filtermap(
+ fun({Opt, internal}) when Opt == db_type;
+ Opt == ram_db_type ->
+ {true, {Opt, mnesia}};
+ ({Opt, odbc}) when Opt == db_type;
+ Opt == ram_db_type ->
+ {true, {Opt, sql}};
+ ({deref_aliases, Val}) ->
+ warn_replaced_option(deref_aliases, ldap_deref_aliases),
+ {true, {ldap_deref_aliases, Val}};
+ ({ldap_group_cache_size, _}) ->
+ warn_removed_option(ldap_group_cache_size, cache_size),
+ false;
+ ({ldap_user_cache_size, _}) ->
+ warn_removed_option(ldap_user_cache_size, cache_size),
+ false;
+ ({ldap_group_cache_validity, _}) ->
+ warn_removed_option(ldap_group_cache_validity, cache_life_time),
+ false;
+ ({ldap_user_cache_validity, _}) ->
+ warn_removed_option(ldap_user_cache_validity, cache_life_time),
+ false;
+ ({iqdisc, _}) ->
+ warn_removed_option(iqdisc),
+ false;
+ (_) ->
+ true
+ end, Opts).
+
+transform_module(_Host, mod_http_bind, Opts, Acc) ->
+ warn_replaced_module(mod_http_bind, mod_bosh),
+ {{mod_bosh, Opts}, Acc};
+transform_module(_Host, mod_vcard_xupdate_odbc, Opts, Acc) ->
+ warn_replaced_module(mod_vcard_xupdate_odbc, mod_vcard_xupdate),
+ {{mod_vcard_xupdate, Opts}, Acc};
+transform_module(_Host, mod_vcard_ldap, Opts, Acc) ->
+ warn_replaced_module(mod_vcard_ldap, mod_vcard, ldap),
+ {{mod_vcard, [{db_type, ldap}|Opts]}, Acc};
+transform_module(_Host, M, Opts, Acc) when (M == mod_announce_odbc orelse
+ M == mod_blocking_odbc orelse
+ M == mod_caps_odbc orelse
+ M == mod_last_odbc orelse
+ M == mod_muc_odbc orelse
+ M == mod_offline_odbc orelse
+ M == mod_privacy_odbc orelse
+ M == mod_private_odbc orelse
+ M == mod_pubsub_odbc orelse
+ M == mod_roster_odbc orelse
+ M == mod_shared_roster_odbc orelse
+ M == mod_vcard_odbc) ->
+ M1 = strip_odbc_suffix(M),
+ warn_replaced_module(M, M1, sql),
+ {{M1, [{db_type, sql}|Opts]}, Acc};
+transform_module(_Host, mod_blocking, Opts, Acc) ->
+ Opts1 = lists:filter(
+ fun({db_type, _}) ->
+ warn_removed_module_option(db_type, mod_blocking),
+ false;
+ (_) ->
+ true
+ end, Opts),
+ {{mod_blocking, Opts1}, Acc};
+transform_module(_Host, mod_carboncopy, Opts, Acc) ->
+ Opts1 = lists:filter(
+ fun({Opt, _}) when Opt == ram_db_type;
+ Opt == use_cache;
+ Opt == cache_size;
+ Opt == cache_missed;
+ Opt == cache_life_time ->
+ warn_removed_module_option(Opt, mod_carboncopy),
+ false;
+ (_) ->
+ true
+ end, Opts),
+ {{mod_carboncopy, Opts1}, Acc};
+transform_module(_Host, mod_http_api, Opts, Acc) ->
+ Opts1 = lists:filter(
+ fun({admin_ip_access, _}) ->
+ warn_removed_option(admin_ip_access, api_permissions),
+ false;
+ (_) ->
+ true
+ end, Opts),
+ {{mod_http_api, Opts1}, Acc};
+transform_module(_Host, Mod, Opts, Acc) ->
+ {{Mod, Opts}, Acc}.
+
+strip_odbc_suffix(M) ->
+ [_|T] = lists:reverse(string:tokens(atom_to_list(M), "_")),
+ list_to_atom(string:join(lists:reverse(T), "_")).
+
+%%%===================================================================
+%%% Aux
+%%%===================================================================
+filtermapfoldr(Fun, Init, List) ->
+ lists:foldr(
+ fun(X, {Ret, Acc}) ->
+ case Fun(X, Acc) of
+ {true, Acc1} -> {[X|Ret], Acc1};
+ {{true, X1}, Acc1} -> {[X1|Ret], Acc1};
+ {false, Acc1} -> {Ret, Acc1}
+ end
+ end, {[], Init}, List).
+
+set_certfiles(Y, #{certfiles := CertFiles} = Acc) ->
+ {lists:keystore(certfiles, 1, Y, {certfiles, CertFiles}), Acc};
+set_certfiles(Y, Acc) ->
+ {Y, Acc}.
+
+%%%===================================================================
+%%% Warnings
+%%%===================================================================
+warn_replaced_module(From, To) ->
+ ?WARNING_MSG("Module ~s is deprecated and was automatically "
+ "replaced by ~s. ~s",
+ [From, To, adjust_hint()]).
+
+warn_replaced_module(From, To, Type) ->
+ ?WARNING_MSG("Module ~s is deprecated and was automatically "
+ "replaced by ~s with db_type: ~s. ~s",
+ [From, To, Type, adjust_hint()]).
+
+warn_replaced_handler(Opt, {Path, Module}) ->
+ ?WARNING_MSG("Listening option '~s' is deprecated "
+ "and was automatically replaced by "
+ "HTTP request handler: \"~s\" -> ~s. ~s",
+ [Opt, Path, Module, adjust_hint()]).
+
+warn_replaced_option(OldOpt, NewOpt) ->
+ ?WARNING_MSG("Option '~s' is deprecated and was automatically "
+ "replaced by '~s'. ~s",
+ [OldOpt, NewOpt, adjust_hint()]).
+
+warn_removed_option(Opt) ->
+ ?WARNING_MSG("Option '~s' is deprecated and has no effect anymore. "
+ "Please remove it from the configuration.", [Opt]).
+
+warn_removed_option(OldOpt, NewOpt) ->
+ ?WARNING_MSG("Option '~s' is deprecated and has no effect anymore. "
+ "Use option '~s' instead.", [OldOpt, NewOpt]).
+
+warn_removed_module_option(Opt, Mod) ->
+ ?WARNING_MSG("Option '~s' of module ~s is deprecated "
+ "and has no effect anymore. ~s",
+ [Opt, Mod, adjust_hint()]).
+
+warn_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
+ ?WARNING_MSG("Value '~B' of option '~s' is too big, "
+ "are you sure you have set seconds?",
+ [T, Opt]);
+warn_huge_timeout(_, _) ->
+ ok.
+
+adjust_hint() ->
+ "Please adjust your configuration accordingly. "
+ "Hint: use `ejabberdctl dump-config` command to view current "
+ "configuration as it is seen by ejabberd.".
+
+%%%===================================================================
+%%% Very raw validator: just to make sure we get properly typed terms
+%%% Expand it if you need to transform more options, but don't
+%%% abuse complex types: simple and composite types are preferred
+%%%===================================================================
+validator() ->
+ Validators =
+ #{s2s_use_starttls => econf:atom(),
+ certfiles => econf:list(econf:any()),
+ c2s_certfile => econf:binary(),
+ s2s_certfile => econf:binary(),
+ domain_certfile => econf:binary(),
+ default_db => econf:atom(),
+ default_ram_db => econf:atom(),
+ auth_method => econf:list_or_single(econf:atom()),
+ listen =>
+ econf:list(
+ econf:options(
+ #{captcha => econf:bool(),
+ register => econf:bool(),
+ web_admin => econf:bool(),
+ http_bind => econf:bool(),
+ http_poll => econf:bool(),
+ xmlrpc => econf:bool(),
+ module => econf:atom(),
+ certfile => econf:binary(),
+ request_handlers =>
+ econf:map(econf:binary(), econf:atom()),
+ '_' => econf:any()},
+ [])),
+ modules =>
+ econf:options(
+ #{'_' =>
+ econf:options(
+ #{db_type => econf:atom(),
+ '_' => econf:any()},
+ [])},
+ []),
+ '_' => econf:any()},
+ econf:options(
+ Validators#{host_config =>
+ econf:map(econf:binary(),
+ econf:options(Validators, [])),
+ append_host_config =>
+ econf:map(econf:binary(),
+ econf:options(Validators, []))},
+ []).
diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl
index f4a898c15..e1c294008 100644
--- a/src/ejabberd_ctl.erl
+++ b/src/ejabberd_ctl.erl
@@ -45,13 +45,11 @@
-module(ejabberd_ctl).
--behaviour(ejabberd_config).
-behaviour(gen_server).
-author('alexey@process-one.net').
-export([start/0, start_link/0, process/1, process2/2,
- register_commands/3, unregister_commands/3,
- opt_type/1]).
+ register_commands/3, unregister_commands/3]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
@@ -177,7 +175,7 @@ process(["status"], _Version) ->
"or other files in that directory.~n", [EjabberdLogPath]),
?STATUS_ERROR;
true ->
- print("ejabberd ~s is running in that node~n", [ejabberd_config:get_version()]),
+ print("ejabberd ~s is running in that node~n", [ejabberd_option:version()]),
?STATUS_SUCCESS
end;
@@ -248,8 +246,7 @@ process(["--version", Arg | Args], _) ->
process(Args, Version);
process(Args, Version) ->
- AccessCommands = get_accesscommands(),
- {String, Code} = process2(Args, AccessCommands, Version),
+ {String, Code} = process2(Args, [], Version),
case String of
[] -> ok;
_ ->
@@ -291,9 +288,6 @@ process2(Args, AccessCommands, Auth, Version) ->
{"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR}
end.
-get_accesscommands() ->
- ejabberd_config:get_option(ejabberdctl_access_commands, []).
-
%%-----------------------------
%% Command calling
%%-----------------------------
@@ -322,8 +316,8 @@ try_run_ctp(Args, Auth, AccessCommands, Version) ->
%% @spec (Args::[string()], Auth, AccessCommands) -> string() | integer() | {string(), integer()}
try_call_command(Args, Auth, AccessCommands, Version) ->
try call_command(Args, Auth, AccessCommands, Version) of
- {error, wrong_command_arguments} ->
- {"Error: wrong arguments", ?STATUS_ERROR};
+ {Reason, wrong_command_arguments} ->
+ {Reason, ?STATUS_ERROR};
Res ->
Res
catch
@@ -346,32 +340,28 @@ call_command([CmdString | Args], Auth, _AccessCommands, Version) ->
CmdStringU = ejabberd_regexp:greplace(
list_to_binary(CmdString), <<"-">>, <<"_">>),
Command = list_to_atom(binary_to_list(CmdStringU)),
- case ejabberd_commands:get_command_format(Command, Auth, Version) of
- {error, command_unknown} ->
- throw({error, unknown_command});
- {ArgsFormat, ResultFormat} ->
- case (catch format_args(Args, ArgsFormat)) of
- ArgsFormatted when is_list(ArgsFormatted) ->
- CI = case Auth of
- {U, S, _, _} -> #{usr => {U, S, <<"">>}, caller_host => S};
- _ -> #{}
- end,
- CI2 = CI#{caller_module => ?MODULE},
- Result = ejabberd_commands:execute_command2(Command,
- ArgsFormatted,
- CI2,
- Version),
- format_result(Result, ResultFormat);
- {'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} ->
- {NumCompa, TextCompa} =
- case {length(A1), length(A2)} of
- {L1, L2} when L1 < L2 -> {L2-L1, "less argument"};
- {L1, L2} when L1 > L2 -> {L1-L2, "more argument"}
- end,
- {io_lib:format("Error: the command ~p requires ~p ~s.",
- [CmdString, NumCompa, TextCompa]),
- wrong_command_arguments}
- end
+ {ArgsFormat, ResultFormat} = ejabberd_commands:get_command_format(Command, Auth, Version),
+ case (catch format_args(Args, ArgsFormat)) of
+ ArgsFormatted when is_list(ArgsFormatted) ->
+ CI = case Auth of
+ {U, S, _, _} -> #{usr => {U, S, <<"">>}, caller_host => S};
+ _ -> #{}
+ end,
+ CI2 = CI#{caller_module => ?MODULE},
+ Result = ejabberd_commands:execute_command2(Command,
+ ArgsFormatted,
+ CI2,
+ Version),
+ format_result(Result, ResultFormat);
+ {'EXIT', {function_clause,[{lists,zip,[A1, A2], _} | _]}} ->
+ {NumCompa, TextCompa} =
+ case {length(A1), length(A2)} of
+ {L1, L2} when L1 < L2 -> {L2-L1, "less argument"};
+ {L1, L2} when L1 > L2 -> {L1-L2, "more argument"}
+ end,
+ {io_lib:format("Error: the command ~p requires ~p ~s.",
+ [CmdString, NumCompa, TextCompa]),
+ wrong_command_arguments}
end.
@@ -735,11 +725,12 @@ print_usage_help(MaxC, ShCode) ->
"Those commands can be identified because the description starts with: *"],
ArgsDef = [],
C = #ejabberd_commands{
- desc = "Show help of ejabberd commands",
- longdesc = lists:flatten(LongDesc),
- args = ArgsDef,
- result = {help, string}},
- print_usage_command("help", C, MaxC, ShCode).
+ name = help,
+ desc = "Show help of ejabberd commands",
+ longdesc = lists:flatten(LongDesc),
+ args = ArgsDef,
+ result = {help, string}},
+ print_usage_command2("help", C, MaxC, ShCode).
%%-----------------------------
@@ -792,12 +783,8 @@ filter_commands_regexp(All, Glob) ->
%% @spec (Cmd::string(), MaxC::integer(), ShCode::boolean()) -> ok
print_usage_command(Cmd, MaxC, ShCode, Version) ->
Name = list_to_atom(Cmd),
- case ejabberd_commands:get_command_definition(Name, Version) of
- command_not_found ->
- io:format("Error: command ~p not known.~n", [Cmd]);
- C ->
- print_usage_command2(Cmd, C, MaxC, ShCode)
- end.
+ C = ejabberd_commands:get_command_definition(Name, Version),
+ print_usage_command2(Cmd, C, MaxC, ShCode).
print_usage_command2(Cmd, C, MaxC, ShCode) ->
#ejabberd_commands{
@@ -881,9 +868,3 @@ print(Format, Args) ->
%% Struct(Integer res) create_account(Struct(String user, String server, String password))
%%format_usage_xmlrpc(ArgsDef, ResultDef) ->
%% ["aaaa bbb ccc"].
-
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(ejabberdctl_access_commands) ->
- fun (V) when is_list(V) -> V end;
-opt_type(_) -> [ejabberdctl_access_commands].
diff --git a/src/ejabberd_db_sup.erl b/src/ejabberd_db_sup.erl
new file mode 100644
index 000000000..f16e60adf
--- /dev/null
+++ b/src/ejabberd_db_sup.erl
@@ -0,0 +1,46 @@
+%%%-------------------------------------------------------------------
+%%% Created : 13 June 2019 by Evgeny Khramtsov <ekhramtsov@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(ejabberd_db_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+%%%===================================================================
+%%% API functions
+%%%===================================================================
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%%%===================================================================
+%%% Supervisor callbacks
+%%%===================================================================
+init([]) ->
+ {ok, {{one_for_one, 10, 1}, []}}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl
index bbd2050a3..6a5caaaca 100644
--- a/src/ejabberd_hooks.erl
+++ b/src/ejabberd_hooks.erl
@@ -151,11 +151,13 @@ run(Hook, Args) ->
-spec run(atom(), binary() | global, list()) -> ok.
run(Hook, Host, Args) ->
- case ets:lookup(hooks, {Hook, Host}) of
+ try ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run1(Ls, Hook, Args);
[] ->
ok
+ catch _:badarg ->
+ ok
end.
-spec run_fold(atom(), any(), list()) -> any().
@@ -171,11 +173,13 @@ run_fold(Hook, Val, Args) ->
-spec run_fold(atom(), binary() | global, any(), list()) -> any().
run_fold(Hook, Host, Val, Args) ->
- case ets:lookup(hooks, {Hook, Host}) of
+ try ets:lookup(hooks, {Hook, Host}) of
[{_, Ls}] ->
run_fold1(Ls, Hook, Val, Args);
[] ->
Val
+ catch _:badarg ->
+ Val
end.
%%%----------------------------------------------------------------------
@@ -190,7 +194,7 @@ run_fold(Hook, Host, Val, Args) ->
%% {stop, Reason}
%%----------------------------------------------------------------------
init([]) ->
- ets:new(hooks, [named_table, {read_concurrency, true}]),
+ _ = ets:new(hooks, [named_table, {read_concurrency, true}]),
{ok, #state{}}.
%%----------------------------------------------------------------------
@@ -381,13 +385,14 @@ safe_apply(Hook, Module, Function, Args) ->
apply(Module, Function, Args)
end
catch ?EX_RULE(E, R, St) when E /= exit; R /= normal ->
+ Stack = ?EX_STACK(St),
?ERROR_MSG("Hook ~p crashed when running ~p:~p/~p:~n" ++
string:join(
- ["** Reason = ~p"|
+ ["** ~s"|
["** Arg " ++ integer_to_list(I) ++ " = ~p"
|| I <- lists:seq(1, length(Args))]],
"~n"),
[Hook, Module, Function, length(Args),
- {E, R, ?EX_STACK(St)}|Args]),
+ misc:format_exception(2, E, R, Stack)|Args]),
'EXIT'
end.
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index 29d23e082..39413e2f3 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -25,17 +25,15 @@
-module(ejabberd_http).
-behaviour(ejabberd_listener).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
%% External exports
-export([start/3, start_link/3,
accept/1, receive_headers/1, recv_file/2,
- transform_listen_option/2, listen_opt_type/1,
- listen_options/0]).
+ listen_opt_type/1, listen_options/0]).
--export([init/3, opt_type/1]).
+-export([init/3]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -112,9 +110,10 @@ init(SockMod, Socket, Opts) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
- TLSOpts3 = case get_certfile(Opts) of
- undefined -> TLSOpts2;
- CertFile -> [{certfile, CertFile}|TLSOpts2]
+ TLSOpts3 = case ejabberd_pkix:get_certfile(
+ ejabberd_config:get_myname()) of
+ error -> TLSOpts2;
+ {ok, CertFile} -> [{certfile, CertFile}|TLSOpts2]
end,
TLSOpts = [verify_none | TLSOpts3],
{SockMod1, Socket1} = if TLSEnabled ->
@@ -124,30 +123,8 @@ init(SockMod, Socket, Opts) ->
{fast_tls, TLSSocket};
true -> {SockMod, Socket}
end,
- Captcha = case proplists:get_bool(captcha, Opts) of
- true -> [{[<<"captcha">>], ejabberd_captcha}];
- false -> []
- end,
- Register = case proplists:get_bool(register, Opts) of
- true -> [{[<<"register">>], mod_register_web}];
- false -> []
- end,
- Admin = case proplists:get_bool(web_admin, Opts) of
- true -> [{[<<"admin">>], ejabberd_web_admin}];
- false -> []
- end,
- Bind = case proplists:get_bool(http_bind, Opts) of
- true -> [{[<<"http-bind">>], mod_bosh}];
- false -> []
- end,
- XMLRPC = case proplists:get_bool(xmlrpc, Opts) of
- 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,
+ RequestHandlers = proplists:get_value(request_handlers, Opts, []),
?DEBUG("S: ~p~n", [RequestHandlers]),
DefaultHost = proplists:get_value(default_host, Opts),
@@ -557,7 +534,7 @@ analyze_ip_xff(IP, [], _Host) -> IP;
analyze_ip_xff({IPLast, Port}, XFF, Host) ->
[ClientIP | ProxiesIPs] = str:tokens(XFF, <<", ">>) ++
[misc:ip_to_list(IPLast)],
- TrustedProxies = ejabberd_config:get_option({trusted_proxies, Host}, []),
+ TrustedProxies = ejabberd_option:trusted_proxies(Host),
IPClient = case is_ipchain_trusted(ProxiesIPs,
TrustedProxies)
of
@@ -581,7 +558,7 @@ is_ipchain_trusted(UserIPs, Masks) ->
{ok, IP2} ->
lists:any(
fun({Mask, MaskLen}) ->
- acl:ip_matches_mask(IP2, Mask, MaskLen)
+ misc:match_ip_mask(IP2, Mask, MaskLen)
end, Masks);
_ ->
false
@@ -803,7 +780,7 @@ rest_dir(N, Path, <<_H, T/binary>>) -> rest_dir(N, Path, T).
expand_custom_headers(Headers) ->
lists:map(fun({K, V}) ->
{K, misc:expand_keyword(<<"@VERSION@">>, V,
- ejabberd_config:get_version())}
+ ejabberd_option:version())}
end, Headers).
code_to_phrase(100) -> <<"Continue">>;
@@ -851,7 +828,7 @@ code_to_phrase(503) -> <<"Service Unavailable">>;
code_to_phrase(504) -> <<"Gateway Timeout">>;
code_to_phrase(505) -> <<"HTTP Version Not Supported">>.
--spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | undefined.
+-spec parse_auth(binary()) -> {binary(), binary()} | {oauth, binary(), []} | invalid.
parse_auth(<<"Basic ", Auth64/binary>>) ->
try base64:decode(Auth64) of
Auth ->
@@ -927,150 +904,32 @@ normalize_path([_Parent, <<"..">>|Path], Norm) ->
normalize_path([Part | Path], Norm) ->
normalize_path(Path, [Part|Norm]).
--spec get_certfile([proplists:property()]) -> binary() | undefined.
-get_certfile(Opts) ->
- case lists:keyfind(certfile, 1, Opts) of
- {_, CertFile} ->
- CertFile;
- false ->
- case ejabberd_pkix:get_certfile(ejabberd_config:get_myname()) of
- {ok, CertFile} ->
- CertFile;
- error ->
- ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()})
- end
- end.
-
-transform_listen_option(captcha, Opts) ->
- [{captcha, true}|Opts];
-transform_listen_option(register, Opts) ->
- [{register, true}|Opts];
-transform_listen_option(web_admin, Opts) ->
- [{web_admin, true}|Opts];
-transform_listen_option(http_bind, Opts) ->
- [{http_bind, true}|Opts];
-transform_listen_option(http_poll, Opts) ->
- Opts;
-transform_listen_option({request_handlers, Hs}, Opts) ->
- Hs1 = lists:map(
- fun({PList, Mod}) when is_list(PList) ->
- Path = iolist_to_binary([[$/, P] || P <- PList]),
- {Path, Mod};
- (Opt) ->
- Opt
- end, Hs),
- [{request_handlers, Hs1} | Opts];
-transform_listen_option(Opt, Opts) ->
- [Opt|Opts].
-
-prepare_request_module(mod_http_bind) ->
- mod_bosh;
-prepare_request_module(Mod) when is_atom(Mod) ->
- case code:ensure_loaded(Mod) of
- {module, Mod} ->
- Mod;
- Err ->
- ?ERROR_MSG(
- "Failed to load request handler ~s, "
- "did you mean ~s? Hint: "
- "make sure there is no typo and file ~s.beam "
- "exists inside either ~s or ~s directory",
- [Mod,
- misc:best_match(Mod, ejabberd_config:get_modules()),
- Mod,
- filename:dirname(code:which(?MODULE)),
- ext_mod:modules_dir()]),
- erlang:error(Err)
- end.
-
-emit_option_replacement(Option, Path, Handler) ->
- ?WARNING_MSG(
- "Listening option '~s' is deprecated, enable it via request handlers, e.g.:~n"
- "listen:~n"
- " ...~n"
- " -~n"
- " module: ~s~n"
- " request_handlers:~n"
- " ...~n"
- " \"~s\": ~s~n",
- [Option, ?MODULE, Path, Handler]).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(trusted_proxies) ->
- fun (all) -> all;
- (TPs) -> lists:filtermap(
- fun(TP) ->
- case acl:parse_ip_netmask(iolist_to_binary(TP)) of
- {ok, Ip, Mask} -> {true, {Ip, Mask}};
- _ -> false
- end
- end, TPs)
- end;
-opt_type(_) -> [trusted_proxies].
-
-listen_opt_type(certfile = Opt) ->
- fun(S) ->
- ?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
- "'certfiles' global option instead", [Opt, ?MODULE]),
- {ok, File} = ejabberd_pkix:add_certfile(S),
- File
- end;
-listen_opt_type(captcha) ->
- fun(B) when is_boolean(B) ->
- emit_option_replacement(captcha, "/captcha", ejabberd_captcha),
- B
- end;
-listen_opt_type(register) ->
- fun(B) when is_boolean(B) ->
- emit_option_replacement(register, "/register", mod_register_web),
- B
- end;
-listen_opt_type(web_admin) ->
- fun(B) when is_boolean(B) ->
- emit_option_replacement(web_admin, "/admin", ejabberd_web_admin),
- B
- end;
-listen_opt_type(http_bind) ->
- fun(B) when is_boolean(B) ->
- emit_option_replacement(http_bind, "/bosh", mod_bosh),
- B
- end;
-listen_opt_type(xmlrpc) ->
- fun(B) when is_boolean(B) ->
- emit_option_replacement(xmlrpc, "/", ejabberd_xmlrpc),
- B
- end;
listen_opt_type(tag) ->
- fun(B) when is_binary(B) -> B end;
+ econf:binary();
listen_opt_type(request_handlers) ->
- fun(Hs) ->
- Hs1 = lists:map(fun
- ({Mod, Path}) when is_atom(Mod) -> {Path, Mod};
- ({Path, Mod}) -> {Path, Mod}
- end, Hs),
- Hs2 = [{str:tokens(
- iolist_to_binary(Path), <<"/">>),
- Mod} || {Path, Mod} <- Hs1],
- [{Path, prepare_request_module(Mod)} || {Path, Mod} <- Hs2]
- end;
+ econf:and_then(
+ econf:map(
+ econf:binary(),
+ econf:beam([[{socket_handoff, 3}, {process, 2}]])),
+ fun(L) ->
+ [{str:tokens(Path, <<"/">>), Mod} || {Path, Mod} <- L]
+ end);
listen_opt_type(default_host) ->
- fun iolist_to_binary/1;
+ econf:domain();
listen_opt_type(custom_headers) ->
- fun expand_custom_headers/1.
+ econf:and_then(
+ econf:map(
+ econf:binary(),
+ econf:binary()),
+ fun expand_custom_headers/1).
listen_options() ->
- [{certfile, undefined},
- {ciphers, undefined},
+ [{ciphers, undefined},
{dhfile, undefined},
{cafile, undefined},
{protocol_options, undefined},
{tls, false},
{tls_compression, false},
- {captcha, false},
- {register, false},
- {web_admin, false},
- {http_bind, false},
- {xmlrpc, false},
{request_handlers, []},
{tag, <<>>},
{default_host, undefined},
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index 26e68fdaa..6e7a0901d 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -40,15 +40,12 @@
-include("ejabberd_http.hrl").
--define(PING_INTERVAL, 60).
--define(WEBSOCKET_TIMEOUT, 300).
-
-record(state,
{socket :: ws_socket(),
- ping_interval = ?PING_INTERVAL :: non_neg_integer(),
+ ping_interval :: non_neg_integer(),
ping_timer = make_ref() :: reference(),
pong_expected = false :: boolean(),
- timeout = ?WEBSOCKET_TIMEOUT :: non_neg_integer(),
+ timeout :: non_neg_integer(),
timer = make_ref() :: reference(),
input = [] :: list(),
active = false :: boolean(),
@@ -133,12 +130,8 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
(_) -> false
end, HOpts),
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
- PingInterval = ejabberd_config:get_option(
- {websocket_ping_interval, ejabberd_config:get_myname()},
- ?PING_INTERVAL) * 1000,
- WSTimeout = ejabberd_config:get_option(
- {websocket_timeout, ejabberd_config:get_myname()},
- ?WEBSOCKET_TIMEOUT) * 1000,
+ PingInterval = ejabberd_option:websocket_ping_interval(),
+ WSTimeout = ejabberd_option:websocket_timeout(),
Socket = {http_ws, self(), IP},
?DEBUG("Client connected through websocket ~p",
[Socket]),
@@ -201,15 +194,15 @@ handle_sync_event({send_xml, Packet}, _From, StateName,
case Packet2 of
{xmlstreamstart, Name, Attrs3} ->
B = fxml:element_to_binary(#xmlel{name = Name, attrs = Attrs3}),
- WsPid ! {text, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>};
+ route_text(WsPid, <<(binary:part(B, 0, byte_size(B)-2))/binary, ">">>);
{xmlstreamend, Name} ->
- WsPid ! {text, <<"</", Name/binary, ">">>};
+ route_text(WsPid, <<"</", Name/binary, ">">>);
{xmlstreamelement, El} ->
- WsPid ! {text, fxml:element_to_binary(El)};
+ route_text(WsPid, fxml:element_to_binary(El));
{xmlstreamraw, Bin} ->
- WsPid ! {text, Bin};
+ route_text(WsPid, Bin);
{xmlstreamcdata, Bin2} ->
- WsPid ! {text, Bin2};
+ route_text(WsPid, Bin2);
skip ->
ok
end,
@@ -224,7 +217,7 @@ handle_sync_event(close, _From, StateName, #state{ws = {_, WsPid}, rfc_compilant
when StateName /= stream_end_sent ->
Close = #xmlel{name = <<"close">>,
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>}]},
- WsPid ! {text, fxml:element_to_binary(Close)},
+ route_text(WsPid, fxml:element_to_binary(Close)),
{stop, normal, StateData};
handle_sync_event(close, _From, _StateName, StateData) ->
{stop, normal, StateData}.
@@ -366,3 +359,8 @@ parsed_items(List) ->
after 0 ->
lists:reverse(List)
end.
+
+-spec route_text(pid(), binary()) -> ok.
+route_text(Pid, Data) ->
+ Pid ! {text, Data},
+ ok.
diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl
index 65902eeb9..4de88afa1 100644
--- a/src/ejabberd_iq.erl
+++ b/src/ejabberd_iq.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%%% File : ejabberd_iq.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Purpose :
+%%% Purpose :
%%% Created : 10 Nov 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
@@ -70,7 +70,7 @@ dispatch(_) ->
%%% gen_server callbacks
%%%===================================================================
init([]) ->
- ets:new(?MODULE, [named_table, ordered_set, public]),
+ _ = ets:new(?MODULE, [named_table, ordered_set, public]),
{ok, #state{}}.
handle_call(Request, From, State) ->
@@ -166,7 +166,7 @@ decode_id(_) ->
-spec calc_checksum(binary()) -> binary().
calc_checksum(Data) ->
- Key = ejabberd_config:get_option(shared_key),
+ Key = ejabberd_config:get_shared_key(),
base64:encode(crypto:hash(sha, <<Data/binary, Key/binary>>)).
-spec callback(atom() | pid(), #iq{} | timeout, term()) -> any().
diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl
index 73baf3142..f648b3eb3 100644
--- a/src/ejabberd_listener.erl
+++ b/src/ejabberd_listener.erl
@@ -25,36 +25,40 @@
-module(ejabberd_listener).
-behaviour(supervisor).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
-author('ekhramtsov@process-one.net').
-export([start_link/0, init/1, start/3, init/3,
start_listeners/0, start_listener/3, stop_listeners/0,
- stop_listener/2, add_listener/3, delete_listener/2,
- transform_options/1, validate_cfg/1, opt_type/1,
- config_reloaded/0, get_certfiles/0]).
-%% Legacy API
--export([parse_listener_portip/2]).
+ add_listener/3, delete_listener/2,
+ config_reloaded/0]).
+-export([listen_options/0, listen_opt_type/1, validator/0]).
+-export([tls_listeners/0]).
-include("logger.hrl").
-type transport() :: tcp | udp.
-type endpoint() :: {inet:port_number(), inet:ip_address(), transport()}.
--type listen_opts() :: [proplists:property()].
--type listener() :: {endpoint(), module(), listen_opts()}.
+-type list_opts() :: [{atom(), term()}].
+-type opts() :: #{atom() => term()}.
+-type listener() :: {endpoint(), module(), opts()}.
-type sockmod() :: gen_tcp.
-type socket() :: inet:socket().
+-type state() :: term().
--callback start(sockmod(), socket(), listen_opts()) ->
+-export_type([listener/0]).
+
+-callback start(sockmod(), socket(), state()) ->
{ok, pid()} | {error, any()} | ignore.
--callback start_link(sockmod(), socket(), listen_opts()) ->
+-callback start_link(sockmod(), socket(), state()) ->
{ok, pid()} | {error, any()} | ignore.
-callback accept(pid()) -> any().
--callback listen_opt_type(atom()) -> fun((term()) -> term()).
--callback listen_options() -> listen_opts().
+-callback listen_opt_type(atom()) -> econf:validator().
+-callback listen_options() -> [{atom(), term()} | atom()].
+-callback tcp_init(socket(), list_opts()) -> state().
+-callback udp_init(socket(), list_opts()) -> state().
--optional_callbacks([listen_opt_type/1]).
+-optional_callbacks([listen_opt_type/1, tcp_init/2, udp_init/2]).
-define(TCP_SEND_TIMEOUT, 15000).
@@ -62,9 +66,9 @@ start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_) ->
- ets:new(?MODULE, [named_table, public]),
+ _ = ets:new(?MODULE, [named_table, public]),
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
- Listeners = ejabberd_config:get_option(listen, []),
+ Listeners = ejabberd_option:listen(),
{ok, {{one_for_one, 10, 1}, listeners_childspec(Listeners)}}.
-spec listeners_childspec([listener()]) -> [supervisor:child_spec()].
@@ -79,22 +83,22 @@ listeners_childspec(Listeners) ->
-spec start_listeners() -> ok.
start_listeners() ->
- Listeners = ejabberd_config:get_option(listen, []),
+ Listeners = ejabberd_option:listen(),
lists:foreach(
fun(Spec) ->
supervisor:start_child(?MODULE, Spec)
end, listeners_childspec(Listeners)).
--spec start(endpoint(), module(), listen_opts()) -> term().
+-spec start(endpoint(), module(), opts()) -> term().
start(EndPoint, Module, Opts) ->
proc_lib:start_link(?MODULE, init, [EndPoint, Module, Opts]).
--spec init(endpoint(), module(), listen_opts()) -> ok.
-init(EndPoint, Module, AllOpts) ->
- {ModuleOpts, SockOpts} = split_opts(AllOpts),
+-spec init(endpoint(), module(), opts()) -> ok.
+init({_, _, Transport} = EndPoint, Module, AllOpts) ->
+ {ModuleOpts, SockOpts} = split_opts(Transport, AllOpts),
init(EndPoint, Module, ModuleOpts, SockOpts).
--spec init(endpoint(), module(), listen_opts(), [gen_tcp:option()]) -> ok.
+-spec init(endpoint(), module(), opts(), [gen_tcp:option()]) -> ok.
init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
case gen_udp:open(Port, [binary,
{active, false},
@@ -104,22 +108,21 @@ init({Port, _, udp} = EndPoint, Module, Opts, SockOpts) ->
case inet:sockname(Socket) of
{ok, {Addr, Port1}} ->
proc_lib:init_ack({ok, self()}),
- application:ensure_started(ejabberd),
- ?INFO_MSG("Start accepting UDP connections at ~s for ~p",
- [format_endpoint({Port1, Addr, udp}), Module]),
- case erlang:function_exported(Module, udp_init, 2) of
- false ->
- udp_recv(Socket, Module, Opts);
- true ->
- case catch Module:udp_init(Socket, Opts) of
- {'EXIT', _} = Err ->
- ?ERROR_MSG("failed to process callback function "
- "~p:~s(~p, ~p): ~p",
- [Module, udp_init, Socket, Opts, Err]),
- udp_recv(Socket, Module, Opts);
- NewOpts ->
- udp_recv(Socket, Module, NewOpts)
- end
+ case application:ensure_started(ejabberd) of
+ ok ->
+ ?INFO_MSG("Start accepting ~s connections at ~s for ~p",
+ [format_transport(udp, Opts),
+ format_endpoint({Port1, Addr, udp}), Module]),
+ Opts1 = opts_to_list(Module, Opts),
+ case erlang:function_exported(Module, udp_init, 2) of
+ false ->
+ udp_recv(Socket, Module, Opts1);
+ true ->
+ State = Module:udp_init(Socket, Opts1),
+ udp_recv(Socket, Module, State)
+ end;
+ {error, _} ->
+ ok
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
@@ -135,27 +138,28 @@ init({Port, _, tcp} = EndPoint, Module, Opts, SockOpts) ->
case inet:sockname(ListenSocket) of
{ok, {Addr, Port1}} ->
proc_lib:init_ack({ok, self()}),
- application:ensure_started(ejabberd),
- Sup = start_module_sup(Module, Opts),
- ?INFO_MSG("Start accepting TCP connections at ~s for ~p",
- [format_endpoint({Port1, Addr, tcp}), Module]),
- case erlang:function_exported(Module, tcp_init, 2) of
- false ->
- accept(ListenSocket, Module, Opts, Sup);
- true ->
- case catch Module:tcp_init(ListenSocket, Opts) of
- {'EXIT', _} = Err ->
- ?ERROR_MSG("failed to process callback function "
- "~p:~s(~p, ~p): ~p",
- [Module, tcp_init, ListenSocket, Opts, Err]),
- accept(ListenSocket, Module, Opts, Sup);
- NewOpts ->
- accept(ListenSocket, Module, NewOpts, Sup)
- end
+ case application:ensure_started(ejabberd) of
+ ok ->
+ Sup = start_module_sup(Module, Opts),
+ Interval = maps:get(accept_interval, Opts),
+ Proxy = maps:get(use_proxy_protocol, Opts),
+ ?INFO_MSG("Start accepting ~s connections at ~s for ~p",
+ [format_transport(tcp, Opts),
+ format_endpoint({Port1, Addr, tcp}), Module]),
+ Opts1 = opts_to_list(Module, Opts),
+ case erlang:function_exported(Module, tcp_init, 2) of
+ false ->
+ accept(ListenSocket, Module, Opts1, Sup, Interval, Proxy);
+ true ->
+ State = Module:tcp_init(ListenSocket, Opts1),
+ accept(ListenSocket, Module, State, Sup, Interval, Proxy)
+ end;
+ {error, _} ->
+ ok
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
- Err
+ proc_lib:init_ack(Err)
end;
{error, Reason} = Err ->
report_socket_error(Reason, EndPoint, Module),
@@ -181,113 +185,115 @@ listen_tcp(Port, SockOpts) ->
Err
end.
--spec split_opts(listen_opts()) -> {listen_opts(), [gen_tcp:option()]}.
-split_opts(Opts) ->
- lists:foldl(
- fun(Opt, {ModOpts, SockOpts} = Acc) ->
- case Opt of
- {ip, _} -> {ModOpts, [Opt|SockOpts]};
- {backlog, _} -> {ModOpts, [Opt|SockOpts]};
- {inet, true} -> {ModOpts, [inet|SockOpts]};
- {inet6, true} -> {ModOpts, [int6|SockOpts]};
- {inet, false} -> Acc;
- {inet6, false} -> Acc;
- _ -> {[Opt|ModOpts], SockOpts}
+-spec split_opts(transport(), opts()) -> {opts(), [gen_tcp:option()]}.
+split_opts(Transport, Opts) ->
+ maps:fold(
+ fun(Opt, Val, {ModOpts, SockOpts}) ->
+ case OptVal = {Opt, Val} of
+ {ip, _} ->
+ {ModOpts, [OptVal|SockOpts]};
+ {backlog, _} when Transport == tcp ->
+ {ModOpts, [OptVal|SockOpts]};
+ {backlog, _} ->
+ {ModOpts, SockOpts};
+ _ ->
+ {ModOpts#{Opt => Val}, SockOpts}
end
- end, {[], []}, Opts).
+ end, {#{}, []}, Opts).
--spec accept(inet:socket(), module(), listen_opts(), atom()) -> no_return().
-accept(ListenSocket, Module, Opts, Sup) ->
- Interval = proplists:get_value(accept_interval, Opts, 0),
+-spec accept(inet:socket(), module(), state(), atom(),
+ non_neg_integer(), boolean()) -> no_return().
+accept(ListenSocket, Module, State, Sup, Interval, Proxy) ->
Arity = case erlang:function_exported(Module, start, 3) of
true -> 3;
false -> 2
end,
- accept(ListenSocket, Module, Opts, Sup, Interval, Arity).
+ accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity).
--spec accept(inet:socket(), module(), listen_opts(), atom(),
- non_neg_integer(), 2|3) -> no_return().
-accept(ListenSocket, Module, Opts, Sup, Interval, Arity) ->
- NewInterval = check_rate_limit(Interval),
+-spec accept(inet:socket(), module(), state(), atom(),
+ non_neg_integer(), boolean(), 2|3) -> no_return().
+accept(ListenSocket, Module, State, Sup, Interval, Proxy, Arity) ->
+ NewInterval = apply_rate_limit(Interval),
case gen_tcp:accept(ListenSocket) of
+ {ok, Socket} when Proxy ->
+ case proxy_protocol:decode(gen_tcp, Socket, 10000) of
+ {error, Err} ->
+ ?ERROR_MSG("(~w) Proxy protocol parsing failed: ~s",
+ [ListenSocket, format_error(Err)]),
+ gen_tcp:close(Socket);
+ {{Addr, Port}, {PAddr, PPort}} = SP ->
+ %% THIS IS WRONG
+ State2 = [{sock_peer_name, SP} | State],
+ Receiver = case start_connection(Module, Arity, Socket, State2, Sup) of
+ {ok, RecvPid} ->
+ RecvPid;
+ _ ->
+ gen_tcp:close(Socket),
+ none
+ end,
+ ?INFO_MSG("(~p) Accepted proxied connection ~s -> ~s",
+ [Receiver,
+ ejabberd_config:may_hide_data(
+ format_endpoint({PPort, PAddr, tcp})),
+ format_endpoint({Port, Addr, tcp})])
+ end,
+ accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity);
{ok, Socket} ->
- 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, Arity, 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;
+ case {inet:sockname(Socket), inet:peername(Socket)} of
+ {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
+ Receiver = case start_connection(Module, Arity, Socket, State, Sup) of
+ {ok, RecvPid} ->
+ RecvPid;
+ _ ->
+ gen_tcp:close(Socket),
+ none
+ end,
+ ?INFO_MSG("(~p) Accepted connection ~s -> ~s",
+ [Receiver,
+ ejabberd_config:may_hide_data(
+ format_endpoint({PPort, PAddr, tcp})),
+ format_endpoint({Port, Addr, tcp})]);
_ ->
- case {inet:sockname(Socket), inet:peername(Socket)} of
- {{ok, {Addr, Port}}, {ok, {PAddr, PPort}}} ->
- Receiver = case start_connection(Module, Arity, 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
+ gen_tcp:close(Socket)
end,
- accept(ListenSocket, Module, Opts, Sup, NewInterval, Arity);
+ accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity);
{error, Reason} ->
?ERROR_MSG("(~w) Failed TCP accept: ~s",
- [ListenSocket, inet:format_error(Reason)]),
- accept(ListenSocket, Module, Opts, Sup, NewInterval, Arity)
+ [ListenSocket, format_error(Reason)]),
+ accept(ListenSocket, Module, State, Sup, NewInterval, Proxy, Arity)
end.
--spec udp_recv(inet:socket(), module(), listen_opts()) -> no_return().
-udp_recv(Socket, Module, Opts) ->
+-spec udp_recv(inet:socket(), module(), state()) -> no_return().
+udp_recv(Socket, Module, State) ->
case gen_udp:recv(Socket, 0) of
{ok, {Addr, Port, Packet}} ->
- case catch Module:udp_recv(Socket, Addr, Port, Packet, Opts) of
+ case catch Module:udp_recv(Socket, Addr, Port, Packet, State) of
{'EXIT', Reason} ->
- ?ERROR_MSG("failed to process UDP packet:~n"
+ ?ERROR_MSG("Failed to process UDP packet:~n"
"** Source: {~p, ~p}~n"
"** Reason: ~p~n** Packet: ~p",
[Addr, Port, Reason, Packet]),
- udp_recv(Socket, Module, Opts);
- NewOpts ->
- udp_recv(Socket, Module, NewOpts)
+ udp_recv(Socket, Module, State);
+ NewState ->
+ udp_recv(Socket, Module, NewState)
end;
{error, Reason} ->
- ?ERROR_MSG("unexpected UDP error: ~s", [format_error(Reason)]),
+ ?ERROR_MSG("Unexpected UDP error: ~s", [format_error(Reason)]),
throw({error, Reason})
end.
--spec start_connection(module(), 2|3, inet:socket(), listen_opts(), atom()) ->
+-spec start_connection(module(), 2|3, inet:socket(), state(), atom()) ->
{ok, pid()} | {error, any()} | ignore.
-start_connection(Module, Arity, Socket, Opts, Sup) ->
+start_connection(Module, Arity, Socket, State, Sup) ->
Res = case Sup of
undefined when Arity == 3 ->
- Module:start(gen_tcp, Socket, Opts);
+ Module:start(gen_tcp, Socket, State);
undefined ->
- Module:start({gen_tcp, Socket}, Opts);
+ Module:start({gen_tcp, Socket}, State);
_ when Arity == 3 ->
- supervisor:start_child(Sup, [gen_tcp, Socket, Opts]);
+ supervisor:start_child(Sup, [gen_tcp, Socket, State]);
_ ->
- supervisor:start_child(Sup, [{gen_tcp, Socket}, Opts])
+ supervisor:start_child(Sup, [{gen_tcp, Socket}, State])
end,
case Res of
{ok, Pid} ->
@@ -303,7 +309,7 @@ start_connection(Module, Arity, Socket, Opts, Sup) ->
Err
end.
--spec start_listener(endpoint(), module(), listen_opts()) ->
+-spec start_listener(endpoint(), module(), opts()) ->
{ok, pid()} | {error, any()}.
start_listener(EndPoint, Module, Opts) ->
%% It is only required to start the supervisor in some cases.
@@ -323,9 +329,9 @@ start_listener(EndPoint, Module, Opts) ->
{error, Error}
end.
--spec start_module_sup(module(), [proplists:property()]) -> atom().
+-spec start_module_sup(module(), opts()) -> atom().
start_module_sup(Module, Opts) ->
- case proplists:get_value(supervisor, Opts, true) of
+ case maps:get(supervisor, Opts) of
true ->
Proc = list_to_atom(atom_to_list(Module) ++ "_sup"),
ChildSpec = {Proc, {ejabberd_tmp_sup, start_link, [Proc, Module]},
@@ -333,13 +339,15 @@ start_module_sup(Module, Opts) ->
infinity,
supervisor,
[ejabberd_tmp_sup]},
- supervisor:start_child(ejabberd_sup, ChildSpec),
- Proc;
+ case supervisor:start_child(ejabberd_sup, ChildSpec) of
+ {ok, _} -> Proc;
+ _ -> undefined
+ end;
false ->
undefined
end.
--spec start_listener_sup(endpoint(), module(), listen_opts()) ->
+-spec start_listener_sup(endpoint(), module(), opts()) ->
{ok, pid()} | {error, any()}.
start_listener_sup(EndPoint, Module, Opts) ->
ChildSpec = {EndPoint,
@@ -352,19 +360,19 @@ start_listener_sup(EndPoint, Module, Opts) ->
-spec stop_listeners() -> ok.
stop_listeners() ->
- Ports = ejabberd_config:get_option(listen, []),
+ Ports = ejabberd_option:listen(),
lists:foreach(
fun({PortIpNetp, Module, _Opts}) ->
delete_listener(PortIpNetp, Module)
end,
Ports).
--spec stop_listener(endpoint(), module()) -> ok | {error, any()}.
-stop_listener({_, _, Transport} = EndPoint, Module) ->
+-spec stop_listener(endpoint(), module(), opts()) -> ok | {error, any()}.
+stop_listener({_, _, Transport} = EndPoint, Module, Opts) ->
case supervisor:terminate_child(?MODULE, EndPoint) of
ok ->
?INFO_MSG("Stop accepting ~s connections at ~s for ~p",
- [case Transport of udp -> "UDP"; tcp -> "TCP" end,
+ [format_transport(Transport, Opts),
format_endpoint(EndPoint), Module]),
ets:delete(?MODULE, EndPoint),
supervisor:delete_child(?MODULE, EndPoint);
@@ -372,9 +380,10 @@ stop_listener({_, _, Transport} = EndPoint, Module) ->
Err
end.
--spec add_listener(endpoint(), module(), listen_opts()) -> ok | {error, any()}.
+-spec add_listener(endpoint(), module(), opts()) -> ok | {error, any()}.
add_listener(EndPoint, Module, Opts) ->
- case start_listener(EndPoint, Module, Opts) of
+ Opts1 = apply_defaults(Module, Opts),
+ case start_listener(EndPoint, Module, Opts1) of
{ok, _Pid} ->
ok;
{error, {already_started, _Pid}} ->
@@ -385,17 +394,30 @@ add_listener(EndPoint, Module, Opts) ->
-spec delete_listener(endpoint(), module()) -> ok | {error, any()}.
delete_listener(EndPoint, Module) ->
- stop_listener(EndPoint, Module).
+ try ets:lookup_element(?MODULE, EndPoint, 3) of
+ Opts -> stop_listener(EndPoint, Module, Opts)
+ catch _:badarg ->
+ ok
+ end.
+
+-spec tls_listeners() -> [module()].
+tls_listeners() ->
+ lists:usort(
+ lists:filtermap(
+ fun({_, Module, #{tls := true}}) -> {true, Module};
+ ({_, Module, #{starttls := true}}) -> {true, Module};
+ (_) -> false
+ end, ets:tab2list(?MODULE))).
-spec config_reloaded() -> ok.
config_reloaded() ->
- New = ejabberd_config:get_option(listen, []),
+ New = ejabberd_option:listen(),
Old = ets:tab2list(?MODULE),
lists:foreach(
- fun({EndPoint, Module, _Opts}) ->
+ fun({EndPoint, Module, Opts}) ->
case lists:keyfind(EndPoint, 1, New) of
false ->
- stop_listener(EndPoint, Module);
+ stop_listener(EndPoint, Module, Opts);
_ ->
ok
end
@@ -405,8 +427,8 @@ config_reloaded() ->
case lists:keyfind(EndPoint, 1, Old) of
{_, Module, Opts} ->
ok;
- {_, OldModule, _} ->
- stop_listener(EndPoint, OldModule),
+ {_, OldModule, OldOpts} ->
+ _ = stop_listener(EndPoint, OldModule, OldOpts),
ets:insert(?MODULE, {EndPoint, Module, Opts}),
start_listener(EndPoint, Module, Opts);
false ->
@@ -415,22 +437,12 @@ config_reloaded() ->
end
end, New).
--spec get_certfiles() -> [binary()].
-get_certfiles() ->
- lists:filtermap(
- fun({_, _, Opts}) ->
- case proplists:get_value(certfile, Opts) of
- undefined -> false;
- Cert -> {true, Cert}
- end
- end, ets:tab2list(?MODULE)).
-
-spec report_socket_error(inet:posix(), endpoint(), module()) -> ok.
report_socket_error(Reason, EndPoint, Module) ->
?ERROR_MSG("Failed to open socket at ~s for ~s: ~s",
[format_endpoint(EndPoint), Module, format_error(Reason)]).
--spec format_error(inet:posix()) -> string().
+-spec format_error(inet:posix() | atom()) -> string().
format_error(Reason) ->
case inet:format_error(Reason) of
"unknown POSIX error" ->
@@ -447,8 +459,17 @@ format_endpoint({Port, IP, _Transport}) ->
end,
IPStr ++ ":" ++ integer_to_list(Port).
--spec check_rate_limit(non_neg_integer()) -> non_neg_integer().
-check_rate_limit(Interval) ->
+-spec format_transport(transport(), opts()) -> string().
+format_transport(Transport, Opts) ->
+ case maps:get(tls, Opts, false) of
+ true when Transport == tcp -> "TLS";
+ true when Transport == udp -> "DTLS";
+ false when Transport == tcp -> "TCP";
+ false when Transport == udp -> "UDP"
+ end.
+
+-spec apply_rate_limit(non_neg_integer()) -> non_neg_integer().
+apply_rate_limit(Interval) ->
NewInterval = receive
{rate_limit, AcceptInterval} ->
AcceptInterval
@@ -473,318 +494,171 @@ check_rate_limit(Interval) ->
end,
NewInterval.
-transform_option({{Port, IP, Transport}, Mod, Opts}) ->
- IPStr = if is_tuple(IP) ->
- list_to_binary(inet_parse:ntoa(IP));
- true ->
- IP
- end,
- Opts1 = lists:map(
- fun({ip, IPT}) when is_tuple(IPT) ->
- {ip, list_to_binary(inet_parse:ntoa(IP))};
- (ssl) -> {tls, true};
- (A) when is_atom(A) -> {A, true};
- (Opt) -> Opt
- end, Opts),
- Opts2 = lists:foldl(
- fun(Opt, Acc) ->
- try
- Mod:transform_listen_option(Opt, Acc)
- catch error:undef ->
- [Opt|Acc]
- end
- end, [], Opts1),
- TransportOpt = if Transport == tcp -> [];
- true -> [{transport, Transport}]
- end,
- IPOpt = if IPStr == <<"0.0.0.0">> -> [];
- true -> [{ip, IPStr}]
- end,
- IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
-transform_option({{Port, Transport}, Mod, Opts})
- when Transport == tcp orelse Transport == udp ->
- transform_option({{Port, all_zero_ip(Opts), Transport}, Mod, Opts});
-transform_option({{Port, IP}, Mod, Opts}) ->
- transform_option({{Port, IP, tcp}, Mod, Opts});
-transform_option({Port, Mod, Opts}) ->
- transform_option({{Port, all_zero_ip(Opts), tcp}, Mod, Opts});
-transform_option(Opt) ->
- Opt.
-
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({listen, LOpts}, Opts) ->
- [{listen, lists:map(fun transform_option/1, LOpts)} | Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
--spec validate_cfg(list()) -> [listener()].
-validate_cfg(Listeners) ->
- Listeners1 = lists:map(fun validate_opts/1, Listeners),
- Listeners2 = lists:keysort(1, Listeners1),
- check_overlapping_listeners(Listeners2).
-
--spec validate_module(module()) -> ok.
-validate_module(Mod) ->
- case code:ensure_loaded(Mod) of
- {module, Mod} ->
- lists:foreach(
- fun({Fun, Arities}) ->
- case lists:any(
- fun(Arity) ->
- erlang:function_exported(Mod, Fun, Arity)
- end, Arities) of
- true -> ok;
- false ->
- ?ERROR_MSG("Failed to load listening module ~s, "
- "because it doesn't export ~s/~B callback. "
- "The module is either not a listening module "
- "or it is a third-party module which "
- "requires update",
- [Mod, Fun, hd(Arities)]),
- erlang:error(badarg)
- end
- end, [{start, [3,2]}, {start_link, [3,2]},
- {accept, [1]}, {listen_options, [0]}]);
- _ ->
- ?ERROR_MSG("Failed to load unknown listening module ~s: "
- "make sure there is no typo and ~s.beam "
- "exists inside either ~s or ~s directory",
- [Mod, Mod,
- filename:dirname(code:which(?MODULE)),
- ext_mod:modules_dir()]),
- erlang:error(badarg)
- end.
-
--spec validate_opts(listen_opts()) -> listener().
-validate_opts(Opts) ->
- case lists:keyfind(module, 1, Opts) of
- {_, Mod} ->
- validate_module(Mod),
- Opts1 = validate_opts(Mod, Opts),
- {Opts2, Opts3} = lists:partition(
- fun({port, _}) -> true;
- ({transport, _}) -> true;
- ({module, _}) -> true;
- (_) -> false
- end, Opts1),
- Port = proplists:get_value(port, Opts2),
- Transport = proplists:get_value(transport, Opts2, tcp),
- IP = proplists:get_value(ip, Opts3, all_zero_ip(Opts3)),
- {{Port, IP, Transport}, Mod, Opts3};
- false ->
- ?ERROR_MSG("Missing required listening option: module", []),
- erlang:error(badarg)
- end.
+-spec validator() -> econf:validator().
+validator() ->
+ econf:and_then(
+ econf:list(
+ econf:and_then(
+ econf:options(
+ #{module => listen_opt_type(module),
+ transport => listen_opt_type(transport),
+ '_' => econf:any()},
+ [{required, [module]}]),
+ fun(Opts) ->
+ M = proplists:get_value(module, Opts),
+ T = proplists:get_value(transport, Opts, tcp),
+ (validator(M, T))(Opts)
+ end)),
+ fun prepare_opts/1).
+
+-spec validator(module(), transport()) -> econf:validator().
+validator(M, T) ->
+ Options = listen_options() ++ M:listen_options(),
+ Required = lists:usort([Opt || Opt <- Options, is_atom(Opt)]),
+ Disallowed = if T == udp ->
+ [backlog, use_proxy_protocol, accept_interval];
+ true ->
+ []
+ end,
+ Validator = maps:from_list(
+ lists:map(
+ fun(Opt) ->
+ try {Opt, M:listen_opt_type(Opt)}
+ catch _:_ when M /= ?MODULE ->
+ {Opt, listen_opt_type(Opt)}
+ end
+ end, proplists:get_keys(Options))),
+ econf:options(
+ Validator,
+ [{required, Required}, {disallowed, Disallowed},
+ {return, map}, unique]).
+
+-spec prepare_opts([opts()]) -> [listener()].
+prepare_opts(Listeners) ->
+ check_overlapping_listeners(
+ lists:map(
+ fun(Opts1) ->
+ {Opts2, Opts3} = partition(
+ fun({port, _}) -> true;
+ ({transport, _}) -> true;
+ ({module, _}) -> true;
+ (_) -> false
+ end, Opts1),
+ Mod = maps:get(module, Opts2),
+ Port = maps:get(port, Opts2),
+ Transport = maps:get(transport, Opts2, tcp),
+ IP = maps:get(ip, Opts3, {0,0,0,0}),
+ Opts4 = apply_defaults(Mod, Opts3),
+ {{Port, IP, Transport}, Mod, Opts4}
+ end, Listeners)).
--spec validate_opts(module(), listen_opts()) -> listen_opts().
-validate_opts(Mod, Opts) ->
- Defaults = listen_options() ++ Mod:listen_options(),
- {Opts1, Defaults1} =
- lists:mapfoldl(
- fun({Opt, Val} = OptVal, Defs) ->
- case proplists:is_defined(Opt, Defaults) of
+-spec check_overlapping_listeners([listener()]) -> [listener()].
+check_overlapping_listeners(Listeners) ->
+ _ = lists:foldl(
+ fun({{Port, IP, Transport} = Key, _, _}, Acc) ->
+ case lists:member(Key, Acc) of
true ->
- NewOptVal = case lists:member(OptVal, Defaults) of
- true -> [];
- false -> [validate_module_opt(Mod, Opt, Val)]
- end,
- {NewOptVal, proplists:delete(Opt, Defs)};
+ econf:fail({listener_dup, {IP, Port}});
false ->
- ?ERROR_MSG("Unknown listening option '~s' of "
- "module ~s; available options are: ~s",
- [Opt, Mod,
- misc:join_atoms(
- proplists:get_keys(Defaults),
- <<", ">>)]),
- erlang:error(badarg)
+ ZeroIP = case size(IP) of
+ 8 -> {0,0,0,0,0,0,0,0};
+ 4 -> {0,0,0,0}
+ end,
+ Key1 = {Port, ZeroIP, Transport},
+ case lists:member(Key1, Acc) of
+ true ->
+ econf:fail({listener_conflict,
+ {IP, Port}, {ZeroIP, Port}});
+ false ->
+ [Key|Acc]
+ end
end
- end, Defaults, Opts),
- case lists:filter(fun is_atom/1, Defaults1) of
- [] ->
- lists:flatten(Opts1);
- MissingRequiredOpts ->
- ?ERROR_MSG("Missing required listening option(s): ~s",
- [misc:join_atoms(MissingRequiredOpts, <<", ">>)]),
- erlang:error(badarg)
- end.
-
--spec validate_module_opt(module(), atom(), any()) -> {atom(), any()}.
-validate_module_opt(Module, Opt, Val) ->
- VFun = try Module:listen_opt_type(Opt)
- catch _:_ -> listen_opt_type(Opt)
- end,
- try {Opt, VFun(Val)}
- catch _:R when R /= undef ->
- ?ERROR_MSG("Invalid value of listening option ~s: ~s",
- [Opt, misc:format_val({yaml, Val})]),
- erlang:error(badarg)
- end.
-
--spec all_zero_ip(listen_opts()) -> inet:ip_address().
-all_zero_ip(Opts) ->
- case proplists:get_bool(inet6, Opts) of
- true -> {0,0,0,0,0,0,0,0};
- false -> {0,0,0,0}
- end.
+ end, [], Listeners),
+ Listeners.
--spec check_overlapping_listeners([listener()]) -> [listener()].
-check_overlapping_listeners(Listeners) ->
+-spec apply_defaults(module(), opts()) -> opts().
+apply_defaults(Mod, Opts) ->
lists:foldl(
- fun({{Port, IP, Transport} = Key, _, _}, Acc) ->
- case lists:member(Key, Acc) of
- true ->
- ?ERROR_MSG("Overlapping listeners found at ~s",
- [format_endpoint(Key)]),
- erlang:error(badarg);
- false ->
- ZeroIP = case size(IP) of
- 8 -> {0,0,0,0,0,0,0,0};
- 4 -> {0,0,0,0}
- end,
- Key1 = {Port, ZeroIP, Transport},
- case lists:member(Key1, Acc) of
- true ->
- ?ERROR_MSG(
- "Overlapping listeners found at ~s and ~s",
- [format_endpoint(Key), format_endpoint(Key1)]),
- erlang:error(badarg);
- false ->
- [Key|Acc]
- end
+ fun({Opt, Default}, M) ->
+ case maps:is_key(Opt, M) of
+ true -> M;
+ false -> M#{Opt => Default}
+ end;
+ (_, M) ->
+ M
+ end, Opts, Mod:listen_options() ++ listen_options()).
+
+%% Convert options to list with removing defaults
+-spec opts_to_list(module(), opts()) -> list_opts().
+opts_to_list(Mod, Opts) ->
+ Defaults = Mod:listen_options() ++ listen_options(),
+ maps:fold(
+ fun(Opt, Val, Acc) ->
+ case proplists:get_value(Opt, Defaults) of
+ Val -> Acc;
+ _ -> [{Opt, Val}|Acc]
end
- end, [], Listeners),
- Listeners.
+ end, [], Opts).
+
+-spec partition(fun(({atom(), term()}) -> boolean()), opts()) -> {opts(), opts()}.
+partition(Fun, Opts) ->
+ maps:fold(
+ fun(Opt, Val, {True, False}) ->
+ case Fun({Opt, Val}) of
+ true -> {True#{Opt => Val}, False};
+ false -> {True, False#{Opt => Val}}
+ end
+ end, {#{}, #{}}, Opts).
+-spec listen_opt_type(atom()) -> econf:validator().
listen_opt_type(port) ->
- fun(I) when is_integer(I), I>0, I<65536 -> I end;
+ econf:int(0, 65535);
listen_opt_type(module) ->
- fun(A) when is_atom(A) -> A end;
+ econf:beam([[{start, 3}, {start, 2}],
+ [{start_link, 3}, {start_link, 2}],
+ {accept, 1}, {listen_options, 0}]);
listen_opt_type(ip) ->
- fun(S) ->
- {ok, Addr} = inet_parse:address(binary_to_list(S)),
- Addr
- end;
+ econf:ip();
listen_opt_type(transport) ->
- fun(tcp) -> tcp;
- (udp) -> udp
- end;
+ econf:enum([tcp, udp]);
listen_opt_type(accept_interval) ->
- fun(I) when is_integer(I), I>=0 -> I end;
+ econf:non_neg_int();
listen_opt_type(backlog) ->
- fun(I) when is_integer(I), I>=0 -> I end;
-listen_opt_type(inet) ->
- fun(B) when is_boolean(B) -> B end;
-listen_opt_type(inet6) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:non_neg_int();
listen_opt_type(supervisor) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
+listen_opt_type(ciphers) ->
+ econf:binary();
+listen_opt_type(dhfile) ->
+ econf:file();
+listen_opt_type(cafile) ->
+ econf:pem();
listen_opt_type(certfile) ->
- fun(S) ->
- {ok, File} = ejabberd_pkix:add_certfile(S),
- File
- end;
-listen_opt_type(ciphers) -> fun iolist_to_binary/1;
-listen_opt_type(dhfile) -> fun misc:try_read_file/1;
-listen_opt_type(cafile) -> fun ejabberd_pkix:try_certfile/1;
+ econf:pem();
listen_opt_type(protocol_options) ->
- fun (Options) -> str:join(Options, <<"|">>) end;
+ econf:and_then(
+ econf:list(econf:binary()),
+ fun(Options) -> str:join(Options, <<"|">>) end);
listen_opt_type(tls_compression) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
listen_opt_type(tls) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
listen_opt_type(max_stanza_size) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
listen_opt_type(max_fsm_queue) ->
- fun(I) when is_integer(I), I>0 -> I end;
+ econf:pos_int();
listen_opt_type(shaper) ->
- fun acl:shaper_rules_validator/1;
+ econf:shaper();
listen_opt_type(access) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
listen_opt_type(use_proxy_protocol) ->
- fun(B) when is_boolean(B) -> B end.
+ econf:bool().
listen_options() ->
[module, port,
{transport, tcp},
- {ip, <<"0.0.0.0">>},
- {inet, true},
- {inet6, false},
+ {ip, {0,0,0,0}},
{accept_interval, 0},
{backlog, 5},
{use_proxy_protocol, false},
{supervisor, true}].
-
-opt_type(listen) -> fun validate_cfg/1;
-opt_type(_) -> [listen].
-
-%%%----------------------------------------------------------------------
-%%% Some legacy code used by ejabberd_web_admin only
-%%%----------------------------------------------------------------------
-parse_listener_portip(PortIP, Opts) ->
- {IPOpt, Opts2} = strip_ip_option(Opts),
- {IPVOpt, OptsClean} = case proplists:get_bool(inet6, Opts2) of
- true -> {inet6, proplists:delete(inet6, Opts2)};
- false -> {inet, Opts2}
- end,
- {Port, IPT, Proto} =
- case add_proto(PortIP, Opts) of
- {P, Prot} ->
- T = get_ip_tuple(IPOpt, IPVOpt),
- {P, T, Prot};
- {P, T, Prot} when is_integer(P) and is_tuple(T) ->
- {P, T, Prot};
- {P, S, Prot} when is_integer(P) and is_binary(S) ->
- {ok, T} = inet_parse:address(binary_to_list(S)),
- {P, T, Prot}
- end,
- IPV = case tuple_size(IPT) of
- 4 -> inet;
- 8 -> inet6
- end,
- {Port, IPT, IPV, Proto, OptsClean}.
-
-add_proto(Port, Opts) when is_integer(Port) ->
- {Port, get_proto(Opts)};
-add_proto({Port, Proto}, _Opts) when is_atom(Proto) ->
- {Port, normalize_proto(Proto)};
-add_proto({Port, Addr}, Opts) ->
- {Port, Addr, get_proto(Opts)};
-add_proto({Port, Addr, Proto}, _Opts) ->
- {Port, Addr, normalize_proto(Proto)}.
-
-strip_ip_option(Opts) ->
- {IPL, OptsNoIP} = lists:partition(
- fun({ip, _}) -> true;
- (_) -> false
- end,
- Opts),
- case IPL of
- %% Only the first ip option is considered
- [{ip, T1} | _] ->
- {T1, OptsNoIP};
- [] ->
- {no_ip_option, OptsNoIP}
- end.
-
-get_ip_tuple(no_ip_option, inet) ->
- {0, 0, 0, 0};
-get_ip_tuple(no_ip_option, inet6) ->
- {0, 0, 0, 0, 0, 0, 0, 0};
-get_ip_tuple(IPOpt, _IPVOpt) ->
- IPOpt.
-
-get_proto(Opts) ->
- case proplists:get_value(proto, Opts) of
- undefined ->
- tcp;
- Proto ->
- normalize_proto(Proto)
- end.
-
-normalize_proto(udp) -> udp;
-normalize_proto(_) -> tcp.
diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl
index 1384a2359..3b7c0f4f2 100644
--- a/src/ejabberd_local.erl
+++ b/src/ejabberd_local.erl
@@ -106,7 +106,7 @@ get_features(Host) ->
init([]) ->
process_flag(trap_exit, true),
- lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
+ lists:foreach(fun host_up/1, ejabberd_option:hosts()),
ejabberd_hooks:add(host_up, ?MODULE, host_up, 10),
ejabberd_hooks:add(host_down, ?MODULE, host_down, 100),
gen_iq_handler:start(?MODULE),
@@ -126,7 +126,7 @@ handle_info(Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
- lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
+ lists:foreach(fun host_down/1, ejabberd_option:hosts()),
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 10),
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 100),
ok.
diff --git a/src/ejabberd_logger.erl b/src/ejabberd_logger.erl
index e35a769e5..529bc75f2 100644
--- a/src/ejabberd_logger.erl
+++ b/src/ejabberd_logger.erl
@@ -24,21 +24,21 @@
%%%-------------------------------------------------------------------
-module(ejabberd_logger).
--behaviour(ejabberd_config).
-
%% API
-export([start/0, restart/0, reopen_log/0, rotate_log/0, get/0, set/1,
- get_log_path/0, opt_type/1]).
+ get_log_path/0]).
-type loglevel() :: 0 | 1 | 2 | 3 | 4 | 5.
+-type lager_level() :: none | emergency | alert | critical |
+ error | warning | notice | info | debug.
-spec start() -> ok.
-spec get_log_path() -> string().
-spec reopen_log() -> ok.
-spec rotate_log() -> ok.
-spec get() -> {loglevel(), atom(), string()}.
--spec set(loglevel() | {loglevel(), list()}) -> {module, module()}.
+-spec set(loglevel()) -> ok.
%%%===================================================================
%%% API
@@ -64,17 +64,6 @@ get_log_path() ->
end
end.
-opt_type(log_rotate_date) ->
- fun(S) -> binary_to_list(iolist_to_binary(S)) end;
-opt_type(log_rotate_size) ->
- fun(I) when is_integer(I), I >= 0 -> I end;
-opt_type(log_rotate_count) ->
- fun(I) when is_integer(I), I >= 0 -> I end;
-opt_type(log_rate_limit) ->
- fun(I) when is_integer(I), I >= 0 -> I end;
-opt_type(_) ->
- [log_rotate_date, log_rotate_size, log_rotate_count, log_rate_limit].
-
get_integer_env(Name, Default) ->
case application:get_env(ejabberd, Name) of
{ok, I} when is_integer(I), I>=0 ->
@@ -130,7 +119,7 @@ do_start_for_logger(Level) ->
ejabberd:start_app(lager),
ok.
-%% Start lager
+-spec do_start(atom()) -> ok.
do_start(Level) ->
application:load(sasl),
application:set_env(sasl, sasl_error_logger, false),
@@ -162,11 +151,10 @@ do_start(Level) ->
ejabberd:start_app(lager),
lists:foreach(fun(Handler) ->
lager:set_loghwm(Handler, LogRateLimit)
- end, gen_event:which_handlers(lager_event)),
- ok.
+ end, gen_event:which_handlers(lager_event)).
restart() ->
- Level = ejabberd_config:get_option(loglevel, 4),
+ Level = ejabberd_option:loglevel(),
application:stop(lager),
start(Level).
@@ -199,7 +187,6 @@ get() ->
debug -> {5, debug, "Debug"}
end.
-%% @spec (loglevel() | {loglevel(), list()}) -> {module, module()}
set(LogLevel) when is_integer(LogLevel) ->
LagerLogLevel = get_lager_loglevel(LogLevel),
case get_lager_loglevel() of
@@ -216,16 +203,12 @@ set(LogLevel) when is_integer(LogLevel) ->
lager:set_loglevel(H, LagerLogLevel);
(_) ->
ok
- end, gen_event:which_handlers(lager_event))
+ end, get_lager_handlers())
end,
case LogLevel of
5 -> xmpp:set_config([{debug, true}]);
_ -> xmpp:set_config([{debug, false}])
- end,
- {module, lager};
-set({_LogLevel, _}) ->
- error_logger:error_msg("custom loglevels are not supported for 'lager'"),
- {module, lager}.
+ end.
get_lager_loglevel() ->
Handlers = get_lager_handlers(),
@@ -238,6 +221,7 @@ get_lager_loglevel() ->
end,
none, Handlers).
+-spec get_lager_loglevel(loglevel()) -> lager_level().
get_lager_loglevel(LogLevel) ->
case LogLevel of
0 -> none;
@@ -245,8 +229,7 @@ get_lager_loglevel(LogLevel) ->
2 -> error;
3 -> warning;
4 -> info;
- 5 -> debug;
- E -> erlang:error({wrong_loglevel, E})
+ 5 -> debug
end.
get_lager_handlers() ->
@@ -257,6 +240,7 @@ get_lager_handlers() ->
Result
end.
+-spec get_lager_version() -> string().
get_lager_version() ->
Apps = application:loaded_applications(),
case lists:keyfind(lager, 1, Apps) of
diff --git a/src/ejabberd_oauth.erl b/src/ejabberd_oauth.erl
index 2913c8ef9..3e1a0cf1c 100644
--- a/src/ejabberd_oauth.erl
+++ b/src/ejabberd_oauth.erl
@@ -27,7 +27,6 @@
-module(ejabberd_oauth).
-behaviour(gen_server).
--behaviour(ejabberd_config).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
@@ -38,7 +37,6 @@
verify_redirection_uri/3,
authenticate_user/2,
authenticate_client/2,
- verify_resowner_scope/3,
associate_access_code/3,
associate_access_token/3,
associate_refresh_token/3,
@@ -47,8 +45,7 @@
check_token/2,
scope_in_scope_list/2,
process/2,
- config_reloaded/0,
- opt_type/1]).
+ config_reloaded/0]).
-export([get_commands_spec/0,
oauth_issue_token/3, oauth_list_tokens/0, oauth_revoke_token/1]).
@@ -73,8 +70,6 @@
%% * Using the command line and oauth_issue_token command, the token is generated in behalf of ejabberd' sysadmin
%% (as it has access to ejabberd command line).
--define(EXPIRE, 4294967).
-
get_commands_spec() ->
[
#ejabberd_commands{name = oauth_issue_token, tags = [oauth],
@@ -189,9 +184,7 @@ authenticate_user({User, Server}, Ctx) ->
case jid:make(User, Server) of
#jid{} = JID ->
Access =
- ejabberd_config:get_option(
- {oauth_access, JID#jid.lserver},
- none),
+ ejabberd_option:oauth_access(JID#jid.lserver),
case acl:match_rule(JID#jid.lserver, Access, JID) of
allow ->
case Ctx of
@@ -214,21 +207,6 @@ authenticate_user({User, Server}, Ctx) ->
authenticate_client(Client, Ctx) -> {ok, {Ctx, {client, Client}}}.
-verify_resowner_scope({user, _User, _Server}, Scope, Ctx) ->
- Cmds = ejabberd_commands:get_exposed_commands(),
- Cmds1 = ['ejabberd:user', 'ejabberd:admin', sasl_auth | Cmds],
- RegisteredScope = [atom_to_binary(C, utf8) || C <- Cmds1],
- case oauth2_priv_set:is_subset(oauth2_priv_set:new(Scope),
- oauth2_priv_set:new(RegisteredScope)) of
- true ->
- {ok, {Ctx, Scope}};
- false ->
- {error, badscope}
- end;
-verify_resowner_scope(_, _, _) ->
- {error, badscope}.
-
-
%% This is callback for oauth tokens generated through the command line. Only open and admin commands are
%% made available.
%verify_client_scope({client, ejabberd_ctl}, Scope, Ctx) ->
@@ -286,6 +264,8 @@ scope_in_scope_list(Scope, ScopeList) ->
oauth2_priv_set:is_member(Scope2, TokenScopeSet) end,
ScopeList).
+-spec check_token(binary()) -> {ok, {binary(), binary()}, [binary()]} |
+ {false, expired | not_found}.
check_token(Token) ->
case lookup(Token) of
{ok, #oauth_token{us = US,
@@ -380,29 +360,20 @@ init_cache(DBMod) ->
use_cache(DBMod) ->
case erlang:function_exported(DBMod, use_cache, 0) of
true -> DBMod:use_cache();
- false ->
- ejabberd_config:get_option(
- oauth_use_cache,
- ejabberd_config:use_cache(global))
+ false -> ejabberd_option:oauth_use_cache()
end.
cache_opts() ->
- MaxSize = ejabberd_config:get_option(
- oauth_cache_size,
- ejabberd_config:cache_size(global)),
- CacheMissed = ejabberd_config:get_option(
- oauth_cache_missed,
- ejabberd_config:cache_missed(global)),
- LifeTime = case ejabberd_config:get_option(
- oauth_cache_life_time,
- ejabberd_config:cache_life_time(global)) of
+ MaxSize = ejabberd_option:oauth_cache_size(),
+ CacheMissed = ejabberd_option:oauth_cache_missed(),
+ LifeTime = case ejabberd_option:oauth_cache_life_time() of
infinity -> infinity;
I -> timer:seconds(I)
end,
[{max_size, MaxSize}, {life_time, LifeTime}, {cache_missed, CacheMissed}].
expire() ->
- ejabberd_config:get_option(oauth_expire, ?EXPIRE).
+ ejabberd_option:oauth_expire().
-define(DIV(Class, Els),
?XAE(<<"div">>, [{<<"class">>, Class}], Els)).
@@ -596,9 +567,7 @@ process(_Handlers, _Request) ->
-spec get_db_backend() -> module().
get_db_backend() ->
- DBType = ejabberd_config:get_option(
- oauth_db_type,
- ejabberd_config:default_db(?MODULE)),
+ DBType = ejabberd_option:oauth_db_type(),
list_to_atom("ejabberd_oauth_" ++ atom_to_list(DBType)).
@@ -645,21 +614,3 @@ logo() ->
{error, _} ->
<<>>
end.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(oauth_expire) ->
- fun(I) when is_integer(I), I >= 0 -> I end;
-opt_type(oauth_access) ->
- fun acl:access_rules_validator/1;
-opt_type(oauth_db_type) ->
- fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-opt_type(O) when O == oauth_cache_life_time; O == oauth_cache_size ->
- fun (I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
-opt_type(O) when O == oauth_use_cache; O == oauth_cache_missed ->
- fun (B) when is_boolean(B) -> B end;
-opt_type(_) ->
- [oauth_expire, oauth_access, oauth_db_type,
- oauth_cache_life_time, oauth_cache_size, oauth_use_cache,
- oauth_cache_missed].
diff --git a/src/ejabberd_oauth_mnesia.erl b/src/ejabberd_oauth_mnesia.erl
index 1c55877eb..dcc70505c 100644
--- a/src/ejabberd_oauth_mnesia.erl
+++ b/src/ejabberd_oauth_mnesia.erl
@@ -45,9 +45,7 @@ init() ->
use_cache() ->
case mnesia:table_info(oauth_token, storage_type) of
disc_only_copies ->
- ejabberd_config:get_option(
- oauth_use_cache,
- ejabberd_config:use_cache(global));
+ ejabberd_option:oauth_use_cache();
_ ->
false
end.
@@ -73,4 +71,3 @@ clean(TS) ->
lists:foreach(fun mnesia:delete_object/1, Ts)
end,
mnesia:async_dirty(F).
-
diff --git a/src/ejabberd_oauth_rest.erl b/src/ejabberd_oauth_rest.erl
index 215766d00..793e928b3 100644
--- a/src/ejabberd_oauth_rest.erl
+++ b/src/ejabberd_oauth_rest.erl
@@ -26,13 +26,11 @@
-module(ejabberd_oauth_rest).
-behaviour(ejabberd_oauth).
--behaviour(ejabberd_config).
-export([init/0,
store/1,
lookup/1,
- clean/1,
- opt_type/1]).
+ clean/1]).
-include("ejabberd_oauth.hrl").
-include("logger.hrl").
@@ -88,11 +86,5 @@ clean(_TS) ->
ok.
path(Path) ->
- Base = ejabberd_config:get_option(ext_api_path_oauth, <<"/oauth">>),
+ Base = ejabberd_option:ext_api_path_oauth(),
<<Base/binary, "/", Path/binary>>.
-
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(ext_api_path_oauth) ->
- fun (X) -> iolist_to_binary(X) end;
-opt_type(_) -> [ext_api_path_oauth].
diff --git a/src/ejabberd_oauth_sql.erl b/src/ejabberd_oauth_sql.erl
index e86e5be9f..8ce2bc574 100644
--- a/src/ejabberd_oauth_sql.erl
+++ b/src/ejabberd_oauth_sql.erl
@@ -26,7 +26,6 @@
-module(ejabberd_oauth_sql).
-behaviour(ejabberd_oauth).
--compile([{parse_transform, ejabberd_sql_pt}]).
-export([init/0,
store/1,
diff --git a/src/ejabberd_old_config.erl b/src/ejabberd_old_config.erl
new file mode 100644
index 000000000..13a006055
--- /dev/null
+++ b/src/ejabberd_old_config.erl
@@ -0,0 +1,655 @@
+%%%----------------------------------------------------------------------
+%%% Purpose: Transform old-style Erlang config to YAML config
+%%%
+%%% 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(ejabberd_old_config).
+
+%% API
+-export([read_file/1]).
+
+-include("logger.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+read_file(File) ->
+ case consult(File) of
+ {ok, Terms1} ->
+ ?INFO_MSG("Converting from old configuration format", []),
+ Terms2 = strings_to_binary(Terms1),
+ Terms3 = transform(Terms2),
+ Terms4 = transform_certfiles(Terms3),
+ Terms5 = transform_host_config(Terms4),
+ {ok, collect_options(Terms5)};
+ {error, Reason} ->
+ {error, {old_config, File, Reason}}
+ end.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+collect_options(Opts) ->
+ {D, InvalidOpts} =
+ lists:foldl(
+ fun({K, V}, {D, Os}) when is_list(V) ->
+ {orddict:append_list(K, V, D), Os};
+ ({K, V}, {D, Os}) ->
+ {orddict:store(K, V, D), Os};
+ (Opt, {D, Os}) ->
+ {D, [Opt|Os]}
+ end, {orddict:new(), []}, Opts),
+ InvalidOpts ++ orddict:to_list(D).
+
+transform(Opts) ->
+ Opts1 = transform_register(Opts),
+ Opts2 = transform_s2s(Opts1),
+ Opts3 = transform_listeners(Opts2),
+ Opts4 = transform_sql(Opts3),
+ Opts5 = transform_riak(Opts4),
+ Opts6 = transform_shaper(Opts5),
+ Opts7 = transform_s2s_out(Opts6),
+ Opts8 = transform_acl(Opts7),
+ Opts9 = transform_modules(Opts8),
+ Opts10 = transform_globals(Opts9),
+ collect_options(Opts10).
+
+%%%===================================================================
+%%% mod_register
+%%%===================================================================
+transform_register(Opts) ->
+ try
+ {value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts),
+ {value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts),
+ {value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts),
+ true = is_list(L),
+ ?WARNING_MSG("Old 'ip_access' format detected. "
+ "The old format is still supported "
+ "but it is better to fix your config: "
+ "use access rules instead.", []),
+ ACLs = lists:flatmap(
+ fun({Action, S}) ->
+ ACLName = misc:binary_to_atom(
+ iolist_to_binary(
+ ["ip_", S])),
+ [{Action, ACLName},
+ {acl, ACLName, {ip, S}}]
+ end, L),
+ Access = {access, mod_register_networks,
+ [{Action, ACLName} || {Action, ACLName} <- ACLs]},
+ [ACL || {acl, _, _} = ACL <- ACLs] ++
+ [Access,
+ {modules,
+ [{mod_register,
+ [{ip_access, mod_register_networks}|RegOpts1]}
+ | ModOpts1]}|Opts1]
+ catch error:{badmatch, false} ->
+ Opts
+ end.
+
+%%%===================================================================
+%%% ejabberd_s2s
+%%%===================================================================
+transform_s2s(Opts) ->
+ lists:foldl(fun transform_s2s/2, [], Opts).
+
+transform_s2s({{s2s_host, Host}, Action}, Opts) ->
+ ?WARNING_MSG("Option 's2s_host' is deprecated.", []),
+ ACLName = misc:binary_to_atom(
+ iolist_to_binary(["s2s_access_", Host])),
+ [{acl, ACLName, {server, Host}},
+ {access, s2s, [{Action, ACLName}]},
+ {s2s_access, s2s} |
+ Opts];
+transform_s2s({s2s_default_policy, Action}, Opts) ->
+ ?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
+ "The option is still supported but it is better to "
+ "fix your config: "
+ "use 's2s_access' with an access rule.", []),
+ [{access, s2s, [{Action, all}]},
+ {s2s_access, s2s} |
+ Opts];
+transform_s2s(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% ejabberd_s2s_out
+%%%===================================================================
+transform_s2s_out(Opts) ->
+ lists:foldl(fun transform_s2s_out/2, [], Opts).
+
+transform_s2s_out({outgoing_s2s_options, Families, Timeout}, Opts) ->
+ ?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
+ "The option is still supported "
+ "but it is better to fix your config: "
+ "use 'outgoing_s2s_timeout' and "
+ "'outgoing_s2s_families' instead.", []),
+ [{outgoing_s2s_families, Families},
+ {outgoing_s2s_timeout, Timeout}
+ | Opts];
+transform_s2s_out({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
+ ?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
+ "The option is still supported "
+ "but it is better to fix your config: "
+ "use 's2s_dns_timeout' and "
+ "'s2s_dns_retries' instead", []),
+ lists:foldr(
+ fun({timeout, T}, AccOpts) ->
+ [{s2s_dns_timeout, T}|AccOpts];
+ ({retries, R}, AccOpts) ->
+ [{s2s_dns_retries, R}|AccOpts];
+ (_, AccOpts) ->
+ AccOpts
+ end, AllOpts, S2SDNSOpts);
+transform_s2s_out(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% ejabberd_listener
+%%%===================================================================
+transform_listeners(Opts) ->
+ lists:foldl(fun transform_listeners/2, [], Opts).
+
+transform_listeners({listen, LOpts}, Opts) ->
+ [{listen, lists:map(fun transform_listener/1, LOpts)} | Opts];
+transform_listeners(Opt, Opts) ->
+ [Opt|Opts].
+
+transform_listener({{Port, IP, Transport}, Mod, Opts}) ->
+ IPStr = if is_tuple(IP) ->
+ list_to_binary(inet_parse:ntoa(IP));
+ true ->
+ IP
+ end,
+ Opts1 = lists:map(
+ fun({ip, IPT}) when is_tuple(IPT) ->
+ {ip, list_to_binary(inet_parse:ntoa(IP))};
+ (ssl) -> {tls, true};
+ (A) when is_atom(A) -> {A, true};
+ (Opt) -> Opt
+ end, Opts),
+ Opts2 = lists:foldl(
+ fun(Opt, Acc) ->
+ transform_listen_option(Mod, Opt, Acc)
+ end, [], Opts1),
+ TransportOpt = if Transport == tcp -> [];
+ true -> [{transport, Transport}]
+ end,
+ IPOpt = if IPStr == <<"0.0.0.0">> -> [];
+ true -> [{ip, IPStr}]
+ end,
+ IPOpt ++ TransportOpt ++ [{port, Port}, {module, Mod} | Opts2];
+transform_listener({{Port, Transport}, Mod, Opts})
+ when Transport == tcp orelse Transport == udp ->
+ transform_listener({{Port, all_zero_ip(Opts), Transport}, Mod, Opts});
+transform_listener({{Port, IP}, Mod, Opts}) ->
+ transform_listener({{Port, IP, tcp}, Mod, Opts});
+transform_listener({Port, Mod, Opts}) ->
+ transform_listener({{Port, all_zero_ip(Opts), tcp}, Mod, Opts});
+transform_listener(Opt) ->
+ Opt.
+
+transform_listen_option(ejabberd_http, captcha, Opts) ->
+ [{captcha, true}|Opts];
+transform_listen_option(ejabberd_http, register, Opts) ->
+ [{register, true}|Opts];
+transform_listen_option(ejabberd_http, web_admin, Opts) ->
+ [{web_admin, true}|Opts];
+transform_listen_option(ejabberd_http, http_bind, Opts) ->
+ [{http_bind, true}|Opts];
+transform_listen_option(ejabberd_http, http_poll, Opts) ->
+ [{http_poll, true}|Opts];
+transform_listen_option(ejabberd_http, {request_handlers, Hs}, Opts) ->
+ Hs1 = lists:map(
+ fun({PList, Mod}) when is_list(PList) ->
+ Path = iolist_to_binary([[$/, P] || P <- PList]),
+ {Path, Mod};
+ (Opt) ->
+ Opt
+ end, Hs),
+ [{request_handlers, Hs1} | Opts];
+transform_listen_option(ejabberd_service, {hosts, Hosts, O}, Opts) ->
+ case lists:keyfind(hosts, 1, Opts) of
+ {_, PrevHostOpts} ->
+ NewHostOpts =
+ lists:foldl(
+ fun(H, Acc) ->
+ dict:append_list(H, O, Acc)
+ end, dict:from_list(PrevHostOpts), Hosts),
+ [{hosts, dict:to_list(NewHostOpts)}|
+ lists:keydelete(hosts, 1, Opts)];
+ _ ->
+ [{hosts, [{H, O} || H <- Hosts]}|Opts]
+ end;
+transform_listen_option(ejabberd_service, {host, Host, Os}, Opts) ->
+ transform_listen_option(ejabberd_service, {hosts, [Host], Os}, Opts);
+transform_listen_option(ejabberd_xmlrpc, {access_commands, ACOpts}, Opts) ->
+ NewACOpts = lists:map(
+ fun({AName, ACmds, AOpts}) ->
+ {AName, [{commands, ACmds}, {options, AOpts}]};
+ (Opt) ->
+ Opt
+ end, ACOpts),
+ [{access_commands, NewACOpts}|Opts];
+transform_listen_option(_, Opt, Opts) ->
+ [Opt|Opts].
+
+-spec all_zero_ip([proplists:property()]) -> inet:ip_address().
+all_zero_ip(Opts) ->
+ case proplists:get_bool(inet6, Opts) of
+ true -> {0,0,0,0,0,0,0,0};
+ false -> {0,0,0,0}
+ end.
+
+%%%===================================================================
+%%% ejabberd_shaper
+%%%===================================================================
+transform_shaper(Opts) ->
+ lists:foldl(fun transform_shaper/2, [], Opts).
+
+transform_shaper({shaper, Name, {maxrate, N}}, Opts) ->
+ [{shaper, [{Name, N}]} | Opts];
+transform_shaper({shaper, Name, none}, Opts) ->
+ [{shaper, [{Name, none}]} | Opts];
+transform_shaper({shaper, List}, Opts) when is_list(List) ->
+ R = lists:map(
+ fun({Name, Args}) when is_list(Args) ->
+ MaxRate = proplists:get_value(rate, Args, 1000),
+ BurstSize = proplists:get_value(burst_size, Args, MaxRate),
+ {Name, MaxRate, BurstSize};
+ ({Name, Val}) ->
+ {Name, Val, Val}
+ end, List),
+ [{shaper, R} | Opts];
+transform_shaper(Opt, Opts) ->
+ [Opt | Opts].
+
+%%%===================================================================
+%%% acl
+%%%===================================================================
+transform_acl(Opts) ->
+ Opts1 = lists:foldl(fun transform_acl/2, [], Opts),
+ {ACLOpts, Opts2} = lists:mapfoldl(
+ fun({acl, Os}, Acc) ->
+ {Os, Acc};
+ (O, Acc) ->
+ {[], [O|Acc]}
+ end, [], Opts1),
+ {AccessOpts, Opts3} = lists:mapfoldl(
+ fun({access, Os}, Acc) ->
+ {Os, Acc};
+ (O, Acc) ->
+ {[], [O|Acc]}
+ end, [], Opts2),
+ {NewAccessOpts, Opts4} = lists:mapfoldl(
+ fun({access_rules, Os}, Acc) ->
+ {Os, Acc};
+ (O, Acc) ->
+ {[], [O|Acc]}
+ end, [], Opts3),
+ {ShaperOpts, Opts5} = lists:mapfoldl(
+ fun({shaper_rules, Os}, Acc) ->
+ {Os, Acc};
+ (O, Acc) ->
+ {[], [O|Acc]}
+ end, [], Opts4),
+ ACLOpts1 = collect_options(lists:flatten(ACLOpts)),
+ AccessOpts1 = case collect_options(lists:flatten(AccessOpts)) of
+ [] -> [];
+ L1 -> [{access, L1}]
+ end,
+ ACLOpts2 = case lists:map(
+ fun({ACLName, Os}) ->
+ {ACLName, collect_options(Os)}
+ end, ACLOpts1) of
+ [] -> [];
+ L2 -> [{acl, L2}]
+ end,
+ NewAccessOpts1 = case lists:map(
+ fun({NAName, Os}) ->
+ {NAName, transform_access_rules_config(Os)}
+ end, lists:flatten(NewAccessOpts)) of
+ [] -> [];
+ L3 -> [{access_rules, L3}]
+ end,
+ ShaperOpts1 = case lists:map(
+ fun({SName, Ss}) ->
+ {SName, transform_access_rules_config(Ss)}
+ end, lists:flatten(ShaperOpts)) of
+ [] -> [];
+ L4 -> [{shaper_rules, L4}]
+ end,
+ ACLOpts2 ++ AccessOpts1 ++ NewAccessOpts1 ++ ShaperOpts1 ++ Opts5.
+
+transform_acl({acl, Name, Type}, Opts) ->
+ T = case Type of
+ all -> all;
+ none -> none;
+ {user, U} -> {user, [b(U)]};
+ {user, U, S} -> {user, [[{b(U), b(S)}]]};
+ {shared_group, G} -> {shared_group, [b(G)]};
+ {shared_group, G, H} -> {shared_group, [[{b(G), b(H)}]]};
+ {user_regexp, UR} -> {user_regexp, [b(UR)]};
+ {user_regexp, UR, S} -> {user_regexp, [[{b(UR), b(S)}]]};
+ {node_regexp, UR, SR} -> {node_regexp, [[{b(UR), b(SR)}]]};
+ {user_glob, UR} -> {user_glob, [b(UR)]};
+ {user_glob, UR, S} -> {user_glob, [[{b(UR), b(S)}]]};
+ {node_glob, UR, SR} -> {node_glob, [[{b(UR), b(SR)}]]};
+ {server, S} -> {server, [b(S)]};
+ {resource, R} -> {resource, [b(R)]};
+ {server_regexp, SR} -> {server_regexp, [b(SR)]};
+ {server_glob, S} -> {server_glob, [b(S)]};
+ {ip, S} -> {ip, [b(S)]};
+ {resource_glob, R} -> {resource_glob, [b(R)]};
+ {resource_regexp, R} -> {resource_regexp, [b(R)]}
+ end,
+ [{acl, [{Name, [T]}]}|Opts];
+transform_acl({access, Name, Rules}, Opts) ->
+ NewRules = [{ACL, Action} || {Action, ACL} <- Rules],
+ [{access, [{Name, NewRules}]}|Opts];
+transform_acl(Opt, Opts) ->
+ [Opt|Opts].
+
+transform_access_rules_config(Config) when is_list(Config) ->
+ lists:map(fun transform_access_rules_config2/1, lists:flatten(Config));
+transform_access_rules_config(Config) ->
+ transform_access_rules_config([Config]).
+
+transform_access_rules_config2(Type) when is_integer(Type); is_atom(Type) ->
+ {Type, [all]};
+transform_access_rules_config2({Type, ACL}) when is_atom(ACL) ->
+ {Type, [{acl, ACL}]};
+transform_access_rules_config2({Res, Rules}) when is_list(Rules) ->
+ T = lists:map(fun({Type, Args}) when is_list(Args) ->
+ {Type, hd(lists:flatten(Args))};
+ (V) ->
+ V
+ end, lists:flatten(Rules)),
+ {Res, T};
+transform_access_rules_config2({Res, Rule}) ->
+ {Res, [Rule]}.
+
+%%%===================================================================
+%%% SQL
+%%%===================================================================
+-define(PGSQL_PORT, 5432).
+-define(MYSQL_PORT, 3306).
+
+transform_sql(Opts) ->
+ lists:foldl(fun transform_sql/2, [], Opts).
+
+transform_sql({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
+ [{sql_type, Type},
+ {sql_server, Server},
+ {sql_port, Port},
+ {sql_database, DB},
+ {sql_username, User},
+ {sql_password, Pass}|Opts];
+transform_sql({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
+ transform_sql({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
+transform_sql({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
+ transform_sql({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
+transform_sql({odbc_server, {sqlite, DB}}, Opts) ->
+ [{sql_type, sqlite},
+ {sql_database, DB}|Opts];
+transform_sql({odbc_pool_size, N}, Opts) ->
+ [{sql_pool_size, N}|Opts];
+transform_sql(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% Riak
+%%%===================================================================
+transform_riak(Opts) ->
+ lists:foldl(fun transform_riak/2, [], Opts).
+
+transform_riak({riak_server, {S, P}}, Opts) ->
+ [{riak_server, S}, {riak_port, P}|Opts];
+transform_riak(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% modules
+%%%===================================================================
+transform_modules(Opts) ->
+ lists:foldl(fun transform_modules/2, [], Opts).
+
+transform_modules({modules, ModOpts}, Opts) ->
+ [{modules, lists:map(
+ fun({Mod, Opts1}) ->
+ {Mod, transform_module(Mod, Opts1)};
+ (Other) ->
+ Other
+ end, ModOpts)}|Opts];
+transform_modules(Opt, Opts) ->
+ [Opt|Opts].
+
+transform_module(mod_disco, Opts) ->
+ lists:map(
+ fun({server_info, Infos}) ->
+ NewInfos = lists:map(
+ fun({Modules, Name, URLs}) ->
+ [[{modules, Modules},
+ {name, Name},
+ {urls, URLs}]];
+ (Opt) ->
+ Opt
+ end, Infos),
+ {server_info, NewInfos};
+ (Opt) ->
+ Opt
+ end, Opts);
+transform_module(mod_muc_log, Opts) ->
+ lists:map(
+ fun({top_link, {S1, S2}}) ->
+ {top_link, [{S1, S2}]};
+ (Opt) ->
+ Opt
+ end, Opts);
+transform_module(mod_proxy65, Opts) ->
+ lists:map(
+ fun({ip, IP}) when is_tuple(IP) ->
+ {ip, misc:ip_to_list(IP)};
+ ({hostname, IP}) when is_tuple(IP) ->
+ {hostname, misc:ip_to_list(IP)};
+ (Opt) ->
+ Opt
+ end, Opts);
+transform_module(mod_register, Opts) ->
+ lists:flatmap(
+ fun({welcome_message, {Subj, Body}}) ->
+ [{welcome_message, [{subject, Subj}, {body, Body}]}];
+ (Opt) ->
+ [Opt]
+ end, Opts);
+transform_module(_Mod, Opts) ->
+ Opts.
+
+%%%===================================================================
+%%% Host config
+%%%===================================================================
+transform_host_config(Opts) ->
+ Opts1 = lists:foldl(fun transform_host_config/2, [], Opts),
+ {HOpts, Opts2} = lists:mapfoldl(
+ fun({host_config, O}, Os) ->
+ {[O], Os};
+ (O, Os) ->
+ {[], [O|Os]}
+ end, [], Opts1),
+ {AHOpts, Opts3} = lists:mapfoldl(
+ fun({append_host_config, O}, Os) ->
+ {[O], Os};
+ (O, Os) ->
+ {[], [O|Os]}
+ end, [], Opts2),
+ HOpts1 = case collect_options(lists:flatten(HOpts)) of
+ [] ->
+ [];
+ HOs ->
+ [{host_config,
+ [{H, transform(O)} || {H, O} <- HOs]}]
+ end,
+ AHOpts1 = case collect_options(lists:flatten(AHOpts)) of
+ [] ->
+ [];
+ AHOs ->
+ [{append_host_config,
+ [{H, transform(O)} || {H, O} <- AHOs]}]
+ end,
+ HOpts1 ++ AHOpts1 ++ Opts3.
+
+transform_host_config({host_config, Host, HOpts}, Opts) ->
+ {AddOpts, HOpts1} =
+ lists:mapfoldl(
+ fun({{add, Opt}, Val}, Os) ->
+ {[{Opt, Val}], Os};
+ (O, Os) ->
+ {[], [O|Os]}
+ end, [], HOpts),
+ [{append_host_config, [{Host, lists:flatten(AddOpts)}]},
+ {host_config, [{Host, HOpts1}]}|Opts];
+transform_host_config(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% Top-level options
+%%%===================================================================
+transform_globals(Opts) ->
+ lists:foldl(fun transform_globals/2, [], Opts).
+
+transform_globals(Opt, Opts) when Opt == override_global;
+ Opt == override_local;
+ Opt == override_acls ->
+ ?WARNING_MSG("Option '~s' has no effect anymore", [Opt]),
+ Opts;
+transform_globals({node_start, _}, Opts) ->
+ ?WARNING_MSG("Option 'node_start' has no effect anymore", []),
+ Opts;
+transform_globals({iqdisc, {queues, N}}, Opts) ->
+ [{iqdisc, N}|Opts];
+transform_globals({define_macro, Macro, Val}, Opts) ->
+ [{define_macro, [{Macro, Val}]}|Opts];
+transform_globals(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% Certfiles
+%%%===================================================================
+transform_certfiles(Opts) ->
+ lists:foldl(fun transform_certfiles/2, [], Opts).
+
+transform_certfiles({domain_certfile, Domain, CertFile}, Opts) ->
+ [{host_config, [{Domain, [{domain_certfile, CertFile}]}]}|Opts];
+transform_certfiles(Opt, Opts) ->
+ [Opt|Opts].
+
+%%%===================================================================
+%%% Consult file
+%%%===================================================================
+consult(File) ->
+ case file:consult(File) of
+ {ok, Terms} ->
+ include_config_files(Terms);
+ Err ->
+ Err
+ end.
+
+include_config_files(Terms) ->
+ include_config_files(Terms, []).
+
+include_config_files([], Res) ->
+ {ok, Res};
+include_config_files([{include_config_file, Filename} | Terms], Res) ->
+ include_config_files([{include_config_file, Filename, []} | Terms], Res);
+include_config_files([{include_config_file, Filename, Options} | Terms], Res) ->
+ case consult(Filename) of
+ {ok, Included_terms} ->
+ Disallow = proplists:get_value(disallow, Options, []),
+ Included_terms2 = delete_disallowed(Disallow, Included_terms),
+ Allow_only = proplists:get_value(allow_only, Options, all),
+ Included_terms3 = keep_only_allowed(Allow_only, Included_terms2),
+ include_config_files(Terms, Res ++ Included_terms3);
+ Err ->
+ Err
+ end;
+include_config_files([Term | Terms], Res) ->
+ include_config_files(Terms, Res ++ [Term]).
+
+delete_disallowed(Disallowed, Terms) ->
+ lists:foldl(
+ fun(Dis, Ldis) ->
+ delete_disallowed2(Dis, Ldis)
+ end,
+ Terms,
+ Disallowed).
+
+delete_disallowed2(Disallowed, [H|T]) ->
+ case element(1, H) of
+ Disallowed ->
+ delete_disallowed2(Disallowed, T);
+ _ ->
+ [H|delete_disallowed2(Disallowed, T)]
+ end;
+delete_disallowed2(_, []) ->
+ [].
+
+keep_only_allowed(all, Terms) ->
+ Terms;
+keep_only_allowed(Allowed, Terms) ->
+ {As, _NAs} = lists:partition(
+ fun(Term) ->
+ lists:member(element(1, Term), Allowed)
+ end, Terms),
+ As.
+
+%%%===================================================================
+%%% Aux functions
+%%%===================================================================
+strings_to_binary([]) ->
+ [];
+strings_to_binary(L) when is_list(L) ->
+ case is_string(L) of
+ true ->
+ list_to_binary(L);
+ false ->
+ strings_to_binary1(L)
+ end;
+strings_to_binary({A, B, C, D}) when
+ is_integer(A), is_integer(B), is_integer(C), is_integer(D) ->
+ {A, B, C ,D};
+strings_to_binary(T) when is_tuple(T) ->
+ list_to_tuple(strings_to_binary1(tuple_to_list(T)));
+strings_to_binary(X) ->
+ X.
+
+strings_to_binary1([El|L]) ->
+ [strings_to_binary(El)|strings_to_binary1(L)];
+strings_to_binary1([]) ->
+ [];
+strings_to_binary1(T) ->
+ T.
+
+is_string([C|T]) when (C >= 0) and (C =< 255) ->
+ is_string(T);
+is_string([]) ->
+ true;
+is_string(_) ->
+ false.
+
+b(S) ->
+ iolist_to_binary(S).
diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl
new file mode 100644
index 000000000..c7a24678c
--- /dev/null
+++ b/src/ejabberd_option.erl
@@ -0,0 +1,1058 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(ejabberd_option).
+
+-export([access_rules/0, access_rules/1]).
+-export([acl/0, acl/1]).
+-export([acme/0]).
+-export([allow_contrib_modules/0]).
+-export([allow_multiple_connections/0, allow_multiple_connections/1]).
+-export([anonymous_protocol/0, anonymous_protocol/1]).
+-export([api_permissions/0]).
+-export([append_host_config/0]).
+-export([auth_cache_life_time/0]).
+-export([auth_cache_missed/0]).
+-export([auth_cache_size/0]).
+-export([auth_method/0, auth_method/1]).
+-export([auth_password_format/0, auth_password_format/1]).
+-export([auth_use_cache/0, auth_use_cache/1]).
+-export([c2s_cafile/0, c2s_cafile/1]).
+-export([c2s_ciphers/0, c2s_ciphers/1]).
+-export([c2s_dhfile/0, c2s_dhfile/1]).
+-export([c2s_protocol_options/0, c2s_protocol_options/1]).
+-export([c2s_tls_compression/0, c2s_tls_compression/1]).
+-export([ca_file/0]).
+-export([cache_life_time/0, cache_life_time/1]).
+-export([cache_missed/0, cache_missed/1]).
+-export([cache_size/0, cache_size/1]).
+-export([captcha_cmd/0]).
+-export([captcha_host/0]).
+-export([captcha_limit/0]).
+-export([certfiles/0]).
+-export([cluster_backend/0]).
+-export([cluster_nodes/0]).
+-export([default_db/0, default_db/1]).
+-export([default_ram_db/0, default_ram_db/1]).
+-export([define_macro/0, define_macro/1]).
+-export([disable_sasl_mechanisms/0, disable_sasl_mechanisms/1]).
+-export([domain_balancing/0]).
+-export([ext_api_headers/0, ext_api_headers/1]).
+-export([ext_api_http_pool_size/0, ext_api_http_pool_size/1]).
+-export([ext_api_path_oauth/0]).
+-export([ext_api_url/0, ext_api_url/1]).
+-export([extauth_pool_name/0, extauth_pool_name/1]).
+-export([extauth_pool_size/0, extauth_pool_size/1]).
+-export([extauth_program/0, extauth_program/1]).
+-export([fqdn/0]).
+-export([hide_sensitive_log_data/0, hide_sensitive_log_data/1]).
+-export([host_config/0]).
+-export([hosts/0]).
+-export([include_config_file/0, include_config_file/1]).
+-export([language/0, language/1]).
+-export([ldap_backups/0, ldap_backups/1]).
+-export([ldap_base/0, ldap_base/1]).
+-export([ldap_deref_aliases/0, ldap_deref_aliases/1]).
+-export([ldap_dn_filter/0, ldap_dn_filter/1]).
+-export([ldap_encrypt/0, ldap_encrypt/1]).
+-export([ldap_filter/0, ldap_filter/1]).
+-export([ldap_password/0, ldap_password/1]).
+-export([ldap_port/0, ldap_port/1]).
+-export([ldap_rootdn/0, ldap_rootdn/1]).
+-export([ldap_servers/0, ldap_servers/1]).
+-export([ldap_tls_cacertfile/0, ldap_tls_cacertfile/1]).
+-export([ldap_tls_certfile/0, ldap_tls_certfile/1]).
+-export([ldap_tls_depth/0, ldap_tls_depth/1]).
+-export([ldap_tls_verify/0, ldap_tls_verify/1]).
+-export([ldap_uids/0, ldap_uids/1]).
+-export([listen/0]).
+-export([log_rate_limit/0]).
+-export([log_rotate_count/0]).
+-export([log_rotate_date/0]).
+-export([log_rotate_size/0]).
+-export([loglevel/0]).
+-export([max_fsm_queue/0, max_fsm_queue/1]).
+-export([modules/0, modules/1]).
+-export([negotiation_timeout/0]).
+-export([net_ticktime/0]).
+-export([new_sql_schema/0]).
+-export([oauth_access/0, oauth_access/1]).
+-export([oauth_cache_life_time/0]).
+-export([oauth_cache_missed/0]).
+-export([oauth_cache_size/0]).
+-export([oauth_db_type/0]).
+-export([oauth_expire/0]).
+-export([oauth_use_cache/0]).
+-export([oom_killer/0]).
+-export([oom_queue/0]).
+-export([oom_watermark/0]).
+-export([outgoing_s2s_families/0, outgoing_s2s_families/1]).
+-export([outgoing_s2s_port/0, outgoing_s2s_port/1]).
+-export([outgoing_s2s_timeout/0, outgoing_s2s_timeout/1]).
+-export([pam_service/0, pam_service/1]).
+-export([pam_userinfotype/0, pam_userinfotype/1]).
+-export([pgsql_users_number_estimate/0, pgsql_users_number_estimate/1]).
+-export([queue_dir/0]).
+-export([queue_type/0, queue_type/1]).
+-export([redis_connect_timeout/0]).
+-export([redis_db/0]).
+-export([redis_password/0]).
+-export([redis_pool_size/0]).
+-export([redis_port/0]).
+-export([redis_queue_type/0]).
+-export([redis_server/0]).
+-export([registration_timeout/0]).
+-export([resource_conflict/0, resource_conflict/1]).
+-export([riak_cacertfile/0]).
+-export([riak_password/0]).
+-export([riak_pool_size/0]).
+-export([riak_port/0]).
+-export([riak_server/0]).
+-export([riak_start_interval/0]).
+-export([riak_username/0]).
+-export([route_subdomains/0, route_subdomains/1]).
+-export([router_cache_life_time/0]).
+-export([router_cache_missed/0]).
+-export([router_cache_size/0]).
+-export([router_db_type/0]).
+-export([router_use_cache/0]).
+-export([rpc_timeout/0]).
+-export([s2s_access/0, s2s_access/1]).
+-export([s2s_cafile/0, s2s_cafile/1]).
+-export([s2s_ciphers/0, s2s_ciphers/1]).
+-export([s2s_dhfile/0, s2s_dhfile/1]).
+-export([s2s_dns_retries/0, s2s_dns_retries/1]).
+-export([s2s_dns_timeout/0, s2s_dns_timeout/1]).
+-export([s2s_max_retry_delay/0]).
+-export([s2s_protocol_options/0, s2s_protocol_options/1]).
+-export([s2s_queue_type/0, s2s_queue_type/1]).
+-export([s2s_timeout/0, s2s_timeout/1]).
+-export([s2s_tls_compression/0, s2s_tls_compression/1]).
+-export([s2s_use_starttls/0, s2s_use_starttls/1]).
+-export([s2s_zlib/0, s2s_zlib/1]).
+-export([shaper/0]).
+-export([shaper_rules/0, shaper_rules/1]).
+-export([sm_cache_life_time/0]).
+-export([sm_cache_missed/0]).
+-export([sm_cache_size/0]).
+-export([sm_db_type/0, sm_db_type/1]).
+-export([sm_use_cache/0, sm_use_cache/1]).
+-export([sql_connect_timeout/0, sql_connect_timeout/1]).
+-export([sql_database/0, sql_database/1]).
+-export([sql_keepalive_interval/0, sql_keepalive_interval/1]).
+-export([sql_password/0, sql_password/1]).
+-export([sql_pool_size/0, sql_pool_size/1]).
+-export([sql_port/0, sql_port/1]).
+-export([sql_query_timeout/0, sql_query_timeout/1]).
+-export([sql_queue_type/0, sql_queue_type/1]).
+-export([sql_server/0, sql_server/1]).
+-export([sql_ssl/0, sql_ssl/1]).
+-export([sql_ssl_cafile/0, sql_ssl_cafile/1]).
+-export([sql_ssl_certfile/0, sql_ssl_certfile/1]).
+-export([sql_ssl_verify/0, sql_ssl_verify/1]).
+-export([sql_start_interval/0, sql_start_interval/1]).
+-export([sql_type/0, sql_type/1]).
+-export([sql_username/0, sql_username/1]).
+-export([trusted_proxies/0, trusted_proxies/1]).
+-export([use_cache/0, use_cache/1]).
+-export([validate_stream/0, validate_stream/1]).
+-export([version/0]).
+-export([websocket_origin/0]).
+-export([websocket_ping_interval/0]).
+-export([websocket_timeout/0]).
+
+-spec access_rules() -> [{atom(),acl:access()}].
+access_rules() ->
+ access_rules(global).
+-spec access_rules(global | binary()) -> [{atom(),acl:access()}].
+access_rules(Host) ->
+ ejabberd_config:get_option({access_rules, Host}).
+
+-spec acl() -> [{atom(),[acl:acl_rule()]}].
+acl() ->
+ acl(global).
+-spec acl(global | binary()) -> [{atom(),[acl:acl_rule()]}].
+acl(Host) ->
+ ejabberd_config:get_option({acl, Host}).
+
+-spec acme() -> #{'ca_url'=>binary(), 'contact'=>binary()}.
+acme() ->
+ ejabberd_config:get_option({acme, global}).
+
+-spec allow_contrib_modules() -> boolean().
+allow_contrib_modules() ->
+ ejabberd_config:get_option({allow_contrib_modules, global}).
+
+-spec allow_multiple_connections() -> boolean().
+allow_multiple_connections() ->
+ allow_multiple_connections(global).
+-spec allow_multiple_connections(global | binary()) -> boolean().
+allow_multiple_connections(Host) ->
+ ejabberd_config:get_option({allow_multiple_connections, Host}).
+
+-spec anonymous_protocol() -> 'both' | 'login_anon' | 'sasl_anon'.
+anonymous_protocol() ->
+ anonymous_protocol(global).
+-spec anonymous_protocol(global | binary()) -> 'both' | 'login_anon' | 'sasl_anon'.
+anonymous_protocol(Host) ->
+ ejabberd_config:get_option({anonymous_protocol, Host}).
+
+-spec api_permissions() -> [ejabberd_access_permissions:permission()].
+api_permissions() ->
+ ejabberd_config:get_option({api_permissions, global}).
+
+-spec append_host_config() -> [{binary(),any()}].
+append_host_config() ->
+ ejabberd_config:get_option({append_host_config, global}).
+
+-spec auth_cache_life_time() -> 'infinity' | pos_integer().
+auth_cache_life_time() ->
+ ejabberd_config:get_option({auth_cache_life_time, global}).
+
+-spec auth_cache_missed() -> boolean().
+auth_cache_missed() ->
+ ejabberd_config:get_option({auth_cache_missed, global}).
+
+-spec auth_cache_size() -> 'infinity' | pos_integer().
+auth_cache_size() ->
+ ejabberd_config:get_option({auth_cache_size, global}).
+
+-spec auth_method() -> [atom()].
+auth_method() ->
+ auth_method(global).
+-spec auth_method(global | binary()) -> [atom()].
+auth_method(Host) ->
+ ejabberd_config:get_option({auth_method, Host}).
+
+-spec auth_password_format() -> 'plain' | 'scram'.
+auth_password_format() ->
+ auth_password_format(global).
+-spec auth_password_format(global | binary()) -> 'plain' | 'scram'.
+auth_password_format(Host) ->
+ ejabberd_config:get_option({auth_password_format, Host}).
+
+-spec auth_use_cache() -> boolean().
+auth_use_cache() ->
+ auth_use_cache(global).
+-spec auth_use_cache(global | binary()) -> boolean().
+auth_use_cache(Host) ->
+ ejabberd_config:get_option({auth_use_cache, Host}).
+
+-spec c2s_cafile() -> 'undefined' | binary().
+c2s_cafile() ->
+ c2s_cafile(global).
+-spec c2s_cafile(global | binary()) -> 'undefined' | binary().
+c2s_cafile(Host) ->
+ ejabberd_config:get_option({c2s_cafile, Host}).
+
+-spec c2s_ciphers() -> 'undefined' | binary().
+c2s_ciphers() ->
+ c2s_ciphers(global).
+-spec c2s_ciphers(global | binary()) -> 'undefined' | binary().
+c2s_ciphers(Host) ->
+ ejabberd_config:get_option({c2s_ciphers, Host}).
+
+-spec c2s_dhfile() -> 'undefined' | binary().
+c2s_dhfile() ->
+ c2s_dhfile(global).
+-spec c2s_dhfile(global | binary()) -> 'undefined' | binary().
+c2s_dhfile(Host) ->
+ ejabberd_config:get_option({c2s_dhfile, Host}).
+
+-spec c2s_protocol_options() -> 'undefined' | binary().
+c2s_protocol_options() ->
+ c2s_protocol_options(global).
+-spec c2s_protocol_options(global | binary()) -> 'undefined' | binary().
+c2s_protocol_options(Host) ->
+ ejabberd_config:get_option({c2s_protocol_options, Host}).
+
+-spec c2s_tls_compression() -> 'false' | 'true' | 'undefined'.
+c2s_tls_compression() ->
+ c2s_tls_compression(global).
+-spec c2s_tls_compression(global | binary()) -> 'false' | 'true' | 'undefined'.
+c2s_tls_compression(Host) ->
+ ejabberd_config:get_option({c2s_tls_compression, Host}).
+
+-spec ca_file() -> binary().
+ca_file() ->
+ ejabberd_config:get_option({ca_file, global}).
+
+-spec cache_life_time() -> 'infinity' | pos_integer().
+cache_life_time() ->
+ cache_life_time(global).
+-spec cache_life_time(global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Host) ->
+ ejabberd_config:get_option({cache_life_time, Host}).
+
+-spec cache_missed() -> boolean().
+cache_missed() ->
+ cache_missed(global).
+-spec cache_missed(global | binary()) -> boolean().
+cache_missed(Host) ->
+ ejabberd_config:get_option({cache_missed, Host}).
+
+-spec cache_size() -> 'infinity' | pos_integer().
+cache_size() ->
+ cache_size(global).
+-spec cache_size(global | binary()) -> 'infinity' | pos_integer().
+cache_size(Host) ->
+ ejabberd_config:get_option({cache_size, Host}).
+
+-spec captcha_cmd() -> 'undefined' | binary().
+captcha_cmd() ->
+ ejabberd_config:get_option({captcha_cmd, global}).
+
+-spec captcha_host() -> binary().
+captcha_host() ->
+ ejabberd_config:get_option({captcha_host, global}).
+
+-spec captcha_limit() -> 'infinity' | pos_integer().
+captcha_limit() ->
+ ejabberd_config:get_option({captcha_limit, global}).
+
+-spec certfiles() -> 'undefined' | [binary()].
+certfiles() ->
+ ejabberd_config:get_option({certfiles, global}).
+
+-spec cluster_backend() -> atom().
+cluster_backend() ->
+ ejabberd_config:get_option({cluster_backend, global}).
+
+-spec cluster_nodes() -> [atom()].
+cluster_nodes() ->
+ ejabberd_config:get_option({cluster_nodes, global}).
+
+-spec default_db() -> 'mnesia' | 'riak' | 'sql'.
+default_db() ->
+ default_db(global).
+-spec default_db(global | binary()) -> 'mnesia' | 'riak' | 'sql'.
+default_db(Host) ->
+ ejabberd_config:get_option({default_db, Host}).
+
+-spec default_ram_db() -> 'mnesia' | 'redis' | 'riak' | 'sql'.
+default_ram_db() ->
+ default_ram_db(global).
+-spec default_ram_db(global | binary()) -> 'mnesia' | 'redis' | 'riak' | 'sql'.
+default_ram_db(Host) ->
+ ejabberd_config:get_option({default_ram_db, Host}).
+
+-spec define_macro() -> any().
+define_macro() ->
+ define_macro(global).
+-spec define_macro(global | binary()) -> any().
+define_macro(Host) ->
+ ejabberd_config:get_option({define_macro, Host}).
+
+-spec disable_sasl_mechanisms() -> [binary()].
+disable_sasl_mechanisms() ->
+ disable_sasl_mechanisms(global).
+-spec disable_sasl_mechanisms(global | binary()) -> [binary()].
+disable_sasl_mechanisms(Host) ->
+ ejabberd_config:get_option({disable_sasl_mechanisms, Host}).
+
+-spec domain_balancing() -> #{binary()=>#{'component_number':=1..1114111, 'type'=>'bare_destination' | 'bare_source' | 'destination' | 'random' | 'source'}}.
+domain_balancing() ->
+ ejabberd_config:get_option({domain_balancing, global}).
+
+-spec ext_api_headers() -> binary().
+ext_api_headers() ->
+ ext_api_headers(global).
+-spec ext_api_headers(global | binary()) -> binary().
+ext_api_headers(Host) ->
+ ejabberd_config:get_option({ext_api_headers, Host}).
+
+-spec ext_api_http_pool_size() -> pos_integer().
+ext_api_http_pool_size() ->
+ ext_api_http_pool_size(global).
+-spec ext_api_http_pool_size(global | binary()) -> pos_integer().
+ext_api_http_pool_size(Host) ->
+ ejabberd_config:get_option({ext_api_http_pool_size, Host}).
+
+-spec ext_api_path_oauth() -> binary().
+ext_api_path_oauth() ->
+ ejabberd_config:get_option({ext_api_path_oauth, global}).
+
+-spec ext_api_url() -> binary().
+ext_api_url() ->
+ ext_api_url(global).
+-spec ext_api_url(global | binary()) -> binary().
+ext_api_url(Host) ->
+ ejabberd_config:get_option({ext_api_url, Host}).
+
+-spec extauth_pool_name() -> 'undefined' | binary().
+extauth_pool_name() ->
+ extauth_pool_name(global).
+-spec extauth_pool_name(global | binary()) -> 'undefined' | binary().
+extauth_pool_name(Host) ->
+ ejabberd_config:get_option({extauth_pool_name, Host}).
+
+-spec extauth_pool_size() -> 'undefined' | pos_integer().
+extauth_pool_size() ->
+ extauth_pool_size(global).
+-spec extauth_pool_size(global | binary()) -> 'undefined' | pos_integer().
+extauth_pool_size(Host) ->
+ ejabberd_config:get_option({extauth_pool_size, Host}).
+
+-spec extauth_program() -> 'undefined' | string().
+extauth_program() ->
+ extauth_program(global).
+-spec extauth_program(global | binary()) -> 'undefined' | string().
+extauth_program(Host) ->
+ ejabberd_config:get_option({extauth_program, Host}).
+
+-spec fqdn() -> [binary()].
+fqdn() ->
+ ejabberd_config:get_option({fqdn, global}).
+
+-spec hide_sensitive_log_data() -> boolean().
+hide_sensitive_log_data() ->
+ hide_sensitive_log_data(global).
+-spec hide_sensitive_log_data(global | binary()) -> boolean().
+hide_sensitive_log_data(Host) ->
+ ejabberd_config:get_option({hide_sensitive_log_data, Host}).
+
+-spec host_config() -> [{binary(),any()}].
+host_config() ->
+ ejabberd_config:get_option({host_config, global}).
+
+-spec hosts() -> [binary(),...].
+hosts() ->
+ ejabberd_config:get_option({hosts, global}).
+
+-spec include_config_file() -> any().
+include_config_file() ->
+ include_config_file(global).
+-spec include_config_file(global | binary()) -> any().
+include_config_file(Host) ->
+ ejabberd_config:get_option({include_config_file, Host}).
+
+-spec language() -> binary().
+language() ->
+ language(global).
+-spec language(global | binary()) -> binary().
+language(Host) ->
+ ejabberd_config:get_option({language, Host}).
+
+-spec ldap_backups() -> [binary()].
+ldap_backups() ->
+ ldap_backups(global).
+-spec ldap_backups(global | binary()) -> [binary()].
+ldap_backups(Host) ->
+ ejabberd_config:get_option({ldap_backups, Host}).
+
+-spec ldap_base() -> binary().
+ldap_base() ->
+ ldap_base(global).
+-spec ldap_base(global | binary()) -> binary().
+ldap_base(Host) ->
+ ejabberd_config:get_option({ldap_base, Host}).
+
+-spec ldap_deref_aliases() -> 'always' | 'finding' | 'never' | 'searching'.
+ldap_deref_aliases() ->
+ ldap_deref_aliases(global).
+-spec ldap_deref_aliases(global | binary()) -> 'always' | 'finding' | 'never' | 'searching'.
+ldap_deref_aliases(Host) ->
+ ejabberd_config:get_option({ldap_deref_aliases, Host}).
+
+-spec ldap_dn_filter() -> {binary(),[binary()]}.
+ldap_dn_filter() ->
+ ldap_dn_filter(global).
+-spec ldap_dn_filter(global | binary()) -> {binary(),[binary()]}.
+ldap_dn_filter(Host) ->
+ ejabberd_config:get_option({ldap_dn_filter, Host}).
+
+-spec ldap_encrypt() -> 'none' | 'starttls' | 'tls'.
+ldap_encrypt() ->
+ ldap_encrypt(global).
+-spec ldap_encrypt(global | binary()) -> 'none' | 'starttls' | 'tls'.
+ldap_encrypt(Host) ->
+ ejabberd_config:get_option({ldap_encrypt, Host}).
+
+-spec ldap_filter() -> binary().
+ldap_filter() ->
+ ldap_filter(global).
+-spec ldap_filter(global | binary()) -> binary().
+ldap_filter(Host) ->
+ ejabberd_config:get_option({ldap_filter, Host}).
+
+-spec ldap_password() -> binary().
+ldap_password() ->
+ ldap_password(global).
+-spec ldap_password(global | binary()) -> binary().
+ldap_password(Host) ->
+ ejabberd_config:get_option({ldap_password, Host}).
+
+-spec ldap_port() -> 1..1114111.
+ldap_port() ->
+ ldap_port(global).
+-spec ldap_port(global | binary()) -> 1..1114111.
+ldap_port(Host) ->
+ ejabberd_config:get_option({ldap_port, Host}).
+
+-spec ldap_rootdn() -> binary().
+ldap_rootdn() ->
+ ldap_rootdn(global).
+-spec ldap_rootdn(global | binary()) -> binary().
+ldap_rootdn(Host) ->
+ ejabberd_config:get_option({ldap_rootdn, Host}).
+
+-spec ldap_servers() -> [binary()].
+ldap_servers() ->
+ ldap_servers(global).
+-spec ldap_servers(global | binary()) -> [binary()].
+ldap_servers(Host) ->
+ ejabberd_config:get_option({ldap_servers, Host}).
+
+-spec ldap_tls_cacertfile() -> 'undefined' | binary().
+ldap_tls_cacertfile() ->
+ ldap_tls_cacertfile(global).
+-spec ldap_tls_cacertfile(global | binary()) -> 'undefined' | binary().
+ldap_tls_cacertfile(Host) ->
+ ejabberd_config:get_option({ldap_tls_cacertfile, Host}).
+
+-spec ldap_tls_certfile() -> 'undefined' | binary().
+ldap_tls_certfile() ->
+ ldap_tls_certfile(global).
+-spec ldap_tls_certfile(global | binary()) -> 'undefined' | binary().
+ldap_tls_certfile(Host) ->
+ ejabberd_config:get_option({ldap_tls_certfile, Host}).
+
+-spec ldap_tls_depth() -> 'undefined' | non_neg_integer().
+ldap_tls_depth() ->
+ ldap_tls_depth(global).
+-spec ldap_tls_depth(global | binary()) -> 'undefined' | non_neg_integer().
+ldap_tls_depth(Host) ->
+ ejabberd_config:get_option({ldap_tls_depth, Host}).
+
+-spec ldap_tls_verify() -> 'false' | 'hard' | 'soft'.
+ldap_tls_verify() ->
+ ldap_tls_verify(global).
+-spec ldap_tls_verify(global | binary()) -> 'false' | 'hard' | 'soft'.
+ldap_tls_verify(Host) ->
+ ejabberd_config:get_option({ldap_tls_verify, Host}).
+
+-spec ldap_uids() -> [{binary(),binary()}].
+ldap_uids() ->
+ ldap_uids(global).
+-spec ldap_uids(global | binary()) -> [{binary(),binary()}].
+ldap_uids(Host) ->
+ ejabberd_config:get_option({ldap_uids, Host}).
+
+-spec listen() -> [ejabberd_listener:listener()].
+listen() ->
+ ejabberd_config:get_option({listen, global}).
+
+-spec log_rate_limit() -> 'undefined' | non_neg_integer().
+log_rate_limit() ->
+ ejabberd_config:get_option({log_rate_limit, global}).
+
+-spec log_rotate_count() -> 'undefined' | non_neg_integer().
+log_rotate_count() ->
+ ejabberd_config:get_option({log_rotate_count, global}).
+
+-spec log_rotate_date() -> 'undefined' | string().
+log_rotate_date() ->
+ ejabberd_config:get_option({log_rotate_date, global}).
+
+-spec log_rotate_size() -> 'undefined' | non_neg_integer().
+log_rotate_size() ->
+ ejabberd_config:get_option({log_rotate_size, global}).
+
+-spec loglevel() -> 0 | 1 | 2 | 3 | 4 | 5.
+loglevel() ->
+ ejabberd_config:get_option({loglevel, global}).
+
+-spec max_fsm_queue() -> 'undefined' | pos_integer().
+max_fsm_queue() ->
+ max_fsm_queue(global).
+-spec max_fsm_queue(global | binary()) -> 'undefined' | pos_integer().
+max_fsm_queue(Host) ->
+ ejabberd_config:get_option({max_fsm_queue, Host}).
+
+-spec modules() -> [{module(),gen_mod:opts(),integer()}].
+modules() ->
+ modules(global).
+-spec modules(global | binary()) -> [{module(),gen_mod:opts(),integer()}].
+modules(Host) ->
+ ejabberd_config:get_option({modules, Host}).
+
+-spec negotiation_timeout() -> pos_integer().
+negotiation_timeout() ->
+ ejabberd_config:get_option({negotiation_timeout, global}).
+
+-spec net_ticktime() -> pos_integer().
+net_ticktime() ->
+ ejabberd_config:get_option({net_ticktime, global}).
+
+-spec new_sql_schema() -> boolean().
+new_sql_schema() ->
+ ejabberd_config:get_option({new_sql_schema, global}).
+
+-spec oauth_access() -> 'none' | acl:acl().
+oauth_access() ->
+ oauth_access(global).
+-spec oauth_access(global | binary()) -> 'none' | acl:acl().
+oauth_access(Host) ->
+ ejabberd_config:get_option({oauth_access, Host}).
+
+-spec oauth_cache_life_time() -> 'infinity' | pos_integer().
+oauth_cache_life_time() ->
+ ejabberd_config:get_option({oauth_cache_life_time, global}).
+
+-spec oauth_cache_missed() -> boolean().
+oauth_cache_missed() ->
+ ejabberd_config:get_option({oauth_cache_missed, global}).
+
+-spec oauth_cache_size() -> 'infinity' | pos_integer().
+oauth_cache_size() ->
+ ejabberd_config:get_option({oauth_cache_size, global}).
+
+-spec oauth_db_type() -> atom().
+oauth_db_type() ->
+ ejabberd_config:get_option({oauth_db_type, global}).
+
+-spec oauth_expire() -> non_neg_integer().
+oauth_expire() ->
+ ejabberd_config:get_option({oauth_expire, global}).
+
+-spec oauth_use_cache() -> boolean().
+oauth_use_cache() ->
+ ejabberd_config:get_option({oauth_use_cache, global}).
+
+-spec oom_killer() -> boolean().
+oom_killer() ->
+ ejabberd_config:get_option({oom_killer, global}).
+
+-spec oom_queue() -> pos_integer().
+oom_queue() ->
+ ejabberd_config:get_option({oom_queue, global}).
+
+-spec oom_watermark() -> 1..255.
+oom_watermark() ->
+ ejabberd_config:get_option({oom_watermark, global}).
+
+-spec outgoing_s2s_families() -> ['inet' | 'inet6',...].
+outgoing_s2s_families() ->
+ outgoing_s2s_families(global).
+-spec outgoing_s2s_families(global | binary()) -> ['inet' | 'inet6',...].
+outgoing_s2s_families(Host) ->
+ ejabberd_config:get_option({outgoing_s2s_families, Host}).
+
+-spec outgoing_s2s_port() -> 1..1114111.
+outgoing_s2s_port() ->
+ outgoing_s2s_port(global).
+-spec outgoing_s2s_port(global | binary()) -> 1..1114111.
+outgoing_s2s_port(Host) ->
+ ejabberd_config:get_option({outgoing_s2s_port, Host}).
+
+-spec outgoing_s2s_timeout() -> 'infinity' | pos_integer().
+outgoing_s2s_timeout() ->
+ outgoing_s2s_timeout(global).
+-spec outgoing_s2s_timeout(global | binary()) -> 'infinity' | pos_integer().
+outgoing_s2s_timeout(Host) ->
+ ejabberd_config:get_option({outgoing_s2s_timeout, Host}).
+
+-spec pam_service() -> binary().
+pam_service() ->
+ pam_service(global).
+-spec pam_service(global | binary()) -> binary().
+pam_service(Host) ->
+ ejabberd_config:get_option({pam_service, Host}).
+
+-spec pam_userinfotype() -> 'jid' | 'username'.
+pam_userinfotype() ->
+ pam_userinfotype(global).
+-spec pam_userinfotype(global | binary()) -> 'jid' | 'username'.
+pam_userinfotype(Host) ->
+ ejabberd_config:get_option({pam_userinfotype, Host}).
+
+-spec pgsql_users_number_estimate() -> boolean().
+pgsql_users_number_estimate() ->
+ pgsql_users_number_estimate(global).
+-spec pgsql_users_number_estimate(global | binary()) -> boolean().
+pgsql_users_number_estimate(Host) ->
+ ejabberd_config:get_option({pgsql_users_number_estimate, Host}).
+
+-spec queue_dir() -> 'undefined' | binary().
+queue_dir() ->
+ ejabberd_config:get_option({queue_dir, global}).
+
+-spec queue_type() -> 'file' | 'ram'.
+queue_type() ->
+ queue_type(global).
+-spec queue_type(global | binary()) -> 'file' | 'ram'.
+queue_type(Host) ->
+ ejabberd_config:get_option({queue_type, Host}).
+
+-spec redis_connect_timeout() -> pos_integer().
+redis_connect_timeout() ->
+ ejabberd_config:get_option({redis_connect_timeout, global}).
+
+-spec redis_db() -> non_neg_integer().
+redis_db() ->
+ ejabberd_config:get_option({redis_db, global}).
+
+-spec redis_password() -> string().
+redis_password() ->
+ ejabberd_config:get_option({redis_password, global}).
+
+-spec redis_pool_size() -> pos_integer().
+redis_pool_size() ->
+ ejabberd_config:get_option({redis_pool_size, global}).
+
+-spec redis_port() -> 1..1114111.
+redis_port() ->
+ ejabberd_config:get_option({redis_port, global}).
+
+-spec redis_queue_type() -> 'file' | 'ram'.
+redis_queue_type() ->
+ ejabberd_config:get_option({redis_queue_type, global}).
+
+-spec redis_server() -> string().
+redis_server() ->
+ ejabberd_config:get_option({redis_server, global}).
+
+-spec registration_timeout() -> 'infinity' | pos_integer().
+registration_timeout() ->
+ ejabberd_config:get_option({registration_timeout, global}).
+
+-spec resource_conflict() -> 'acceptnew' | 'closenew' | 'closeold' | 'setresource'.
+resource_conflict() ->
+ resource_conflict(global).
+-spec resource_conflict(global | binary()) -> 'acceptnew' | 'closenew' | 'closeold' | 'setresource'.
+resource_conflict(Host) ->
+ ejabberd_config:get_option({resource_conflict, Host}).
+
+-spec riak_cacertfile() -> 'nil' | string().
+riak_cacertfile() ->
+ ejabberd_config:get_option({riak_cacertfile, global}).
+
+-spec riak_password() -> 'nil' | string().
+riak_password() ->
+ ejabberd_config:get_option({riak_password, global}).
+
+-spec riak_pool_size() -> pos_integer().
+riak_pool_size() ->
+ ejabberd_config:get_option({riak_pool_size, global}).
+
+-spec riak_port() -> 1..1114111.
+riak_port() ->
+ ejabberd_config:get_option({riak_port, global}).
+
+-spec riak_server() -> string().
+riak_server() ->
+ ejabberd_config:get_option({riak_server, global}).
+
+-spec riak_start_interval() -> pos_integer().
+riak_start_interval() ->
+ ejabberd_config:get_option({riak_start_interval, global}).
+
+-spec riak_username() -> 'nil' | string().
+riak_username() ->
+ ejabberd_config:get_option({riak_username, global}).
+
+-spec route_subdomains() -> 'local' | 's2s'.
+route_subdomains() ->
+ route_subdomains(global).
+-spec route_subdomains(global | binary()) -> 'local' | 's2s'.
+route_subdomains(Host) ->
+ ejabberd_config:get_option({route_subdomains, Host}).
+
+-spec router_cache_life_time() -> 'infinity' | pos_integer().
+router_cache_life_time() ->
+ ejabberd_config:get_option({router_cache_life_time, global}).
+
+-spec router_cache_missed() -> boolean().
+router_cache_missed() ->
+ ejabberd_config:get_option({router_cache_missed, global}).
+
+-spec router_cache_size() -> 'infinity' | pos_integer().
+router_cache_size() ->
+ ejabberd_config:get_option({router_cache_size, global}).
+
+-spec router_db_type() -> atom().
+router_db_type() ->
+ ejabberd_config:get_option({router_db_type, global}).
+
+-spec router_use_cache() -> boolean().
+router_use_cache() ->
+ ejabberd_config:get_option({router_use_cache, global}).
+
+-spec rpc_timeout() -> pos_integer().
+rpc_timeout() ->
+ ejabberd_config:get_option({rpc_timeout, global}).
+
+-spec s2s_access() -> 'all' | acl:acl().
+s2s_access() ->
+ s2s_access(global).
+-spec s2s_access(global | binary()) -> 'all' | acl:acl().
+s2s_access(Host) ->
+ ejabberd_config:get_option({s2s_access, Host}).
+
+-spec s2s_cafile() -> 'undefined' | binary().
+s2s_cafile() ->
+ s2s_cafile(global).
+-spec s2s_cafile(global | binary()) -> 'undefined' | binary().
+s2s_cafile(Host) ->
+ ejabberd_config:get_option({s2s_cafile, Host}).
+
+-spec s2s_ciphers() -> 'undefined' | binary().
+s2s_ciphers() ->
+ s2s_ciphers(global).
+-spec s2s_ciphers(global | binary()) -> 'undefined' | binary().
+s2s_ciphers(Host) ->
+ ejabberd_config:get_option({s2s_ciphers, Host}).
+
+-spec s2s_dhfile() -> 'undefined' | binary().
+s2s_dhfile() ->
+ s2s_dhfile(global).
+-spec s2s_dhfile(global | binary()) -> 'undefined' | binary().
+s2s_dhfile(Host) ->
+ ejabberd_config:get_option({s2s_dhfile, Host}).
+
+-spec s2s_dns_retries() -> non_neg_integer().
+s2s_dns_retries() ->
+ s2s_dns_retries(global).
+-spec s2s_dns_retries(global | binary()) -> non_neg_integer().
+s2s_dns_retries(Host) ->
+ ejabberd_config:get_option({s2s_dns_retries, Host}).
+
+-spec s2s_dns_timeout() -> 'infinity' | pos_integer().
+s2s_dns_timeout() ->
+ s2s_dns_timeout(global).
+-spec s2s_dns_timeout(global | binary()) -> 'infinity' | pos_integer().
+s2s_dns_timeout(Host) ->
+ ejabberd_config:get_option({s2s_dns_timeout, Host}).
+
+-spec s2s_max_retry_delay() -> pos_integer().
+s2s_max_retry_delay() ->
+ ejabberd_config:get_option({s2s_max_retry_delay, global}).
+
+-spec s2s_protocol_options() -> 'undefined' | binary().
+s2s_protocol_options() ->
+ s2s_protocol_options(global).
+-spec s2s_protocol_options(global | binary()) -> 'undefined' | binary().
+s2s_protocol_options(Host) ->
+ ejabberd_config:get_option({s2s_protocol_options, Host}).
+
+-spec s2s_queue_type() -> 'file' | 'ram'.
+s2s_queue_type() ->
+ s2s_queue_type(global).
+-spec s2s_queue_type(global | binary()) -> 'file' | 'ram'.
+s2s_queue_type(Host) ->
+ ejabberd_config:get_option({s2s_queue_type, Host}).
+
+-spec s2s_timeout() -> 'infinity' | pos_integer().
+s2s_timeout() ->
+ s2s_timeout(global).
+-spec s2s_timeout(global | binary()) -> 'infinity' | pos_integer().
+s2s_timeout(Host) ->
+ ejabberd_config:get_option({s2s_timeout, Host}).
+
+-spec s2s_tls_compression() -> 'false' | 'true' | 'undefined'.
+s2s_tls_compression() ->
+ s2s_tls_compression(global).
+-spec s2s_tls_compression(global | binary()) -> 'false' | 'true' | 'undefined'.
+s2s_tls_compression(Host) ->
+ ejabberd_config:get_option({s2s_tls_compression, Host}).
+
+-spec s2s_use_starttls() -> 'false' | 'optional' | 'required' | 'true'.
+s2s_use_starttls() ->
+ s2s_use_starttls(global).
+-spec s2s_use_starttls(global | binary()) -> 'false' | 'optional' | 'required' | 'true'.
+s2s_use_starttls(Host) ->
+ ejabberd_config:get_option({s2s_use_starttls, Host}).
+
+-spec s2s_zlib() -> boolean().
+s2s_zlib() ->
+ s2s_zlib(global).
+-spec s2s_zlib(global | binary()) -> boolean().
+s2s_zlib(Host) ->
+ ejabberd_config:get_option({s2s_zlib, Host}).
+
+-spec shaper() -> #{atom()=>ejabberd_shaper:shaper_rate()}.
+shaper() ->
+ ejabberd_config:get_option({shaper, global}).
+
+-spec shaper_rules() -> [{atom(),[ejabberd_shaper:shaper_rule()]}].
+shaper_rules() ->
+ shaper_rules(global).
+-spec shaper_rules(global | binary()) -> [{atom(),[ejabberd_shaper:shaper_rule()]}].
+shaper_rules(Host) ->
+ ejabberd_config:get_option({shaper_rules, Host}).
+
+-spec sm_cache_life_time() -> 'infinity' | pos_integer().
+sm_cache_life_time() ->
+ ejabberd_config:get_option({sm_cache_life_time, global}).
+
+-spec sm_cache_missed() -> boolean().
+sm_cache_missed() ->
+ ejabberd_config:get_option({sm_cache_missed, global}).
+
+-spec sm_cache_size() -> 'infinity' | pos_integer().
+sm_cache_size() ->
+ ejabberd_config:get_option({sm_cache_size, global}).
+
+-spec sm_db_type() -> atom().
+sm_db_type() ->
+ sm_db_type(global).
+-spec sm_db_type(global | binary()) -> atom().
+sm_db_type(Host) ->
+ ejabberd_config:get_option({sm_db_type, Host}).
+
+-spec sm_use_cache() -> boolean().
+sm_use_cache() ->
+ sm_use_cache(global).
+-spec sm_use_cache(global | binary()) -> boolean().
+sm_use_cache(Host) ->
+ ejabberd_config:get_option({sm_use_cache, Host}).
+
+-spec sql_connect_timeout() -> pos_integer().
+sql_connect_timeout() ->
+ sql_connect_timeout(global).
+-spec sql_connect_timeout(global | binary()) -> pos_integer().
+sql_connect_timeout(Host) ->
+ ejabberd_config:get_option({sql_connect_timeout, Host}).
+
+-spec sql_database() -> 'undefined' | binary().
+sql_database() ->
+ sql_database(global).
+-spec sql_database(global | binary()) -> 'undefined' | binary().
+sql_database(Host) ->
+ ejabberd_config:get_option({sql_database, Host}).
+
+-spec sql_keepalive_interval() -> 'undefined' | pos_integer().
+sql_keepalive_interval() ->
+ sql_keepalive_interval(global).
+-spec sql_keepalive_interval(global | binary()) -> 'undefined' | pos_integer().
+sql_keepalive_interval(Host) ->
+ ejabberd_config:get_option({sql_keepalive_interval, Host}).
+
+-spec sql_password() -> binary().
+sql_password() ->
+ sql_password(global).
+-spec sql_password(global | binary()) -> binary().
+sql_password(Host) ->
+ ejabberd_config:get_option({sql_password, Host}).
+
+-spec sql_pool_size() -> pos_integer().
+sql_pool_size() ->
+ sql_pool_size(global).
+-spec sql_pool_size(global | binary()) -> pos_integer().
+sql_pool_size(Host) ->
+ ejabberd_config:get_option({sql_pool_size, Host}).
+
+-spec sql_port() -> 1..1114111.
+sql_port() ->
+ sql_port(global).
+-spec sql_port(global | binary()) -> 1..1114111.
+sql_port(Host) ->
+ ejabberd_config:get_option({sql_port, Host}).
+
+-spec sql_query_timeout() -> pos_integer().
+sql_query_timeout() ->
+ sql_query_timeout(global).
+-spec sql_query_timeout(global | binary()) -> pos_integer().
+sql_query_timeout(Host) ->
+ ejabberd_config:get_option({sql_query_timeout, Host}).
+
+-spec sql_queue_type() -> 'file' | 'ram'.
+sql_queue_type() ->
+ sql_queue_type(global).
+-spec sql_queue_type(global | binary()) -> 'file' | 'ram'.
+sql_queue_type(Host) ->
+ ejabberd_config:get_option({sql_queue_type, Host}).
+
+-spec sql_server() -> binary().
+sql_server() ->
+ sql_server(global).
+-spec sql_server(global | binary()) -> binary().
+sql_server(Host) ->
+ ejabberd_config:get_option({sql_server, Host}).
+
+-spec sql_ssl() -> boolean().
+sql_ssl() ->
+ sql_ssl(global).
+-spec sql_ssl(global | binary()) -> boolean().
+sql_ssl(Host) ->
+ ejabberd_config:get_option({sql_ssl, Host}).
+
+-spec sql_ssl_cafile() -> 'undefined' | binary().
+sql_ssl_cafile() ->
+ sql_ssl_cafile(global).
+-spec sql_ssl_cafile(global | binary()) -> 'undefined' | binary().
+sql_ssl_cafile(Host) ->
+ ejabberd_config:get_option({sql_ssl_cafile, Host}).
+
+-spec sql_ssl_certfile() -> 'undefined' | binary().
+sql_ssl_certfile() ->
+ sql_ssl_certfile(global).
+-spec sql_ssl_certfile(global | binary()) -> 'undefined' | binary().
+sql_ssl_certfile(Host) ->
+ ejabberd_config:get_option({sql_ssl_certfile, Host}).
+
+-spec sql_ssl_verify() -> boolean().
+sql_ssl_verify() ->
+ sql_ssl_verify(global).
+-spec sql_ssl_verify(global | binary()) -> boolean().
+sql_ssl_verify(Host) ->
+ ejabberd_config:get_option({sql_ssl_verify, Host}).
+
+-spec sql_start_interval() -> pos_integer().
+sql_start_interval() ->
+ sql_start_interval(global).
+-spec sql_start_interval(global | binary()) -> pos_integer().
+sql_start_interval(Host) ->
+ ejabberd_config:get_option({sql_start_interval, Host}).
+
+-spec sql_type() -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite' | 'undefined'.
+sql_type() ->
+ sql_type(global).
+-spec sql_type(global | binary()) -> 'mssql' | 'mysql' | 'odbc' | 'pgsql' | 'sqlite' | 'undefined'.
+sql_type(Host) ->
+ ejabberd_config:get_option({sql_type, Host}).
+
+-spec sql_username() -> binary().
+sql_username() ->
+ sql_username(global).
+-spec sql_username(global | binary()) -> binary().
+sql_username(Host) ->
+ ejabberd_config:get_option({sql_username, Host}).
+
+-spec trusted_proxies() -> 'all' | [{inet:ip4_address() | inet:ip6_address(),byte()}].
+trusted_proxies() ->
+ trusted_proxies(global).
+-spec trusted_proxies(global | binary()) -> 'all' | [{inet:ip4_address() | inet:ip6_address(),byte()}].
+trusted_proxies(Host) ->
+ ejabberd_config:get_option({trusted_proxies, Host}).
+
+-spec use_cache() -> boolean().
+use_cache() ->
+ use_cache(global).
+-spec use_cache(global | binary()) -> boolean().
+use_cache(Host) ->
+ ejabberd_config:get_option({use_cache, Host}).
+
+-spec validate_stream() -> boolean().
+validate_stream() ->
+ validate_stream(global).
+-spec validate_stream(global | binary()) -> boolean().
+validate_stream(Host) ->
+ ejabberd_config:get_option({validate_stream, Host}).
+
+-spec version() -> binary().
+version() ->
+ ejabberd_config:get_option({version, global}).
+
+-spec websocket_origin() -> [binary()].
+websocket_origin() ->
+ ejabberd_config:get_option({websocket_origin, global}).
+
+-spec websocket_ping_interval() -> pos_integer().
+websocket_ping_interval() ->
+ ejabberd_config:get_option({websocket_ping_interval, global}).
+
+-spec websocket_timeout() -> pos_integer().
+websocket_timeout() ->
+ ejabberd_config:get_option({websocket_timeout, global}).
+
diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl
new file mode 100644
index 000000000..e25cc2e64
--- /dev/null
+++ b/src/ejabberd_options.erl
@@ -0,0 +1,757 @@
+%%%----------------------------------------------------------------------
+%%% 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(ejabberd_options).
+-behaviour(ejabberd_config).
+
+-export([opt_type/1, options/0, globals/0]).
+
+-ifdef(NEW_SQL_SCHEMA).
+-define(USE_NEW_SQL_SCHEMA_DEFAULT, true).
+-else.
+-define(USE_NEW_SQL_SCHEMA_DEFAULT, false).
+-endif.
+
+-include_lib("kernel/include/inet.hrl").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+-spec opt_type(atom()) -> econf:validator().
+opt_type(access_rules) ->
+ acl:validator(access_rules);
+opt_type(acl) ->
+ acl:validator(acl);
+opt_type(acme) ->
+ econf:options(
+ #{ca_url => econf:url(),
+ contact => econf:binary("^[a-zA-Z]+:[^:]+$")},
+ [unique, {return, map}]);
+opt_type(allow_contrib_modules) ->
+ econf:bool();
+opt_type(allow_multiple_connections) ->
+ econf:bool();
+opt_type(anonymous_protocol) ->
+ econf:enum([sasl_anon, login_anon, both]);
+opt_type(api_permissions) ->
+ ejabberd_access_permissions:validator();
+opt_type(append_host_config) ->
+ econf:map(
+ econf:and_then(
+ econf:domain(),
+ econf:enum(ejabberd_option:hosts())),
+ validator());
+opt_type(auth_cache_life_time) ->
+ econf:pos_int(infinity);
+opt_type(auth_cache_missed) ->
+ econf:bool();
+opt_type(auth_cache_size) ->
+ econf:pos_int(infinity);
+opt_type(auth_method) ->
+ econf:list_or_single(econf:db_type(ejabberd_auth));
+opt_type(auth_password_format) ->
+ econf:enum([plain, scram]);
+opt_type(auth_use_cache) ->
+ econf:bool();
+opt_type(c2s_cafile) ->
+ econf:file();
+opt_type(c2s_ciphers) ->
+ econf:binary();
+opt_type(c2s_dhfile) ->
+ econf:file();
+opt_type(c2s_protocol_options) ->
+ econf:and_then(
+ econf:list(econf:binary(), [unique]),
+ fun concat_tls_protocol_options/1);
+opt_type(c2s_tls_compression) ->
+ econf:bool();
+opt_type(ca_file) ->
+ econf:pem();
+opt_type(cache_life_time) ->
+ econf:pos_int(infinity);
+opt_type(cache_missed) ->
+ econf:bool();
+opt_type(cache_size) ->
+ econf:pos_int(infinity);
+opt_type(captcha_cmd) ->
+ econf:file();
+opt_type(captcha_host) ->
+ econf:binary();
+opt_type(captcha_limit) ->
+ econf:pos_int(infinity);
+opt_type(certfiles) ->
+ econf:list(econf:binary());
+opt_type(cluster_backend) ->
+ econf:db_type(ejabberd_cluster);
+opt_type(cluster_nodes) ->
+ econf:list(econf:atom(), [unique]);
+opt_type(default_db) ->
+ econf:enum([mnesia, riak, sql]);
+opt_type(default_ram_db) ->
+ econf:enum([mnesia, riak, sql, redis]);
+opt_type(define_macro) ->
+ econf:any();
+opt_type(disable_sasl_mechanisms) ->
+ econf:list_or_single(
+ econf:and_then(
+ econf:binary(),
+ fun str:to_upper/1));
+opt_type(domain_balancing) ->
+ econf:map(
+ econf:domain(),
+ econf:options(
+ #{component_number => econf:int(2, 1000),
+ type => econf:enum([random, source, destination,
+ bare_source, bare_destination])},
+ [{required, [component_number]}, {return, map}, unique]),
+ [{return, map}]);
+opt_type(ext_api_path_oauth) ->
+ econf:binary();
+opt_type(ext_api_http_pool_size) ->
+ econf:pos_int();
+opt_type(ext_api_url) ->
+ econf:url();
+opt_type(ext_api_headers) ->
+ econf:binary();
+opt_type(extauth_pool_name) ->
+ econf:binary();
+opt_type(extauth_pool_size) ->
+ econf:pos_int();
+opt_type(extauth_program) ->
+ econf:string();
+opt_type(fqdn) ->
+ econf:list_or_single(econf:domain());
+opt_type(hide_sensitive_log_data) ->
+ econf:bool();
+opt_type(host_config) ->
+ econf:map(
+ econf:and_then(
+ econf:domain(),
+ econf:enum(ejabberd_option:hosts())),
+ validator());
+opt_type(hosts) ->
+ econf:non_empty(econf:list(econf:domain(), [unique]));
+opt_type(include_config_file) ->
+ econf:any();
+opt_type(language) ->
+ econf:lang();
+opt_type(ldap_backups) ->
+ econf:list(econf:domain(), [unique]);
+opt_type(ldap_base) ->
+ econf:binary();
+opt_type(ldap_deref_aliases) ->
+ econf:enum([never, searching, finding, always]);
+opt_type(ldap_dn_filter) ->
+ econf:and_then(
+ econf:non_empty(
+ econf:map(
+ econf:ldap_filter(),
+ econf:list(econf:binary()))),
+ fun hd/1);
+opt_type(ldap_encrypt) ->
+ econf:enum([tls, starttls, none]);
+opt_type(ldap_filter) ->
+ econf:ldap_filter();
+opt_type(ldap_password) ->
+ econf:binary();
+opt_type(ldap_port) ->
+ econf:port();
+opt_type(ldap_rootdn) ->
+ econf:binary();
+opt_type(ldap_servers) ->
+ econf:list(econf:domain(), [unique]);
+opt_type(ldap_tls_cacertfile) ->
+ econf:pem();
+opt_type(ldap_tls_certfile) ->
+ econf:pem();
+opt_type(ldap_tls_depth) ->
+ econf:non_neg_int();
+opt_type(ldap_tls_verify) ->
+ econf:enum([hard, soft, false]);
+opt_type(ldap_uids) ->
+ econf:either(
+ econf:list(
+ econf:and_then(
+ econf:binary(),
+ fun(U) -> {U, <<"%u">>} end)),
+ econf:map(econf:binary(), econf:binary(), [unique]));
+opt_type(listen) ->
+ ejabberd_listener:validator();
+opt_type(log_rate_limit) ->
+ econf:non_neg_int();
+opt_type(log_rotate_count) ->
+ econf:non_neg_int();
+opt_type(log_rotate_date) ->
+ econf:string("^(\\$((D(([0-9])|(1[0-9])|(2[0-3])))|"
+ "(((W[0-6])|(M(([1-2][0-9])|(3[0-1])|([1-9]))))"
+ "(D(([0-9])|(1[0-9])|(2[0-3])))?)))?$");
+opt_type(log_rotate_size) ->
+ econf:non_neg_int();
+opt_type(loglevel) ->
+ econf:int(0, 5);
+opt_type(max_fsm_queue) ->
+ econf:pos_int();
+opt_type(modules) ->
+ econf:map(econf:atom(), econf:any());
+opt_type(negotiation_timeout) ->
+ econf:timeout(second);
+opt_type(net_ticktime) ->
+ econf:pos_int();
+opt_type(new_sql_schema) ->
+ econf:bool();
+opt_type(oauth_access) ->
+ econf:acl();
+opt_type(oauth_cache_life_time) ->
+ econf:pos_int(infinity);
+opt_type(oauth_cache_missed) ->
+ econf:bool();
+opt_type(oauth_cache_size) ->
+ econf:pos_int(infinity);
+opt_type(oauth_db_type) ->
+ econf:db_type(ejabberd_oauth);
+opt_type(oauth_expire) ->
+ econf:non_neg_int();
+opt_type(oauth_use_cache) ->
+ econf:bool();
+opt_type(oom_killer) ->
+ econf:bool();
+opt_type(oom_queue) ->
+ econf:pos_int();
+opt_type(oom_watermark) ->
+ econf:int(1, 99);
+opt_type(outgoing_s2s_families) ->
+ econf:and_then(
+ econf:non_empty(
+ econf:list(econf:enum([ipv4, ipv6]), [unique])),
+ fun(L) ->
+ lists:map(
+ fun(ipv4) -> inet;
+ (ipv6) -> inet6
+ end, L)
+ end);
+opt_type(outgoing_s2s_port) ->
+ econf:port();
+opt_type(outgoing_s2s_timeout) ->
+ econf:timeout(second, infinity);
+opt_type(pam_service) ->
+ econf:binary();
+opt_type(pam_userinfotype) ->
+ econf:enum([username, jid]);
+opt_type(pgsql_users_number_estimate) ->
+ econf:bool();
+opt_type(queue_dir) ->
+ econf:directory(write);
+opt_type(queue_type) ->
+ econf:enum([ram, file]);
+opt_type(redis_connect_timeout) ->
+ econf:timeout(second);
+opt_type(redis_db) ->
+ econf:non_neg_int();
+opt_type(redis_password) ->
+ econf:string();
+opt_type(redis_pool_size) ->
+ econf:pos_int();
+opt_type(redis_port) ->
+ econf:port();
+opt_type(redis_queue_type) ->
+ econf:enum([ram, file]);
+opt_type(redis_server) ->
+ econf:string();
+opt_type(registration_timeout) ->
+ econf:pos_int(infinity);
+opt_type(resource_conflict) ->
+ econf:enum([setresource, closeold, closenew, acceptnew]);
+opt_type(riak_cacertfile) ->
+ econf:and_then(econf:pem(), econf:string());
+opt_type(riak_password) ->
+ econf:string();
+opt_type(riak_pool_size) ->
+ econf:pos_int();
+opt_type(riak_port) ->
+ econf:port();
+opt_type(riak_server) ->
+ econf:string();
+opt_type(riak_start_interval) ->
+ econf:timeout(second);
+opt_type(riak_username) ->
+ econf:string();
+opt_type(route_subdomains) ->
+ econf:enum([s2s, local]);
+opt_type(router_cache_life_time) ->
+ econf:pos_int(infinity);
+opt_type(router_cache_missed) ->
+ econf:bool();
+opt_type(router_cache_size) ->
+ econf:pos_int(infinity);
+opt_type(router_db_type) ->
+ econf:db_type(ejabberd_router);
+opt_type(router_use_cache) ->
+ econf:bool();
+opt_type(rpc_timeout) ->
+ econf:timeout(second);
+opt_type(s2s_access) ->
+ econf:acl();
+opt_type(s2s_cafile) ->
+ econf:pem();
+opt_type(s2s_ciphers) ->
+ econf:binary();
+opt_type(s2s_dhfile) ->
+ econf:file();
+opt_type(s2s_dns_retries) ->
+ econf:non_neg_int();
+opt_type(s2s_dns_timeout) ->
+ econf:timeout(second, infinity);
+opt_type(s2s_max_retry_delay) ->
+ econf:pos_int();
+opt_type(s2s_protocol_options) ->
+ econf:and_then(
+ econf:list(econf:binary(), [unique]),
+ fun concat_tls_protocol_options/1);
+opt_type(s2s_queue_type) ->
+ econf:enum([ram, file]);
+opt_type(s2s_timeout) ->
+ econf:timeout(second, infinity);
+opt_type(s2s_tls_compression) ->
+ econf:bool();
+opt_type(s2s_use_starttls) ->
+ econf:either(
+ econf:bool(),
+ econf:enum([optional, required]));
+opt_type(s2s_zlib) ->
+ econf:and_then(
+ econf:bool(),
+ fun(false) -> false;
+ (true) ->
+ ejabberd:start_app(ezlib),
+ true
+ end);
+opt_type(shaper) ->
+ ejabberd_shaper:validator(shaper);
+opt_type(shaper_rules) ->
+ ejabberd_shaper:validator(shaper_rules);
+opt_type(sm_cache_life_time) ->
+ econf:pos_int(infinity);
+opt_type(sm_cache_missed) ->
+ econf:bool();
+opt_type(sm_cache_size) ->
+ econf:pos_int(infinity);
+opt_type(sm_db_type) ->
+ econf:db_type(ejabberd_sm);
+opt_type(sm_use_cache) ->
+ econf:bool();
+opt_type(sql_connect_timeout) ->
+ econf:timeout(second);
+opt_type(sql_database) ->
+ econf:binary();
+opt_type(sql_keepalive_interval) ->
+ econf:timeout(second);
+opt_type(sql_password) ->
+ econf:binary();
+opt_type(sql_pool_size) ->
+ econf:pos_int();
+opt_type(sql_port) ->
+ econf:port();
+opt_type(sql_query_timeout) ->
+ econf:timeout(second);
+opt_type(sql_queue_type) ->
+ econf:enum([ram, file]);
+opt_type(sql_server) ->
+ econf:binary();
+opt_type(sql_ssl) ->
+ econf:bool();
+opt_type(sql_ssl_cafile) ->
+ econf:pem();
+opt_type(sql_ssl_certfile) ->
+ econf:pem();
+opt_type(sql_ssl_verify) ->
+ econf:bool();
+opt_type(sql_start_interval) ->
+ econf:timeout(second);
+opt_type(sql_type) ->
+ econf:enum([mysql, pgsql, sqlite, mssql, odbc]);
+opt_type(sql_username) ->
+ econf:binary();
+opt_type(trusted_proxies) ->
+ econf:either(all, econf:list(econf:ip_mask()));
+opt_type(use_cache) ->
+ econf:bool();
+opt_type(validate_stream) ->
+ econf:bool();
+opt_type(version) ->
+ econf:binary();
+opt_type(websocket_origin) ->
+ econf:list(
+ econf:and_then(
+ econf:and_then(
+ econf:binary_sep("\\s+"),
+ econf:list(econf:url(), [unique])),
+ fun(L) -> str:join(L, <<" ">>) end),
+ [unique]);
+opt_type(websocket_ping_interval) ->
+ econf:timeout(second);
+opt_type(websocket_timeout) ->
+ econf:timeout(second).
+
+%% We only define the types of options that cannot be derived
+%% automatically by tools/opt_type.sh script
+-spec options() -> [{s2s_protocol_options, undefined | binary()} |
+ {c2s_protocol_options, undefined | binary()} |
+ {websocket_origin, [binary()]} |
+ {disable_sasl_mechanisms, [binary()]} |
+ {s2s_zlib, boolean()} |
+ {listen, [ejabberd_listener:listener()]} |
+ {modules, [{module(), gen_mod:opts(), integer()}]} |
+ {ldap_uids, [{binary(), binary()}]} |
+ {ldap_dn_filter, {binary(), [binary()]}} |
+ {outgoing_s2s_families, [inet | inet6, ...]} |
+ {acl, [{atom(), [acl:acl_rule()]}]} |
+ {access_rules, [{atom(), acl:access()}]} |
+ {shaper, #{atom() => ejabberd_shaper:shaper_rate()}} |
+ {shaper_rules, [{atom(), [ejabberd_shaper:shaper_rule()]}]} |
+ {api_permissions, [ejabberd_access_permissions:permission()]} |
+ {append_host_config, [{binary(), any()}]} |
+ {host_config, [{binary(), any()}]} |
+ {define_macro, any()} |
+ {include_config_file, any()} |
+ {atom(), any()}].
+options() ->
+ [%% Top-priority options
+ hosts,
+ {loglevel, 4},
+ {cache_life_time, 3600},
+ {cache_missed, true},
+ {cache_size, 1000},
+ {use_cache, true},
+ {default_db, mnesia},
+ {default_ram_db, mnesia},
+ {queue_type, ram},
+ %% Other options
+ {acl, []},
+ {access_rules, []},
+ {acme, #{}},
+ {allow_contrib_modules, true},
+ {allow_multiple_connections, false},
+ {anonymous_protocol, sasl_anon},
+ {api_permissions,
+ [{<<"admin access">>,
+ {[],
+ [{acl, admin},
+ {oauth, {[<<"ejabberd:admin">>], [{acl, admin}]}}],
+ {all, [start, stop]}}}]},
+ {append_host_config, []},
+ {auth_cache_life_time,
+ fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
+ {auth_cache_missed,
+ fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
+ {auth_cache_size,
+ fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
+ {auth_method,
+ fun(Host) -> [ejabberd_config:default_db(Host, ejabberd_auth)] end},
+ {auth_password_format, plain},
+ {auth_use_cache,
+ fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
+ {c2s_cafile, undefined},
+ {c2s_ciphers, undefined},
+ {c2s_dhfile, undefined},
+ {c2s_protocol_options, undefined},
+ {c2s_tls_compression, undefined},
+ {ca_file, iolist_to_binary(pkix:get_cafile())},
+ {captcha_cmd, undefined},
+ {captcha_host, <<"">>},
+ {captcha_limit, infinity},
+ {certfiles, undefined},
+ {cluster_backend, mnesia},
+ {cluster_nodes, []},
+ {define_macro, []},
+ {disable_sasl_mechanisms, []},
+ {domain_balancing, #{}},
+ {ext_api_headers, <<>>},
+ {ext_api_http_pool_size, 100},
+ {ext_api_path_oauth, <<"/oauth">>},
+ {ext_api_url, <<"http://localhost/api">>},
+ {extauth_pool_name, undefined},
+ {extauth_pool_size, undefined},
+ {extauth_program, undefined},
+ {fqdn, fun fqdn/1},
+ {hide_sensitive_log_data, false},
+ {host_config, []},
+ {include_config_file, []},
+ {language, <<"en">>},
+ {ldap_backups, []},
+ {ldap_base, <<"">>},
+ {ldap_deref_aliases, never},
+ {ldap_dn_filter, {undefined, []}},
+ {ldap_encrypt, none},
+ {ldap_filter, <<"">>},
+ {ldap_password, <<"">>},
+ {ldap_port,
+ fun(Host) ->
+ case ejabberd_option:ldap_encrypt(Host) of
+ tls -> 636;
+ _ -> 389
+ end
+ end},
+ {ldap_rootdn, <<"">>},
+ {ldap_servers, [<<"localhost">>]},
+ {ldap_tls_cacertfile, undefined},
+ {ldap_tls_certfile, undefined},
+ {ldap_tls_depth, undefined},
+ {ldap_tls_verify, false},
+ {ldap_uids, [{<<"uid">>, <<"%u">>}]},
+ {listen, []},
+ {log_rate_limit, undefined},
+ {log_rotate_count, undefined},
+ {log_rotate_date, undefined},
+ {log_rotate_size, undefined},
+ {max_fsm_queue, undefined},
+ {modules, []},
+ {negotiation_timeout, timer:seconds(30)},
+ {net_ticktime, 60},
+ {new_sql_schema, ?USE_NEW_SQL_SCHEMA_DEFAULT},
+ {oauth_access, none},
+ {oauth_cache_life_time,
+ fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
+ {oauth_cache_missed,
+ fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
+ {oauth_cache_size,
+ fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
+ {oauth_db_type,
+ fun(Host) -> ejabberd_config:default_db(Host, ejabberd_oauth) end},
+ {oauth_expire, 4294967},
+ {oauth_use_cache,
+ fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
+ {oom_killer, true},
+ {oom_queue, 10000},
+ {oom_watermark, 80},
+ {outgoing_s2s_families, [inet, inet6]},
+ {outgoing_s2s_port, 5269},
+ {outgoing_s2s_timeout, timer:seconds(10)},
+ {pam_service, <<"ejabberd">>},
+ {pam_userinfotype, username},
+ {pgsql_users_number_estimate, false},
+ {queue_dir, undefined},
+ {redis_connect_timeout, timer:seconds(1)},
+ {redis_db, 0},
+ {redis_password, ""},
+ {redis_pool_size, 10},
+ {redis_port, 6379},
+ {redis_queue_type,
+ fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
+ {redis_server, "localhost"},
+ {registration_timeout, 600},
+ {resource_conflict, acceptnew},
+ {riak_cacertfile, nil},
+ {riak_password, nil},
+ {riak_pool_size, 10},
+ {riak_port, 8087},
+ {riak_server, "127.0.0.1"},
+ {riak_start_interval, timer:seconds(30)},
+ {riak_username, nil},
+ {route_subdomains, local},
+ {router_cache_life_time,
+ fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
+ {router_cache_missed,
+ fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
+ {router_cache_size,
+ fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
+ {router_db_type,
+ fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_router) end},
+ {router_use_cache,
+ fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
+ {rpc_timeout, timer:seconds(5)},
+ {s2s_access, all},
+ {s2s_cafile, undefined},
+ {s2s_ciphers, undefined},
+ {s2s_dhfile, undefined},
+ {s2s_dns_retries, 2},
+ {s2s_dns_timeout, timer:seconds(10)},
+ {s2s_max_retry_delay, 300},
+ {s2s_protocol_options, undefined},
+ {s2s_queue_type,
+ fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
+ {s2s_timeout, timer:minutes(10)},
+ {s2s_tls_compression, undefined},
+ {s2s_use_starttls, false},
+ {s2s_zlib, false},
+ {shaper, #{}},
+ {shaper_rules, []},
+ {sm_cache_life_time,
+ fun(Host) -> ejabberd_config:get_option({cache_life_time, Host}) end},
+ {sm_cache_missed,
+ fun(Host) -> ejabberd_config:get_option({cache_missed, Host}) end},
+ {sm_cache_size,
+ fun(Host) -> ejabberd_config:get_option({cache_size, Host}) end},
+ {sm_db_type,
+ fun(Host) -> ejabberd_config:default_ram_db(Host, ejabberd_sm) end},
+ {sm_use_cache,
+ fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
+ {sql_type, undefined},
+ {sql_connect_timeout, timer:seconds(5)},
+ {sql_database, undefined},
+ {sql_keepalive_interval, undefined},
+ {sql_password, <<"">>},
+ {sql_pool_size,
+ fun(Host) ->
+ case ejabberd_config:get_option({sql_type, Host}) of
+ sqlite -> 1;
+ _ -> 10
+ end
+ end},
+ {sql_port,
+ fun(Host) ->
+ case ejabberd_config:get_option({sql_type, Host}) of
+ mssql -> 1433;
+ mysql -> 3306;
+ pgsql -> 5432;
+ _ -> undefined
+ end
+ end},
+ {sql_query_timeout, timer:seconds(60)},
+ {sql_queue_type,
+ fun(Host) -> ejabberd_config:get_option({queue_type, Host}) end},
+ {sql_server, <<"localhost">>},
+ {sql_ssl, false},
+ {sql_ssl_cafile, undefined},
+ {sql_ssl_certfile, undefined},
+ {sql_ssl_verify, false},
+ {sql_start_interval, timer:seconds(30)},
+ {sql_username, <<"ejabberd">>},
+ {trusted_proxies, []},
+ {validate_stream, false},
+ {version, fun version/1},
+ {websocket_origin, []},
+ {websocket_ping_interval, timer:seconds(60)},
+ {websocket_timeout, timer:minutes(5)}].
+
+-spec globals() -> [atom()].
+globals() ->
+ [acme,
+ allow_contrib_modules,
+ api_permissions,
+ append_host_config,
+ auth_cache_life_time,
+ auth_cache_missed,
+ auth_cache_size,
+ ca_file,
+ captcha_cmd,
+ captcha_host,
+ captcha_limit,
+ certfiles,
+ cluster_backend,
+ cluster_nodes,
+ domain_balancing,
+ ext_api_path_oauth,
+ fqdn,
+ hosts,
+ host_config,
+ listen,
+ loglevel,
+ log_rate_limit,
+ log_rotate_count,
+ log_rotate_date,
+ log_rotate_size,
+ negotiation_timeout,
+ net_ticktime,
+ new_sql_schema,
+ node_start,
+ oauth_cache_life_time,
+ oauth_cache_missed,
+ oauth_cache_size,
+ oauth_db_type,
+ oauth_expire,
+ oauth_use_cache,
+ oom_killer,
+ oom_queue,
+ oom_watermark,
+ queue_dir,
+ redis_connect_timeout,
+ redis_db,
+ redis_password,
+ redis_pool_size,
+ redis_port,
+ redis_queue_type,
+ redis_server,
+ registration_timeout,
+ riak_cacertfile,
+ riak_password,
+ riak_pool_size,
+ riak_port,
+ riak_server,
+ riak_start_interval,
+ riak_username,
+ router_cache_life_time,
+ router_cache_missed,
+ router_cache_size,
+ router_db_type,
+ router_use_cache,
+ rpc_timeout,
+ s2s_max_retry_delay,
+ shaper,
+ sm_cache_life_time,
+ sm_cache_missed,
+ sm_cache_size,
+ version,
+ websocket_origin,
+ websocket_ping_interval,
+ websocket_timeout].
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+-spec validator() -> econf:validator().
+validator() ->
+ Disallowed = ejabberd_config:globals(),
+ {Validators, Required} = ejabberd_config:validators(Disallowed),
+ econf:options(
+ Validators,
+ [{disallowed, Required ++ Disallowed}, unique]).
+
+-spec fqdn(global | binary()) -> [binary()].
+fqdn(global) ->
+ {ok, Hostname} = inet:gethostname(),
+ case inet:gethostbyname(Hostname) of
+ {ok, #hostent{h_name = FQDN}} ->
+ case jid:nameprep(iolist_to_binary(FQDN)) of
+ error -> [];
+ Domain -> [Domain]
+ end;
+ {error, _} ->
+ []
+ end;
+fqdn(_) ->
+ ejabberd_config:get_option(fqdn).
+
+-spec version(global | binary()) -> binary().
+version(global) ->
+ case application:get_env(ejabberd, custom_vsn) of
+ {ok, Vsn0} when is_list(Vsn0) ->
+ list_to_binary(Vsn0);
+ {ok, Vsn1} when is_binary(Vsn1) ->
+ Vsn1;
+ _ ->
+ case application:get_key(ejabberd, vsn) of
+ undefined -> <<"">>;
+ {ok, Vsn} -> list_to_binary(Vsn)
+ end
+ end;
+version(_) ->
+ ejabberd_config:get_option(version).
+
+-spec concat_tls_protocol_options([binary()]) -> binary().
+concat_tls_protocol_options(Opts) ->
+ str:join(Opts, <<"|">>).
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 02b79ed4d..68e5119a9 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -92,7 +92,7 @@ import_file(FileName, State) ->
-spec export_server(binary()) -> any().
export_server(Dir) ->
- export_hosts(ejabberd_config:get_myhosts(), Dir).
+ export_hosts(ejabberd_option:hosts(), Dir).
-spec export_host(binary(), binary()) -> any().
export_host(Dir, Host) ->
diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl
index 39b69d033..fc5c5379a 100644
--- a/src/ejabberd_pkix.erl
+++ b/src/ejabberd_pkix.erl
@@ -22,11 +22,10 @@
%%%-------------------------------------------------------------------
-module(ejabberd_pkix).
-behaviour(gen_server).
--behaviour(ejabberd_config).
%% API
--export([start_link/0, opt_type/1]).
--export([certs_dir/0, ca_file/0]).
+-export([start_link/0]).
+-export([certs_dir/0]).
-export([add_certfile/1, try_certfile/1, get_certfile/0, get_certfile/1]).
%% Hooks
-export([ejabberd_started/0, config_reloaded/0]).
@@ -99,11 +98,7 @@ get_certfile() ->
Ret -> {ok, select_certfile(Ret)}
end.
--spec ca_file() -> filename() | undefined.
-ca_file() ->
- ejabberd_config:get_option(ca_file).
-
--spec certs_dir() -> file:dirname_all().
+-spec certs_dir() -> file:filename_all().
certs_dir() ->
MnesiaDir = mnesia:system_info(directory),
filename:join(MnesiaDir, "certs").
@@ -116,24 +111,6 @@ ejabberd_started() ->
config_reloaded() ->
gen_server:call(?MODULE, config_reloaded, ?CALL_TIMEOUT).
-opt_type(ca_path) ->
- fun(_) ->
- ?WARNING_MSG("Option 'ca_path' has no effect anymore, "
- "use 'ca_file' instead", []),
- undefined
- end;
-opt_type(ca_file) ->
- fun try_certfile/1;
-opt_type(certfiles) ->
- fun(Paths) -> [iolist_to_binary(Path) || Path <- Paths] end;
-opt_type(O) when O == c2s_certfile; O == s2s_certfile; O == domain_certfile ->
- fun(Path) ->
- ?WARNING_MSG("Option '~s' is deprecated, use 'certfiles' instead", [O]),
- prep_path(Path)
- end;
-opt_type(_) ->
- [ca_path, ca_file, certfiles, c2s_certfile, s2s_certfile, domain_certfile].
-
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
@@ -177,7 +154,7 @@ handle_call(config_reloaded, _From, State) ->
Old = State#state.files,
New = get_certfiles_from_config_options(),
del_files(sets:subtract(Old, New)),
- add_files(New),
+ _ = add_files(New),
case commit() of
{ok, _} ->
check_domain_certfiles(),
@@ -258,10 +235,9 @@ del_files(Files) ->
-spec commit() -> {ok, [{filename(), pkix:error_reason()}]} | error.
commit() ->
- Opts = case ca_file() of
- undefined -> [];
- CAFile -> [{cafile, CAFile}]
- end,
+ CAFile = ejabberd_option:ca_file(),
+ ?DEBUG("Using CA root certificates from: ~s", [CAFile]),
+ Opts = [{cafile, CAFile}],
case pkix:commit(certs_dir(), Opts) of
{ok, Errors, Warnings, CAError} ->
log_errors(Errors),
@@ -277,35 +253,36 @@ commit() ->
-spec check_domain_certfiles() -> ok.
check_domain_certfiles() ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
Routes = ejabberd_router:get_all_routes(),
check_domain_certfiles(Hosts ++ Routes).
-spec check_domain_certfiles([binary()]) -> ok.
check_domain_certfiles(Hosts) ->
- lists:foreach(
- fun(Host) ->
- case get_certfile_no_default(Host) of
- error ->
- ?WARNING_MSG("No certificate found matching '~s': strictly "
- "configured clients or servers will reject "
- "connections with this host; obtain "
- "a certificate for this (sub)domain from any "
- "trusted CA such as Let's Encrypt "
- "(www.letsencrypt.org)",
- [Host]);
- _ ->
- ok
- end
- end, Hosts).
-
--spec deprecated_options() -> [atom()].
-deprecated_options() ->
- [c2s_certfile, s2s_certfile, domain_certfile].
-
--spec global_certfiles() -> sets:set(filename()).
-global_certfiles() ->
- case ejabberd_config:get_option(certfiles) of
+ case ejabberd_listener:tls_listeners() of
+ [] -> ok;
+ _ ->
+ lists:foreach(
+ fun(Host) ->
+ case get_certfile_no_default(Host) of
+ error ->
+ ?WARNING_MSG(
+ "No certificate found matching '~s': strictly "
+ "configured clients or servers will reject "
+ "connections with this host; obtain "
+ "a certificate for this (sub)domain from any "
+ "trusted CA such as Let's Encrypt "
+ "(www.letsencrypt.org)",
+ [Host]);
+ _ ->
+ ok
+ end
+ end, Hosts)
+ end.
+
+-spec get_certfiles_from_config_options() -> sets:set(filename()).
+get_certfiles_from_config_options() ->
+ case ejabberd_option:certfiles() of
undefined ->
sets:new();
Paths ->
@@ -316,25 +293,6 @@ global_certfiles() ->
end, sets:new(), Paths)
end.
--spec local_certfiles() -> sets:set(filename()).
-local_certfiles() ->
- Opts = [{Opt, Host} || Opt <- deprecated_options(),
- Host <- ejabberd_config:get_myhosts()],
- lists:foldl(
- fun(OptHost, Acc) ->
- case ejabberd_config:get_option(OptHost) of
- undefined -> Acc;
- Path -> sets:add_element(Path, Acc)
- end
- end, sets:new(), Opts).
-
--spec get_certfiles_from_config_options() -> sets:set(filename()).
-get_certfiles_from_config_options() ->
- Global = global_certfiles(),
- Local = local_certfiles(),
- Listen = sets:from_list(ejabberd_listener:get_certfiles()),
- sets:union([Global, Local, Listen]).
-
-spec prep_path(file:filename_all()) -> filename().
prep_path(Path0) ->
case filename:pathtype(Path0) of
diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl
index c6f5536d7..3acb044f0 100644
--- a/src/ejabberd_rdbms.erl
+++ b/src/ejabberd_rdbms.erl
@@ -26,11 +26,10 @@
-module(ejabberd_rdbms).
-behaviour(supervisor).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
--export([start_link/0, init/1, opt_type/1,
+-export([start_link/0, init/1,
config_reloaded/0, start_host/1, stop_host/1]).
-include("logger.hrl").
@@ -55,7 +54,7 @@ get_specs() ->
{ok, Spec} -> [Spec];
undefined -> []
end
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec get_spec(binary()) -> {ok, supervisor:child_spec()} | undefined.
get_spec(Host) ->
@@ -71,7 +70,7 @@ get_spec(Host) ->
-spec config_reloaded() -> ok.
config_reloaded() ->
- lists:foreach(fun reload_host/1, ejabberd_config:get_myhosts()).
+ lists:foreach(fun reload_host/1, ejabberd_option:hosts()).
-spec start_host(binary()) -> ok.
start_host(Host) ->
@@ -89,12 +88,13 @@ start_host(Host) ->
ok
end.
--spec stop_host(binary()) -> ok.
+-spec stop_host(binary()) -> ok | {error, atom()}.
stop_host(Host) ->
SupName = gen_mod:get_module_proc(Host, ejabberd_sql_sup),
- supervisor:terminate_child(?MODULE, SupName),
- supervisor:delete_child(?MODULE, SupName),
- ok.
+ case supervisor:terminate_child(?MODULE, SupName) of
+ ok -> supervisor:delete_child(?MODULE, SupName);
+ Err -> Err
+ end.
-spec reload_host(binary()) -> ok.
reload_host(Host) ->
@@ -106,7 +106,7 @@ reload_host(Host) ->
%% Returns {true, App} if we have configured sql for the given host
needs_sql(Host) ->
LHost = jid:nameprep(Host),
- case ejabberd_config:get_option({sql_type, LHost}, undefined) of
+ case ejabberd_option:sql_type(LHost) of
mysql -> {true, p1_mysql};
pgsql -> {true, p1_pgsql};
sqlite -> {true, sqlite3};
@@ -114,13 +114,3 @@ needs_sql(Host) ->
odbc -> {true, odbc};
undefined -> false
end.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(sql_type) ->
- fun (mysql) -> mysql;
- (pgsql) -> pgsql;
- (sqlite) -> sqlite;
- (mssql) -> mssql;
- (odbc) -> odbc
- end;
-opt_type(_) -> [sql_type].
diff --git a/src/ejabberd_redis.erl b/src/ejabberd_redis.erl
index 6284238a2..55e9dfcff 100644
--- a/src/ejabberd_redis.erl
+++ b/src/ejabberd_redis.erl
@@ -59,8 +59,8 @@
-type error_reason() :: binary() | timeout | disconnected | overloaded.
-type redis_error() :: {error, error_reason()}.
--type redis_reply() :: binary() | [binary()].
--type redis_command() :: [binary()].
+-type redis_reply() :: undefined | binary() | [binary()].
+-type redis_command() :: [iodata() | integer()].
-type redis_pipeline() :: [redis_command()].
-type redis_info() :: server | clients | memory | persistence |
stats | replication | cpu | commandstats |
@@ -89,19 +89,18 @@ get_connection(I) ->
q(Command) ->
call(get_rnd_id(), {q, Command}, ?MAX_RETRIES).
--spec qp(redis_pipeline()) -> {ok, [redis_reply()]} | redis_error().
+-spec qp(redis_pipeline()) -> [{ok, redis_reply()} | redis_error()] | redis_error().
qp(Pipeline) ->
call(get_rnd_id(), {qp, Pipeline}, ?MAX_RETRIES).
--spec multi(fun(() -> any())) -> {ok, [redis_reply()]} | redis_error().
+-spec multi(fun(() -> any())) -> {ok, redis_reply()} | redis_error().
multi(F) ->
case erlang:get(?TR_STACK) of
undefined ->
erlang:put(?TR_STACK, []),
try F() of
_ ->
- Stack = erlang:get(?TR_STACK),
- erlang:erase(?TR_STACK),
+ Stack = erlang:erase(?TR_STACK),
Command = [["MULTI"]|lists:reverse([["EXEC"]|Stack])],
case qp(Command) of
{error, _} = Err -> Err;
@@ -298,7 +297,7 @@ hkeys(Key) ->
-spec subscribe([binary()]) -> ok | redis_error().
subscribe(Channels) ->
- try ?GEN_SERVER:call(get_proc(1), {subscribe, self(), Channels}, ?CALL_TIMEOUT)
+ try gen_server_call(get_proc(1), {subscribe, self(), Channels})
catch exit:{Why, {?GEN_SERVER, call, _}} ->
Reason = case Why of
timeout -> timeout;
@@ -329,7 +328,7 @@ script_load(Data) ->
erlang:error(transaction_unsupported)
end.
--spec evalsha(binary(), [iodata()], [iodata()]) -> {ok, binary()} | redis_error().
+-spec evalsha(binary(), [iodata()], [iodata() | integer()]) -> {ok, binary()} | redis_error().
evalsha(SHA, Keys, Args) ->
case erlang:get(?TR_STACK) of
undefined ->
@@ -458,13 +457,11 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
-spec connect(state()) -> {ok, pid()} | {error, any()}.
connect(#state{num = Num}) ->
- Server = ejabberd_config:get_option(redis_server, "localhost"),
- Port = ejabberd_config:get_option(redis_port, 6379),
- DB = ejabberd_config:get_option(redis_db, 0),
- Pass = ejabberd_config:get_option(redis_password, ""),
- ConnTimeout = timer:seconds(
- ejabberd_config:get_option(
- redis_connect_timeout, 1)),
+ Server = ejabberd_option:redis_server(),
+ Port = ejabberd_option:redis_port(),
+ DB = ejabberd_option:redis_db(),
+ Pass = ejabberd_option:redis_password(),
+ ConnTimeout = ejabberd_option:redis_connect_timeout(),
try case do_connect(Num, Server, Port, Pass, DB, ConnTimeout) of
{ok, Client} ->
?DEBUG("Connection #~p established to Redis at ~s:~p",
@@ -498,7 +495,7 @@ do_connect(_, Server, Port, Pass, DB, ConnTimeout) ->
-spec call(pos_integer(), {q, redis_command()}, integer()) ->
{ok, redis_reply()} | redis_error();
(pos_integer(), {qp, redis_pipeline()}, integer()) ->
- {ok, [redis_reply()]} | redis_error().
+ [{ok, redis_reply()} | redis_error()] | redis_error().
call(I, {F, Cmd}, Retries) ->
?DEBUG("redis query: ~p", [Cmd]),
Conn = get_connection(I),
@@ -513,7 +510,7 @@ call(I, {F, Cmd}, Retries) ->
end,
case Res of
{error, disconnected} when Retries > 0 ->
- try ?GEN_SERVER:call(get_proc(I), connect, ?CALL_TIMEOUT) of
+ try gen_server_call(get_proc(I), connect) of
ok -> call(I, {F, Cmd}, Retries-1);
{error, _} = Err -> Err
catch exit:{Why, {?GEN_SERVER, call, _}} ->
@@ -531,6 +528,14 @@ call(I, {F, Cmd}, Retries) ->
Res
end.
+gen_server_call(Proc, Msg) ->
+ case ejabberd_redis_sup:start() of
+ ok ->
+ ?GEN_SERVER:call(Proc, Msg, ?CALL_TIMEOUT);
+ {error, _} ->
+ {error, disconnected}
+ end.
+
-spec log_error(redis_command() | redis_pipeline(), atom() | binary()) -> ok.
log_error(Cmd, Reason) ->
?ERROR_MSG("Redis request has failed:~n"
@@ -542,8 +547,8 @@ log_error(Cmd, Reason) ->
get_rnd_id() ->
p1_rand:round_robin(ejabberd_redis_sup:get_pool_size() - 1) + 2.
--spec get_result([{error, atom() | binary()} | {ok, iodata()}]) ->
- {ok, [redis_reply()]} | {error, binary()}.
+-spec get_result([{ok, redis_reply()} | redis_error()]) ->
+ {ok, redis_reply()} | redis_error().
get_result([{error, _} = Err|_]) ->
Err;
get_result([{ok, _} = OK]) ->
@@ -584,9 +589,7 @@ fsm_limit_opts() ->
ejabberd_config:fsm_limit_opts([]).
get_queue_type() ->
- ejabberd_config:get_option(
- redis_queue_type,
- ejabberd_config:default_queue_type(global)).
+ ejabberd_option:redis_queue_type().
-spec flush_queue(p1_queue:queue()) -> p1_queue:queue().
flush_queue(Q) ->
diff --git a/src/ejabberd_redis_sup.erl b/src/ejabberd_redis_sup.erl
index f34a96655..3b5d4b7af 100644
--- a/src/ejabberd_redis_sup.erl
+++ b/src/ejabberd_redis_sup.erl
@@ -23,41 +23,41 @@
-module(ejabberd_redis_sup).
-behaviour(supervisor).
--behaviour(ejabberd_config).
%% API
--export([start_link/0, get_pool_size/0,
- host_up/1, config_reloaded/0, opt_type/1]).
+-export([start/0, start_link/0]).
+-export([get_pool_size/0, config_reloaded/0]).
%% Supervisor callbacks
-export([init/1]).
-include("logger.hrl").
--define(DEFAULT_POOL_SIZE, 10).
-
%%%===================================================================
%%% API functions
%%%===================================================================
-start_link() ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, []).
-
-host_up(Host) ->
- case is_redis_configured(Host) of
- true ->
- ejabberd:start_app(eredis),
- lists:foreach(
- fun(Spec) ->
- supervisor:start_child(?MODULE, Spec)
- end, get_specs());
+start() ->
+ case is_started() of
+ true -> ok;
false ->
- ok
+ ejabberd:start_app(eredis),
+ Spec = {?MODULE, {?MODULE, start_link, []},
+ permanent, infinity, supervisor, [?MODULE]},
+ case supervisor:start_child(ejabberd_db_sup, Spec) of
+ {ok, _} -> ok;
+ {error, {already_started, _}} -> ok;
+ {error, Why} = Err ->
+ ?ERROR_MSG("Failed to start ~s: ~p", [?MODULE, Why]),
+ Err
+ end
end.
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
config_reloaded() ->
- case is_redis_configured() of
+ case is_started() of
true ->
- ejabberd:start_app(eredis),
lists:foreach(
fun(Spec) ->
supervisor:start_child(?MODULE, Spec)
@@ -65,17 +65,15 @@ config_reloaded() ->
PoolSize = get_pool_size(),
lists:foreach(
fun({Id, _, _, _}) when Id > PoolSize ->
- supervisor:terminate_child(?MODULE, Id),
- supervisor:delete_child(?MODULE, Id);
+ case supervisor:terminate_child(?MODULE, Id) of
+ ok -> supervisor:delete_child(?MODULE, Id);
+ _ -> ok
+ end;
(_) ->
ok
end, supervisor:which_children(?MODULE));
false ->
- lists:foreach(
- fun({Id, _, _, _}) ->
- supervisor:terminate_child(?MODULE, Id),
- supervisor:delete_child(?MODULE, Id)
- end, supervisor:which_children(?MODULE))
+ ok
end.
%%%===================================================================
@@ -83,36 +81,11 @@ config_reloaded() ->
%%%===================================================================
init([]) ->
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
- ejabberd_hooks:add(host_up, ?MODULE, host_up, 20),
- Specs = case is_redis_configured() of
- true ->
- ejabberd:start_app(eredis),
- get_specs();
- false ->
- []
- end,
- {ok, {{one_for_one, 500, 1}, Specs}}.
+ {ok, {{one_for_one, 500, 1}, get_specs()}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-is_redis_configured() ->
- lists:any(fun is_redis_configured/1, ejabberd_config:get_myhosts()).
-
-is_redis_configured(Host) ->
- ServerConfigured = ejabberd_config:has_option({redis_server, Host}),
- PortConfigured = ejabberd_config:has_option({redis_port, Host}),
- DBConfigured = ejabberd_config:has_option({redis_db, Host}),
- PassConfigured = ejabberd_config:has_option({redis_password, Host}),
- PoolSize = ejabberd_config:has_option({redis_pool_size, Host}),
- ConnTimeoutConfigured = ejabberd_config:has_option(
- {redis_connect_timeout, Host}),
- SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == redis,
- RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == redis,
- ServerConfigured or PortConfigured or DBConfigured or PassConfigured or
- PoolSize or ConnTimeoutConfigured or
- SMConfigured or RouterConfigured.
-
get_specs() ->
lists:map(
fun(I) ->
@@ -121,24 +94,7 @@ get_specs() ->
end, lists:seq(1, get_pool_size())).
get_pool_size() ->
- ejabberd_config:get_option(redis_pool_size, ?DEFAULT_POOL_SIZE) + 1.
-
-iolist_to_list(IOList) ->
- binary_to_list(iolist_to_binary(IOList)).
+ ejabberd_option:redis_pool_size() + 1.
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(redis_connect_timeout) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(redis_db) ->
- fun (I) when is_integer(I), I >= 0 -> I end;
-opt_type(redis_password) -> fun iolist_to_list/1;
-opt_type(redis_port) ->
- fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
-opt_type(redis_server) -> fun iolist_to_list/1;
-opt_type(redis_pool_size) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(redis_queue_type) ->
- fun(ram) -> ram; (file) -> file end;
-opt_type(_) ->
- [redis_connect_timeout, redis_db, redis_password,
- redis_port, redis_pool_size, redis_server, redis_queue_type].
+is_started() ->
+ whereis(?MODULE) /= undefined.
diff --git a/src/ejabberd_regexp.erl b/src/ejabberd_regexp.erl
index c454835a9..9e1a979a4 100644
--- a/src/ejabberd_regexp.erl
+++ b/src/ejabberd_regexp.erl
@@ -125,13 +125,12 @@ sh_to_awk_3(<<"]", Sh/binary>>, false) ->
sh_to_awk_3(<<C:8, Sh/binary>>, UpArrow) ->
[C|sh_to_awk_3(Sh, UpArrow)];
sh_to_awk_3(<<>>, true) ->
- [$^|sh_to_awk_1([])];
+ [$^|sh_to_awk_1(<<>>)];
sh_to_awk_3(<<>>, false) ->
- sh_to_awk_1([]).
+ sh_to_awk_1(<<>>).
-%% -type sh_special_char(char()) -> bool().
%% Test if a character is a special character.
-
+-spec sh_special_char(char()) -> boolean().
sh_special_char($|) -> true;
sh_special_char($*) -> true;
sh_special_char($+) -> true;
@@ -146,4 +145,3 @@ sh_special_char($[) -> true;
sh_special_char($]) -> true;
sh_special_char($") -> true;
sh_special_char(_C) -> false.
-
diff --git a/src/ejabberd_riak.erl b/src/ejabberd_riak.erl
index a86ac06d3..5eadcc8ba 100644
--- a/src/ejabberd_riak.erl
+++ b/src/ejabberd_riak.erl
@@ -181,7 +181,7 @@ get(Table, RecSchema, Key) ->
-spec get_by_index(atom(), record_schema(), binary(), any()) ->
{ok, [any()]} | {error, any()}.
-%% @doc Reads records by `Index' and value `Key' from `Table'
+%% @doc Reads records by `Index' and value `Key' from `Table'
get_by_index(Table, RecSchema, Index, Key) ->
{NewIndex, NewKey} = encode_index_key(Index, Key),
case get_by_index_raw(Table, NewIndex, NewKey) of
@@ -520,8 +520,13 @@ make_invalid_object(Val) ->
(str:format("Invalid object: ~p", [Val])).
get_random_pid() ->
- PoolPid = ejabberd_riak_sup:get_random_pid(),
- get_riak_pid(PoolPid).
+ case ejabberd_riak_sup:start() of
+ ok ->
+ PoolPid = ejabberd_riak_sup:get_random_pid(),
+ get_riak_pid(PoolPid);
+ {error, _} = Err ->
+ Err
+ end.
get_riak_pid(PoolPid) ->
case catch gen_server:call(PoolPid, get_pid) of
diff --git a/src/ejabberd_riak_sup.erl b/src/ejabberd_riak_sup.erl
index 2598297b9..ce8072670 100644
--- a/src/ejabberd_riak_sup.erl
+++ b/src/ejabberd_riak_sup.erl
@@ -26,88 +26,64 @@
-module(ejabberd_riak_sup).
-behaviour(supervisor).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
--export([start_link/0, init/1, get_pids/0,
- transform_options/1, get_random_pid/0,
- host_up/1, config_reloaded/0, opt_type/1]).
+-export([start/0, start_link/0, init/1, get_pids/0,
+ get_random_pid/0, config_reloaded/0]).
-include("logger.hrl").
--define(DEFAULT_POOL_SIZE, 10).
--define(DEFAULT_RIAK_START_INTERVAL, 30). % 30 seconds
--define(DEFAULT_RIAK_HOST, "127.0.0.1").
--define(DEFAULT_RIAK_PORT, 8087).
-
% time to wait for the supervisor to start its child before returning
% a timeout error to the request
-define(CONNECT_TIMEOUT, 500). % milliseconds
-host_up(Host) ->
- case is_riak_configured(Host) of
- true ->
- ejabberd:start_app(riakc),
- lists:foreach(
- fun(Spec) ->
- supervisor:start_child(?MODULE, Spec)
- end, get_specs());
+start() ->
+ case is_started() of
+ true -> ok;
false ->
- ok
+ ejabberd:start_app(riakc),
+ Spec = {?MODULE, {?MODULE, start_link, []},
+ permanent, infinity, supervisor, [?MODULE]},
+ case supervisor:start_child(ejabberd_db_sup, Spec) of
+ {ok, _} -> ok;
+ {error, {already_started, _}} -> ok;
+ {error, Why} = Err ->
+ ?ERROR_MSG("Failed to start ~s: ~p",
+ [?MODULE, Why]),
+ Err
+ end
end.
config_reloaded() ->
- case is_riak_configured() of
+ case is_started() of
true ->
- ejabberd:start_app(riakc),
lists:foreach(
fun(Spec) ->
supervisor:start_child(?MODULE, Spec)
- end, get_specs());
- false ->
+ end, get_specs()),
+ PoolSize = get_pool_size(),
lists:foreach(
- fun({Id, _, _, _}) ->
- supervisor:terminate_child(?MODULE, Id),
- supervisor:delete_child(?MODULE, Id)
- end, supervisor:which_children(?MODULE))
+ fun({Id, _, _, _}) when Id > PoolSize ->
+ case supervisor:terminate_child(?MODULE, Id) of
+ ok -> supervisor:delete_child(?MODULE, Id);
+ _ -> ok
+ end;
+ (_) ->
+ ok
+ end, supervisor:which_children(?MODULE));
+ false ->
+ ok
end.
-is_riak_configured() ->
- lists:any(fun is_riak_configured/1, ejabberd_config:get_myhosts()).
-
-is_riak_configured(Host) ->
- ServerConfigured = ejabberd_config:has_option({riak_server, Host}),
- PortConfigured = ejabberd_config:has_option({riak_port, Host}),
- StartIntervalConfigured = ejabberd_config:has_option({riak_start_interval, Host}),
- PoolConfigured = ejabberd_config:has_option({riak_pool_size, Host}),
- CacertConfigured = ejabberd_config:has_option({riak_cacertfile, Host}),
- UserConfigured = ejabberd_config:has_option({riak_username, Host}),
- PassConfigured = ejabberd_config:has_option({riak_password, Host}),
- AuthConfigured = lists:member(
- ejabberd_auth_riak,
- ejabberd_auth:auth_modules(Host)),
- SMConfigured = ejabberd_config:get_option({sm_db_type, Host}) == riak,
- RouterConfigured = ejabberd_config:get_option({router_db_type, Host}) == riak,
- ServerConfigured or PortConfigured or StartIntervalConfigured
- or PoolConfigured or CacertConfigured
- or UserConfigured or PassConfigured
- or SMConfigured or RouterConfigured
- or AuthConfigured.
-
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 20),
- ejabberd_hooks:add(host_up, ?MODULE, host_up, 20),
- Specs = case is_riak_configured() of
- true ->
- ejabberd:start_app(riakc),
- get_specs();
- false ->
- []
- end,
- {ok, {{one_for_one, 500, 1}, Specs}}.
+ {ok, {{one_for_one, 500, 1}, get_specs()}}.
+
+is_started() ->
+ whereis(?MODULE) /= undefined.
-spec get_specs() -> [supervisor:child_spec()].
get_specs() ->
@@ -133,30 +109,30 @@ get_specs() ->
fun(I) ->
{ejabberd_riak:get_proc(I),
{ejabberd_riak, start_link,
- [I, Server, Port, StartInterval*1000, Options]},
+ [I, Server, Port, StartInterval, Options]},
transient, 2000, worker, [?MODULE]}
end, lists:seq(1, PoolSize)).
get_start_interval() ->
- ejabberd_config:get_option(riak_start_interval, ?DEFAULT_RIAK_START_INTERVAL).
+ ejabberd_option:riak_start_interval().
get_pool_size() ->
- ejabberd_config:get_option(riak_pool_size, ?DEFAULT_POOL_SIZE).
+ ejabberd_option:riak_pool_size().
get_riak_server() ->
- ejabberd_config:get_option(riak_server, ?DEFAULT_RIAK_HOST).
+ ejabberd_option:riak_server().
get_riak_cacertfile() ->
- ejabberd_config:get_option(riak_cacertfile, nil).
+ ejabberd_option:riak_cacertfile().
get_riak_username() ->
- ejabberd_config:get_option(riak_username, nil).
+ ejabberd_option:riak_username().
get_riak_password() ->
- ejabberd_config:get_option(riak_password, nil).
+ ejabberd_option:riak_password().
get_riak_port() ->
- ejabberd_config:get_option(riak_port, ?DEFAULT_RIAK_PORT).
+ ejabberd_option:riak_port().
get_pids() ->
[ejabberd_riak:get_proc(I) || I <- lists:seq(1, get_pool_size())].
@@ -164,30 +140,3 @@ get_pids() ->
get_random_pid() ->
I = p1_rand:round_robin(get_pool_size()) + 1,
ejabberd_riak:get_proc(I).
-
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({riak_server, {S, P}}, Opts) ->
- [{riak_server, S}, {riak_port, P}|Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(riak_pool_size) ->
- fun (N) when is_integer(N), N >= 1 -> N end;
-opt_type(riak_port) ->
- fun(P) when is_integer(P), P > 0, P < 65536 -> P end;
-opt_type(riak_server) ->
- fun(S) -> binary_to_list(iolist_to_binary(S)) end;
-opt_type(riak_start_interval) ->
- fun (N) when is_integer(N), N >= 1 -> N end;
-opt_type(riak_cacertfile) ->
- fun(S) -> binary_to_list(iolist_to_binary(S)) end;
-opt_type(riak_username) ->
- fun(S) -> binary_to_list(iolist_to_binary(S)) end;
-opt_type(riak_password) ->
- fun(S) -> binary_to_list(iolist_to_binary(S)) end;
-opt_type(_) ->
- [riak_pool_size, riak_port, riak_server,
- riak_start_interval, riak_cacertfile, riak_username, riak_password].
diff --git a/src/ejabberd_router.erl b/src/ejabberd_router.erl
index f6c6d2db2..6f75944c4 100644
--- a/src/ejabberd_router.erl
+++ b/src/ejabberd_router.erl
@@ -25,8 +25,6 @@
-module(ejabberd_router).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-ifndef(GEN_SERVER).
@@ -59,7 +57,7 @@
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3, opt_type/1]).
+ handle_info/2, terminate/2, code_change/3]).
%% Deprecated functions
-export([route/3, route_error/4]).
@@ -388,10 +386,9 @@ do_route(_Pkt, _Route) ->
-spec balancing_route(jid(), jid(), stanza(), [#route{}]) -> any().
balancing_route(From, To, Packet, Rs) ->
- LDstDomain = To#jid.lserver,
- Value = get_domain_balancing(From, To, LDstDomain),
- case get_component_number(LDstDomain) of
+ case get_domain_balancing(From, To, To#jid.lserver) of
undefined ->
+ Value = erlang:system_time(),
case [R || R <- Rs, node(R#route.pid) == node()] of
[] ->
R = lists:nth(erlang:phash(Value, length(Rs)), Rs),
@@ -400,7 +397,7 @@ balancing_route(From, To, Packet, Rs) ->
R = lists:nth(erlang:phash(Value, length(LRs)), LRs),
do_route(Packet, R)
end;
- _ ->
+ Value ->
SRs = lists:ukeysort(#route.local_hint, Rs),
R = lists:nth(erlang:phash(Value, length(SRs)), SRs),
do_route(Packet, R)
@@ -408,24 +405,30 @@ balancing_route(From, To, Packet, Rs) ->
-spec get_component_number(binary()) -> pos_integer() | undefined.
get_component_number(LDomain) ->
- ejabberd_config:get_option({domain_balancing_component_number, LDomain}).
+ M = ejabberd_option:domain_balancing(),
+ case maps:get(LDomain, M, undefined) of
+ undefined -> undefined;
+ Opts -> maps:get(component_number, Opts)
+ end.
--spec get_domain_balancing(jid(), jid(), binary()) -> any().
+-spec get_domain_balancing(jid(), jid(), binary()) -> integer() | ljid() | undefined.
get_domain_balancing(From, To, LDomain) ->
- case ejabberd_config:get_option({domain_balancing, LDomain}) of
- undefined -> erlang:system_time();
- random -> erlang:system_time();
- source -> jid:tolower(From);
- destination -> jid:tolower(To);
- bare_source -> jid:remove_resource(jid:tolower(From));
- bare_destination -> jid:remove_resource(jid:tolower(To))
+ M = ejabberd_option:domain_balancing(),
+ case maps:get(LDomain, M, undefined) of
+ undefined -> undefined;
+ Opts ->
+ case maps:get(type, Opts, random) of
+ random -> erlang:system_time();
+ source -> jid:tolower(From);
+ destination -> jid:tolower(To);
+ bare_source -> jid:remove_resource(jid:tolower(From));
+ bare_destination -> jid:remove_resource(jid:tolower(To))
+ end
end.
-spec get_backend() -> module().
get_backend() ->
- DBType = ejabberd_config:get_option(
- router_db_type,
- ejabberd_config:default_ram_db(?MODULE)),
+ DBType = ejabberd_option:router_db_type(),
list_to_atom("ejabberd_router_" ++ atom_to_list(DBType)).
-spec cache_nodes(module()) -> [node()].
@@ -439,10 +442,7 @@ cache_nodes(Mod) ->
use_cache(Mod) ->
case erlang:function_exported(Mod, use_cache, 0) of
true -> Mod:use_cache();
- false ->
- ejabberd_config:get_option(
- router_use_cache,
- ejabberd_config:use_cache(global))
+ false -> ejabberd_option:router_use_cache()
end.
-spec delete_cache(module(), binary()) -> ok.
@@ -466,15 +466,9 @@ init_cache(Mod) ->
-spec cache_opts() -> [proplists:property()].
cache_opts() ->
- MaxSize = ejabberd_config:get_option(
- router_cache_size,
- ejabberd_config:cache_size(global)),
- CacheMissed = ejabberd_config:get_option(
- router_cache_missed,
- ejabberd_config:cache_missed(global)),
- LifeTime = case ejabberd_config:get_option(
- router_cache_life_time,
- ejabberd_config:cache_life_time(global)) of
+ MaxSize = ejabberd_option:router_cache_size(),
+ CacheMissed = ejabberd_option:router_cache_missed(),
+ LifeTime = case ejabberd_option:router_cache_life_time() of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -498,26 +492,3 @@ clean_cache(Node) ->
-spec clean_cache() -> ok.
clean_cache() ->
ejabberd_cluster:eval_everywhere(?MODULE, clean_cache, [node()]).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(domain_balancing) ->
- fun (random) -> random;
- (source) -> source;
- (destination) -> destination;
- (bare_source) -> bare_source;
- (bare_destination) -> bare_destination
- end;
-opt_type(domain_balancing_component_number) ->
- fun (N) when is_integer(N), N > 1 -> N end;
-opt_type(router_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-opt_type(O) when O == router_use_cache; O == router_cache_missed ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(O) when O == router_cache_size; O == router_cache_life_time ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
-opt_type(_) ->
- [domain_balancing, domain_balancing_component_number,
- router_db_type, router_use_cache, router_cache_size,
- router_cache_missed, router_cache_life_time].
diff --git a/src/ejabberd_router_mnesia.erl b/src/ejabberd_router_mnesia.erl
index 03fbe495f..329f5e061 100644
--- a/src/ejabberd_router_mnesia.erl
+++ b/src/ejabberd_router_mnesia.erl
@@ -100,8 +100,12 @@ register_route(Domain, ServerHost, _LocalHint, N, Pid) ->
unregister_route(Domain, undefined, Pid) ->
F = fun () ->
- case mnesia:match_object(
- #route{domain = Domain, pid = Pid, _ = '_'}) of
+ case mnesia:select(
+ route,
+ ets:fun2ms(
+ fun(#route{domain = D, pid = P} = R)
+ when D == Domain, P == Pid -> R
+ end)) of
[R] -> mnesia:delete_object(R);
_ -> ok
end
@@ -109,8 +113,12 @@ unregister_route(Domain, undefined, Pid) ->
transaction(F);
unregister_route(Domain, _, Pid) ->
F = fun () ->
- case mnesia:match_object(
- #route{domain = Domain, pid = Pid, _ = '_'}) of
+ case mnesia:select(
+ route,
+ ets:fun2ms(
+ fun(#route{domain = D, pid = P} = R)
+ when D == Domain, P == Pid -> R
+ end)) of
[R] ->
I = R#route.local_hint,
ServerHost = R#route.server_host,
@@ -147,8 +155,10 @@ init([]) ->
mnesia:subscribe({table, route, simple}),
lists:foreach(
fun (Pid) -> erlang:monitor(process, Pid) end,
- mnesia:dirty_select(route,
- [{#route{pid = '$1', _ = '_'}, [], ['$1']}])),
+ mnesia:dirty_select(
+ route,
+ ets:fun2ms(
+ fun(#route{pid = Pid}) -> Pid end))),
{ok, #state{}}.
handle_call(_Request, _From, State) ->
@@ -166,8 +176,12 @@ handle_info({mnesia_table_event, _}, State) ->
{noreply, State};
handle_info({'DOWN', _Ref, _Type, Pid, _Info}, State) ->
F = fun () ->
- Es = mnesia:select(route,
- [{#route{pid = Pid, _ = '_'}, [], ['$_']}]),
+ Es = mnesia:select(
+ route,
+ ets:fun2ms(
+ fun(#route{pid = P} = E)
+ when P == Pid -> E
+ end)),
lists:foreach(
fun(E) ->
if is_integer(E#route.local_hint) ->
diff --git a/src/ejabberd_router_riak.erl b/src/ejabberd_router_riak.erl
index 20346c369..434034cd9 100644
--- a/src/ejabberd_router_riak.erl
+++ b/src/ejabberd_router_riak.erl
@@ -67,7 +67,7 @@ get_all_routes() ->
%%% Internal functions
%%%===================================================================
route_schema() ->
- {record_info(fields, route), #route{}}.
+ {record_info(fields, route), #route{domain = <<>>, server_host = <<>>}}.
clean_table() ->
?DEBUG("Cleaning Riak 'route' table...", []),
diff --git a/src/ejabberd_router_sql.erl b/src/ejabberd_router_sql.erl
index bc3ef52ef..940333d40 100644
--- a/src/ejabberd_router_sql.erl
+++ b/src/ejabberd_router_sql.erl
@@ -23,7 +23,6 @@
-module(ejabberd_router_sql).
-behaviour(ejabberd_router).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/0, register_route/5, unregister_route/3, find_routes/1,
diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl
index 77c511d92..4a56191ff 100644
--- a/src/ejabberd_s2s.erl
+++ b/src/ejabberd_s2s.erl
@@ -27,8 +27,6 @@
-protocol({xep, 220, '1.1'}).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-behaviour(gen_server).
@@ -44,15 +42,14 @@
list_temporarily_blocked_hosts/0,
external_host_overloaded/1, is_temporarly_blocked/1,
get_commands_spec/0, zlib_enabled/1, get_idle_timeout/1,
- tls_required/1, tls_verify/1, tls_enabled/1, tls_options/2,
+ tls_required/1, tls_enabled/1, tls_options/2,
host_up/1, host_down/1, queue_type/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
--export([get_info_s2s_connections/1,
- transform_options/1, opt_type/1]).
+-export([get_info_s2s_connections/1]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -131,19 +128,21 @@ is_temporarly_blocked(Host) ->
end
end.
--spec remove_connection({binary(), binary()},
- pid()) -> {atomic, ok} | ok | {aborted, any()}.
-
+-spec remove_connection({binary(), binary()}, pid()) -> ok.
remove_connection(FromTo, Pid) ->
- case catch mnesia:dirty_match_object(s2s,
- #s2s{fromto = FromTo, pid = Pid})
- of
- [#s2s{pid = Pid}] ->
- F = fun () ->
- mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
- end,
- mnesia:transaction(F);
- _ -> ok
+ case mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo, pid = Pid}) of
+ [#s2s{pid = Pid}] ->
+ F = fun() ->
+ mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid})
+ end,
+ case mnesia:transaction(F) of
+ {atomic, _} -> ok;
+ {aborted, Reason} ->
+ ?ERROR_MSG("Failed to unregister s2s connection: "
+ "Mnesia failure: ~p", [Reason])
+ end;
+ _ ->
+ ok
end.
-spec have_connection({binary(), binary()}) -> boolean().
@@ -195,36 +194,32 @@ dirty_get_connections() ->
-spec tls_options(binary(), [proplists:property()]) -> [proplists:property()].
tls_options(LServer, DefaultOpts) ->
- TLSOpts1 = case get_certfile(LServer) of
- undefined -> DefaultOpts;
- CertFile ->
+ TLSOpts1 = case ejabberd_pkix:get_certfile(LServer) of
+ error -> DefaultOpts;
+ {ok, CertFile} ->
lists:keystore(certfile, 1, DefaultOpts,
{certfile, CertFile})
end,
- TLSOpts2 = case ejabberd_config:get_option(
- {s2s_ciphers, LServer}) of
+ TLSOpts2 = case ejabberd_option:s2s_ciphers(LServer) of
undefined -> TLSOpts1;
Ciphers -> lists:keystore(ciphers, 1, TLSOpts1,
{ciphers, Ciphers})
end,
- TLSOpts3 = case ejabberd_config:get_option(
- {s2s_protocol_options, LServer}) of
+ TLSOpts3 = case ejabberd_option:s2s_protocol_options(LServer) of
undefined -> TLSOpts2;
ProtoOpts -> lists:keystore(protocol_options, 1, TLSOpts2,
{protocol_options, ProtoOpts})
end,
- TLSOpts4 = case ejabberd_config:get_option(
- {s2s_dhfile, LServer}) of
+ TLSOpts4 = case ejabberd_option:s2s_dhfile(LServer) of
undefined -> TLSOpts3;
DHFile -> lists:keystore(dhfile, 1, TLSOpts3,
{dhfile, DHFile})
end,
- TLSOpts5 = case get_cafile(LServer) of
- undefined -> TLSOpts4;
- CAFile -> lists:keystore(cafile, 1, TLSOpts4,
- {cafile, CAFile})
+ TLSOpts5 = case lists:keymember(cafile, 1, TLSOpts4) of
+ true -> TLSOpts4;
+ false -> [{cafile, get_cafile(LServer)}|TLSOpts4]
end,
- case ejabberd_config:get_option({s2s_tls_compression, LServer}) of
+ case ejabberd_option:s2s_tls_compression(LServer) of
undefined -> TLSOpts5;
false -> [compression_none | TLSOpts5];
true -> lists:delete(compression_none, TLSOpts5)
@@ -233,12 +228,7 @@ tls_options(LServer, DefaultOpts) ->
-spec tls_required(binary()) -> boolean().
tls_required(LServer) ->
TLS = use_starttls(LServer),
- TLS == required orelse TLS == required_trusted.
-
--spec tls_verify(binary()) -> boolean().
-tls_verify(LServer) ->
- TLS = use_starttls(LServer),
- TLS == required_trusted.
+ TLS == required.
-spec tls_enabled(binary()) -> boolean().
tls_enabled(LServer) ->
@@ -247,38 +237,25 @@ tls_enabled(LServer) ->
-spec zlib_enabled(binary()) -> boolean().
zlib_enabled(LServer) ->
- ejabberd_config:get_option({s2s_zlib, LServer}, false).
+ ejabberd_option:s2s_zlib(LServer).
--spec use_starttls(binary()) -> boolean() | optional | required | required_trusted.
+-spec use_starttls(binary()) -> boolean() | optional | required.
use_starttls(LServer) ->
- ejabberd_config:get_option({s2s_use_starttls, LServer}, false).
+ ejabberd_option:s2s_use_starttls(LServer).
-spec get_idle_timeout(binary()) -> non_neg_integer() | infinity.
get_idle_timeout(LServer) ->
- ejabberd_config:get_option({s2s_timeout, LServer}, timer:minutes(10)).
+ ejabberd_option:s2s_timeout(LServer).
-spec queue_type(binary()) -> ram | file.
queue_type(LServer) ->
- ejabberd_config:get_option(
- {s2s_queue_type, LServer},
- ejabberd_config:default_queue_type(LServer)).
-
--spec get_certfile(binary()) -> file:filename_all() | undefined.
-get_certfile(LServer) ->
- case ejabberd_pkix:get_certfile(LServer) of
- {ok, CertFile} ->
- CertFile;
- error ->
- ejabberd_config:get_option(
- {domain_certfile, LServer},
- ejabberd_config:get_option({s2s_certfile, LServer}))
- end.
+ ejabberd_option:s2s_queue_type(LServer).
-spec get_cafile(binary()) -> file:filename_all() | undefined.
get_cafile(LServer) ->
- case ejabberd_config:get_option({s2s_cafile, LServer}) of
+ case ejabberd_option:s2s_cafile(LServer) of
undefined ->
- ejabberd_pkix:ca_file();
+ ejabberd_option:ca_file();
File ->
File
end.
@@ -286,22 +263,26 @@ get_cafile(LServer) ->
%%====================================================================
%% gen_server callbacks
%%====================================================================
-
init([]) ->
update_tables(),
ejabberd_mnesia:create(?MODULE, s2s,
- [{ram_copies, [node()]},
- {type, bag},
- {attributes, record_info(fields, s2s)}]),
- mnesia:subscribe(system),
- ejabberd_commands:register_commands(get_commands_spec()),
- ejabberd_mnesia:create(?MODULE, temporarily_blocked,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, temporarily_blocked)}]),
- ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
- ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
- lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
- {ok, #state{}}.
+ [{ram_copies, [node()]},
+ {type, bag},
+ {attributes, record_info(fields, s2s)}]),
+ case mnesia:subscribe(system) of
+ {ok, _} ->
+ ejabberd_commands:register_commands(get_commands_spec()),
+ ejabberd_mnesia:create(
+ ?MODULE, temporarily_blocked,
+ [{ram_copies, [node()]},
+ {attributes, record_info(fields, temporarily_blocked)}]),
+ ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
+ ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
+ lists:foreach(fun host_up/1, ejabberd_option:hosts()),
+ {ok, #state{}};
+ {error, Reason} ->
+ {stop, Reason}
+ end.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
@@ -319,7 +300,7 @@ handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) ->
ejabberd_commands:unregister_commands(get_commands_spec()),
- lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
+ lists:foreach(fun host_down/1, ejabberd_option:hosts()),
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
ok.
@@ -508,18 +489,18 @@ new_connection(MyServer, Server, From, FromTo,
[]
end.
--spec max_s2s_connections_number({binary(), binary()}) -> integer().
+-spec max_s2s_connections_number({binary(), binary()}) -> pos_integer().
max_s2s_connections_number({From, To}) ->
- case acl:match_rule(From, max_s2s_connections, jid:make(To)) of
- Max when is_integer(Max) -> Max;
- _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER
+ case ejabberd_shaper:match(From, max_s2s_connections, jid:make(To)) of
+ Max when is_integer(Max) -> Max;
+ _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER
end.
--spec max_s2s_connections_number_per_node({binary(), binary()}) -> integer().
+-spec max_s2s_connections_number_per_node({binary(), binary()}) -> pos_integer().
max_s2s_connections_number_per_node({From, To}) ->
- case acl:match_rule(From, max_s2s_connections_per_node, jid:make(To)) of
- Max when is_integer(Max) -> Max;
- _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE
+ case ejabberd_shaper:match(From, max_s2s_connections_per_node, jid:make(To)) of
+ Max when is_integer(Max) -> Max;
+ _ -> ?DEFAULT_MAX_S2S_CONNECTIONS_NUMBER_PER_NODE
end.
-spec needed_connections_number([#s2s{}], integer(), integer()) -> integer().
@@ -537,11 +518,11 @@ needed_connections_number(Ls, MaxS2SConnectionsNumber,
-spec is_service(jid(), jid()) -> boolean().
is_service(From, To) ->
LFromDomain = From#jid.lserver,
- case ejabberd_config:get_option({route_subdomains, LFromDomain}, local) of
+ case ejabberd_option:route_subdomains(LFromDomain) of
s2s -> % bypass RFC 3920 10.3
false;
local ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
P = fun (ParentDomain) ->
lists:member(ParentDomain, Hosts)
end,
@@ -602,32 +583,15 @@ stop_s2s_connections() ->
fun({_Id, Pid, _Type, _Module}) ->
supervisor:terminate_child(ejabberd_s2s_out_sup, Pid)
end, supervisor:which_children(ejabberd_s2s_out_sup)),
- mnesia:clear_table(s2s),
+ _ = mnesia:clear_table(s2s),
ok.
%%%----------------------------------------------------------------------
%%% Update Mnesia tables
update_tables() ->
- case catch mnesia:table_info(s2s, type) of
- bag -> ok;
- {'EXIT', _} -> ok;
- _ -> mnesia:delete_table(s2s)
- end,
- case catch mnesia:table_info(s2s, attributes) of
- [fromto, node, key] ->
- mnesia:transform_table(s2s, ignore, [fromto, pid]),
- mnesia:clear_table(s2s);
- [fromto, pid, key] ->
- mnesia:transform_table(s2s, ignore, [fromto, pid]),
- mnesia:clear_table(s2s);
- [fromto, pid] -> ok;
- {'EXIT', _} -> ok
- end,
- case lists:member(local_s2s, mnesia:system_info(tables)) of
- true -> mnesia:delete_table(local_s2s);
- false -> ok
- end.
+ _ = mnesia:delete_table(local_s2s),
+ ok.
%% Check if host is in blacklist or white list
allow_host(MyServer, S2SHost) ->
@@ -635,7 +599,7 @@ allow_host(MyServer, S2SHost) ->
not is_temporarly_blocked(S2SHost).
allow_host1(MyHost, S2SHost) ->
- Rule = ejabberd_config:get_option({s2s_access, MyHost}, all),
+ Rule = ejabberd_option:s2s_access(MyHost),
JID = jid:make(S2SHost),
case acl:match_rule(MyHost, Rule, JID) of
deny -> false;
@@ -648,30 +612,6 @@ allow_host1(MyHost, S2SHost) ->
end
end.
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({{s2s_host, Host}, Action}, Opts) ->
- ?WARNING_MSG("Option 's2s_host' is deprecated. "
- "The option is still supported but it is better to "
- "fix your config: use access rules instead.", []),
- ACLName = misc:binary_to_atom(
- iolist_to_binary(["s2s_access_", Host])),
- [{acl, ACLName, {server, Host}},
- {access, s2s, [{Action, ACLName}]},
- {s2s_access, s2s} |
- Opts];
-transform_options({s2s_default_policy, Action}, Opts) ->
- ?WARNING_MSG("Option 's2s_default_policy' is deprecated. "
- "The option is still supported but it is better to "
- "fix your config: "
- "use 's2s_access' with an access rule.", []),
- [{access, s2s, [{Action, all}]},
- {s2s_access, s2s} |
- Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
%% Get information about S2S connections of the specified type.
%% @spec (Type) -> [Info]
%% where Type = in | out
@@ -704,51 +644,3 @@ get_s2s_state(S2sPid) ->
{badrpc, _} -> [{status, error}]
end,
[{s2s_pid, S2sPid} | Infos].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(route_subdomains) ->
- fun (s2s) -> s2s;
- (local) -> local
- end;
-opt_type(s2s_access) ->
- fun acl:access_rules_validator/1;
-opt_type(s2s_ciphers) -> fun iolist_to_binary/1;
-opt_type(s2s_dhfile) -> fun misc:try_read_file/1;
-opt_type(s2s_cafile) -> fun misc:try_read_file/1;
-opt_type(s2s_protocol_options) ->
- fun (Options) -> str:join(Options, <<"|">>) end;
-opt_type(s2s_tls_compression) ->
- fun (true) -> true;
- (false) -> false
- end;
-opt_type(s2s_use_starttls) ->
- fun (true) -> true;
- (false) -> false;
- (optional) -> optional;
- (required) -> required;
- (required_trusted) ->
- ?WARNING_MSG("The value 'required_trusted' of option "
- "'s2s_use_starttls' is deprected and will be "
- "unsupported in future releases. Instead, "
- "set it to 'required' and make sure "
- "mod_s2s_dialback is *NOT* loaded", []),
- required_trusted
- end;
-opt_type(s2s_zlib) ->
- fun(true) ->
- ejabberd:start_app(ezlib),
- true;
- (false) ->
- false
- end;
-opt_type(s2s_timeout) ->
- fun(I) when is_integer(I), I >= 0 -> timer:seconds(I);
- (infinity) -> infinity;
- (unlimited) -> infinity
- end;
-opt_type(s2s_queue_type) ->
- fun(ram) -> ram; (file) -> file end;
-opt_type(_) ->
- [route_subdomains, s2s_access, s2s_zlib,
- s2s_ciphers, s2s_dhfile, s2s_cafile, s2s_protocol_options,
- s2s_tls_compression, s2s_use_starttls, s2s_timeout, s2s_queue_type].
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index be2f85370..0eb5f2a1d 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -22,9 +22,11 @@
-module(ejabberd_s2s_in).
-behaviour(xmpp_stream_in).
-behaviour(ejabberd_listener).
+-dialyzer([{no_fail_call, [stop/1, process_closed/2]},
+ {no_return, process_closed/2}]).
%% ejabberd_listener callbacks
--export([start/3, start_link/3, accept/1, listen_opt_type/1, listen_options/0]).
+-export([start/3, start_link/3, accept/1, listen_options/0]).
%% xmpp_stream_in callbacks
-export([init/1, handle_call/3, handle_cast/2,
handle_info/2, terminate/2, code_change/3]).
@@ -252,11 +254,11 @@ init([State, Opts]) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
- Timeout = ejabberd_config:negotiation_timeout(),
+ Timeout = ejabberd_option:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
auth_domains => sets:new(),
xmlns => ?NS_SERVER,
- lang => ejabberd_config:get_mylang(),
+ lang => ejabberd_option:language(),
server => ejabberd_config:get_myname(),
lserver => ejabberd_config:get_myname(),
server_host => ejabberd_config:get_myname(),
@@ -337,20 +339,11 @@ set_idle_timeout(State) ->
-spec change_shaper(state(), binary()) -> state().
change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
RServer) ->
- Shaper = acl:match_rule(ServerHost, ShaperName, jid:make(RServer)),
+ Shaper = ejabberd_shaper:match(ServerHost, ShaperName, jid:make(RServer)),
xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)).
-listen_opt_type(certfile = Opt) ->
- fun(S) ->
- ?WARNING_MSG("Listening option '~s' for ~s is deprecated, use "
- "'certfiles' global option instead", [Opt, ?MODULE]),
- {ok, File} = ejabberd_pkix:add_certfile(S),
- File
- end.
-
listen_options() ->
[{shaper, none},
- {certfile, undefined},
{ciphers, undefined},
{dhfile, undefined},
{cafile, undefined},
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index d940284ef..5b27f96c4 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -21,10 +21,9 @@
%%%-------------------------------------------------------------------
-module(ejabberd_s2s_out).
-behaviour(xmpp_stream_out).
--behaviour(ejabberd_config).
+-dialyzer([{no_fail_call, [stop/1, process_closed/2, handle_timeout/1]},
+ {no_return, [process_closed/2, handle_timeout/1]}]).
-%% ejabberd_config callbacks
--export([opt_type/1, transform_options/1]).
%% xmpp_stream_out callbacks
-export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1,
connect_timeout/1, address_families/1, default_port/1,
@@ -77,8 +76,7 @@ connect(Ref) ->
close(Ref) ->
xmpp_stream_out:close(Ref).
--spec close(pid(), atom()) -> ok;
- (state(), atom()) -> state().
+-spec close(pid(), atom()) -> ok.
close(Ref, Reason) ->
xmpp_stream_out:close(Ref, Reason).
@@ -184,30 +182,26 @@ tls_options(#{server := LServer}) ->
tls_required(#{server := LServer}) ->
ejabberd_s2s:tls_required(LServer).
-tls_verify(#{server := LServer}) ->
- ejabberd_s2s:tls_verify(LServer).
+tls_verify(#{server := LServer} = State) ->
+ ejabberd_hooks:run_fold(s2s_out_tls_verify, LServer, true, [State]).
tls_enabled(#{server := LServer}) ->
ejabberd_s2s:tls_enabled(LServer).
connect_timeout(#{server := LServer}) ->
- ejabberd_config:get_option(
- {outgoing_s2s_timeout, LServer},
- timer:seconds(10)).
+ ejabberd_option:outgoing_s2s_timeout(LServer).
default_port(#{server := LServer}) ->
- ejabberd_config:get_option({outgoing_s2s_port, LServer}, 5269).
+ ejabberd_option:outgoing_s2s_port(LServer).
address_families(#{server := LServer}) ->
- ejabberd_config:get_option(
- {outgoing_s2s_families, LServer},
- [inet, inet6]).
+ ejabberd_option:outgoing_s2s_families(LServer).
dns_retries(#{server := LServer}) ->
- ejabberd_config:get_option({s2s_dns_retries, LServer}, 2).
+ ejabberd_option:s2s_dns_retries(LServer).
dns_timeout(#{server := LServer}) ->
- ejabberd_config:get_option({s2s_dns_timeout, LServer}, timer:seconds(10)).
+ ejabberd_option:s2s_dns_timeout(LServer).
handle_auth_success(Mech, #{socket := Socket, ip := IP,
remote_server := RServer,
@@ -269,11 +263,11 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
{_, N} -> N;
false -> unlimited
end,
- Timeout = ejabberd_config:negotiation_timeout(),
+ Timeout = ejabberd_option:negotiation_timeout(),
State1 = State#{on_route => queue,
queue => p1_queue:new(QueueType, QueueLimit),
xmlns => ?NS_SERVER,
- lang => ejabberd_config:get_mylang(),
+ lang => ejabberd_option:language(),
server_host => ServerHost,
shaper => none},
State2 = xmpp_stream_out:set_timeout(State1, Timeout),
@@ -314,8 +308,8 @@ terminate(Reason, #{server := LServer,
normal -> State;
_ -> State#{stop_reason => internal_failure}
end,
- bounce_queue(State1),
- bounce_message_queue(State1).
+ State2 = bounce_queue(State1),
+ bounce_message_queue(State2).
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -374,7 +368,7 @@ mk_bounce_error(_Lang, _State) ->
-spec get_delay() -> non_neg_integer().
get_delay() ->
- MaxDelay = ejabberd_config:get_option(s2s_max_retry_delay, 300),
+ MaxDelay = ejabberd_option:s2s_max_retry_delay(),
p1_rand:uniform(MaxDelay).
-spec set_idle_timeout(state()) -> state().
@@ -400,76 +394,3 @@ format_error(queue_full) ->
<<"Stream queue is overloaded">>;
format_error(Reason) ->
xmpp_stream_out:format_error(Reason).
-
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({outgoing_s2s_options, Families, Timeout}, Opts) ->
- ?WARNING_MSG("Option 'outgoing_s2s_options' is deprecated. "
- "The option is still supported "
- "but it is better to fix your config: "
- "use 'outgoing_s2s_timeout' and "
- "'outgoing_s2s_families' instead.", []),
- maybe_report_huge_timeout(outgoing_s2s_timeout, Timeout),
- [{outgoing_s2s_families, Families},
- {outgoing_s2s_timeout, Timeout}
- | Opts];
-transform_options({s2s_dns_options, S2SDNSOpts}, AllOpts) ->
- ?WARNING_MSG("Option 's2s_dns_options' is deprecated. "
- "The option is still supported "
- "but it is better to fix your config: "
- "use 's2s_dns_timeout' and "
- "'s2s_dns_retries' instead", []),
- lists:foldr(
- fun({timeout, T}, AccOpts) ->
- maybe_report_huge_timeout(s2s_dns_timeout, T),
- [{s2s_dns_timeout, T}|AccOpts];
- ({retries, R}, AccOpts) ->
- [{s2s_dns_retries, R}|AccOpts];
- (_, AccOpts) ->
- AccOpts
- end, AllOpts, S2SDNSOpts);
-transform_options({Opt, T}, Opts)
- when Opt == outgoing_s2s_timeout; Opt == s2s_dns_timeout ->
- maybe_report_huge_timeout(Opt, T),
- [{Opt, T}|Opts];
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
-maybe_report_huge_timeout(Opt, T) when is_integer(T), T >= 1000 ->
- ?WARNING_MSG("value '~p' of option '~p' is too big, "
- "are you sure you have set seconds?",
- [T, Opt]);
-maybe_report_huge_timeout(_, _) ->
- ok.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(outgoing_s2s_families) ->
- fun(Families) ->
- lists:map(
- fun(ipv4) -> inet;
- (ipv6) -> inet6
- end, Families)
- end;
-opt_type(outgoing_s2s_port) ->
- fun (I) when is_integer(I), I > 0, I < 65536 -> I end;
-opt_type(outgoing_s2s_timeout) ->
- fun(TimeOut) when is_integer(TimeOut), TimeOut > 0 ->
- timer:seconds(TimeOut);
- (unlimited) ->
- infinity;
- (infinity) ->
- infinity
- end;
-opt_type(s2s_dns_retries) ->
- fun (I) when is_integer(I), I >= 0 -> I end;
-opt_type(s2s_dns_timeout) ->
- fun(I) when is_integer(I), I>=0 -> timer:seconds(I);
- (infinity) -> infinity;
- (unlimited) -> infinity
- end;
-opt_type(s2s_max_retry_delay) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(_) ->
- [outgoing_s2s_families, outgoing_s2s_port, outgoing_s2s_timeout,
- s2s_dns_retries, s2s_dns_timeout, s2s_max_retry_delay].
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index d78a1e2ea..3130823fc 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -27,7 +27,7 @@
%% ejabberd_listener callbacks
-export([start/3, start_link/3, accept/1]).
--export([listen_opt_type/1, listen_options/0, transform_listen_option/2]).
+-export([listen_opt_type/1, listen_options/0]).
%% xmpp_stream_in callbacks
-export([init/1, handle_info/2, terminate/2, code_change/3]).
-export([handle_stream_start/2, handle_auth_success/4, handle_auth_failure/4,
@@ -65,8 +65,7 @@ send(Stream, Pkt) ->
close(Ref) ->
xmpp_stream_in:close(Ref).
--spec close(pid(), atom()) -> ok;
- (state(), atom()) -> state().
+-spec close(pid(), atom()) -> ok.
close(Ref, Reason) ->
xmpp_stream_in:close(Ref, Reason).
@@ -100,12 +99,12 @@ init([State, Opts]) ->
true -> TLSOpts1
end,
GlobalRoutes = proplists:get_value(global_routes, Opts, true),
- Timeout = ejabberd_config:negotiation_timeout(),
+ Timeout = ejabberd_option:negotiation_timeout(),
State1 = xmpp_stream_in:change_shaper(State, ejabberd_shaper:new(Shaper)),
State2 = xmpp_stream_in:set_timeout(State1, Timeout),
State3 = State2#{access => Access,
xmlns => ?NS_COMPONENT,
- lang => ejabberd_config:get_mylang(),
+ lang => ejabberd_option:language(),
server => ejabberd_config:get_myname(),
host_opts => dict:from_list(HostOpts1),
stream_version => undefined,
@@ -263,45 +262,30 @@ check_from(From, #{host_opts := HostOpts}) ->
random_password() ->
str:sha(p1_rand:bytes(20)).
-transform_listen_option({hosts, Hosts, O}, Opts) ->
- case lists:keyfind(hosts, 1, Opts) of
- {_, PrevHostOpts} ->
- NewHostOpts =
- lists:foldl(
- fun(H, Acc) ->
- dict:append_list(H, O, Acc)
- end, dict:from_list(PrevHostOpts), Hosts),
- [{hosts, dict:to_list(NewHostOpts)}|
- lists:keydelete(hosts, 1, Opts)];
- _ ->
- [{hosts, [{H, O} || H <- Hosts]}|Opts]
- end;
-transform_listen_option({host, Host, Os}, Opts) ->
- transform_listen_option({hosts, [Host], Os}, Opts);
-transform_listen_option(Opt, Opts) ->
- [Opt|Opts].
-
listen_opt_type(shaper_rule) ->
- fun(V) ->
- ?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
- "is renamed to 'shaper'", [?MODULE]),
- acl:shaper_rules_validator(V)
- end;
-listen_opt_type(check_from) -> fun(B) when is_boolean(B) -> B end;
-listen_opt_type(password) -> fun iolist_to_binary/1;
+ econf:and_then(
+ econf:shaper(),
+ fun(S) ->
+ ?WARNING_MSG("Listening option 'shaper_rule' of module ~s "
+ "is renamed to 'shaper'. Please adjust your "
+ "configuration", [?MODULE]),
+ S
+ end);
+listen_opt_type(check_from) ->
+ econf:bool();
+listen_opt_type(password) ->
+ econf:binary();
listen_opt_type(hosts) ->
- fun(HostOpts) ->
- lists:map(
- fun({Host, Opts}) ->
- Password = case proplists:get_value(password, Opts) of
- undefined -> undefined;
- P -> iolist_to_binary(P)
- end,
- {iolist_to_binary(Host), Password}
- end, HostOpts)
- end;
+ econf:and_then(
+ econf:map(
+ econf:domain(),
+ econf:options(
+ #{password => econf:binary()})),
+ fun({Host, Opts}) ->
+ {Host, proplists:get_value(password, Opts)}
+ end);
listen_opt_type(global_routes) ->
- fun(B) when is_boolean(B) -> B end.
+ econf:bool().
listen_options() ->
[{access, all},
diff --git a/src/ejabberd_shaper.erl b/src/ejabberd_shaper.erl
index cad04986c..8617b3609 100644
--- a/src/ejabberd_shaper.erl
+++ b/src/ejabberd_shaper.erl
@@ -1,10 +1,4 @@
%%%----------------------------------------------------------------------
-%%% File : ejabberd_shaper.erl
-%%% Author : Alexey Shchepin <alexey@process-one.net>
-%%% Purpose : Functions to control connections traffic
-%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
-%%%
-%%%
%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
@@ -22,131 +16,225 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-
-module(ejabberd_shaper).
-
-behaviour(gen_server).
--behaviour(ejabberd_config).
-
--author('alexey@process-one.net').
--export([start_link/0, new/1, update/2,
- get_max_rate/1, transform_options/1, load_from_config/0,
- opt_type/1]).
+-export([start_link/0, new/1, update/2, match/3, get_max_rate/1]).
+-export([reload_from_config/0]).
+-export([validator/1, shaper_rules_validator/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-include("logger.hrl").
--record(shaper, {name :: {atom(), global},
- maxrate :: integer(),
- burst_size :: integer()}).
-
--record(state, {}).
-
+-type state() :: #{hosts := [binary()]}.
-type shaper() :: none | p1_shaper:state().
--export_type([shaper/0]).
+-type shaper_rate() :: {pos_integer(), pos_integer()} | pos_integer() | infinity.
+-type shaper_rule() :: {atom() | pos_integer(), [acl:access_rule()]}.
+-type shaper_rate_rule() :: {shaper_rate(), [acl:access_rule()]}.
+
+-export_type([shaper/0, shaper_rule/0, shaper_rate/0]).
+%%%===================================================================
+%%% API
+%%%===================================================================
-spec start_link() -> {ok, pid()} | {error, any()}.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+-spec match(global | binary(), atom() | [shaper_rule()],
+ jid:jid() | jid:ljid() | inet:ip_address() | acl:match()) -> none | shaper_rate().
+match(_, none, _) -> none;
+match(_, infinity, _) -> infinity;
+match(Host, Shaper, Match) when is_map(Match) ->
+ Rules = if is_atom(Shaper) -> read_shaper_rules(Shaper, Host);
+ true -> Shaper
+ end,
+ Rate = acl:match_rules(Host, Rules, Match, none),
+ read_shaper(Rate);
+match(Host, Shaper, IP) when tuple_size(IP) == 4; tuple_size(IP) == 8 ->
+ match(Host, Shaper, #{ip => IP});
+match(Host, Shaper, JID) ->
+ match(Host, Shaper, #{usr => jid:tolower(JID)}).
+
+-spec get_max_rate(none | shaper_rate()) -> none | pos_integer().
+get_max_rate({Rate, _}) -> Rate;
+get_max_rate(Rate) when is_integer(Rate), Rate > 0 -> Rate;
+get_max_rate(_) -> none.
+
+-spec new(none | shaper_rate()) -> shaper().
+new({Rate, Burst}) -> p1_shaper:new(Rate, Burst);
+new(Rate) when is_integer(Rate), Rate > 0 -> p1_shaper:new(Rate);
+new(_) -> none.
+
+-spec update(shaper(), non_neg_integer()) -> {shaper(), non_neg_integer()}.
+update(none, _Size) -> {none, 0};
+update(Shaper1, Size) ->
+ Shaper2 = p1_shaper:update(Shaper1, Size),
+ ?DEBUG("Shaper update:~n~s =>~n~s",
+ [p1_shaper:pp(Shaper1), p1_shaper:pp(Shaper2)]),
+ Shaper2.
+
+-spec validator(shaper | shaper_rules) -> econf:validator().
+validator(shaper) ->
+ econf:options(
+ #{'_' => shaper_validator()},
+ [{disallowed, reserved()}, {return, map}, unique]);
+validator(shaper_rules) ->
+ econf:options(
+ #{'_' => shaper_rules_validator()},
+ [{disallowed, reserved()}, unique]).
+
+-spec shaper_rules_validator() -> econf:validator().
+shaper_rules_validator() ->
+ fun(L) when is_list(L) ->
+ lists:map(
+ fun({K, V}) ->
+ {(shaper_name())(K), (acl:access_validator())(V)};
+ (N) ->
+ {(shaper_name())(N), [{acl, all}]}
+ end, lists:flatten(L));
+ (N) ->
+ [{(shaper_name())(N), [{acl, all}]}]
+ end.
+
+-spec reload_from_config() -> ok.
+reload_from_config() ->
+ gen_server:call(?MODULE, reload_from_config, timer:minutes(1)).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
init([]) ->
- ejabberd_mnesia:create(?MODULE, shaper,
- [{ram_copies, [node()]},
- {local_content, true},
- {attributes, record_info(fields, shaper)}]),
- ejabberd_hooks:add(config_reloaded, ?MODULE, load_from_config, 20),
- load_from_config(),
- {ok, #state{}}.
-
-handle_call(_Request, _From, State) ->
- Reply = ok,
- {reply, Reply, State}.
-
-handle_cast(_Msg, State) ->
+ create_tabs(),
+ Hosts = ejabberd_option:hosts(),
+ load_from_config([], Hosts),
+ ejabberd_hooks:add(config_reloaded, ?MODULE, reload_from_config, 20),
+ {ok, #{hosts => Hosts}}.
+
+-spec handle_call(term(), term(), state()) -> {reply, ok, state()} | {noreply, state()}.
+handle_call(reload_from_config, _, #{hosts := OldHosts} = State) ->
+ NewHosts = ejabberd_option:hosts(),
+ load_from_config(OldHosts, NewHosts),
+ {reply, ok, State#{hosts => NewHosts}};
+handle_call(Request, From, State) ->
+ ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
+ {noreply, State}.
+
+-spec handle_cast(term(), state()) -> {noreply, state()}.
+handle_cast(Msg, State) ->
+ ?WARNING_MSG("Unexpected cast: ~p", [Msg]),
{noreply, State}.
-handle_info(_Info, State) ->
+-spec handle_info(term(), state()) -> {noreply, state()}.
+handle_info(Info, State) ->
+ ?WARNING_MSG("Unexpected info: ~p", [Info]),
{noreply, State}.
+-spec terminate(any(), state()) -> ok.
terminate(_Reason, _State) ->
- ok.
+ ejabberd_hooks:delete(config_reloaded, ?MODULE, reload_from_config, 20).
+-spec code_change(term(), state(), term()) -> {ok, state()}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
--spec load_from_config() -> ok | {error, any()}.
-load_from_config() ->
- Shapers = ejabberd_config:get_option(shaper, []),
- case mnesia:transaction(
- fun() ->
- lists:foreach(
- fun({Name, MaxRate, BurstSize}) ->
- mnesia:write(
- #shaper{name = {Name, global},
- maxrate = MaxRate,
- burst_size = BurstSize})
- end,
- Shapers)
- end) of
- {atomic, ok} ->
- ok;
- Err ->
- {error, Err}
- end.
-
--spec get_max_rate(atom()) -> none | non_neg_integer().
-get_max_rate(none) ->
- none;
-get_max_rate(Name) ->
- case ets:lookup(shaper, {Name, global}) of
- [#shaper{maxrate = R}] ->
- R;
- [] ->
- none
- end.
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+%%%===================================================================
+%%% Table management
+%%%===================================================================
+-spec load_from_config([binary()], [binary()]) -> ok.
+load_from_config(OldHosts, NewHosts) ->
+ ?DEBUG("Loading shaper rules from config", []),
+ Shapers = ejabberd_option:shaper(),
+ ets:insert(shaper, maps:to_list(Shapers)),
+ ets:insert(
+ shaper_rules,
+ lists:flatmap(
+ fun(Host) ->
+ lists:flatmap(
+ fun({Name, List}) ->
+ case resolve_shapers(Name, List, Shapers) of
+ [] -> [];
+ List1 ->
+ [{{Name, Host}, List1}]
+ end
+ end, ejabberd_option:shaper_rules(Host))
+ end, [global|NewHosts])),
+ lists:foreach(
+ fun(Host) ->
+ ets:match_delete(shaper_rules, {{'_', Host}, '_'})
+ end, OldHosts -- NewHosts),
+ ?DEBUG("Shaper rules loaded successfully", []).
+
+-spec create_tabs() -> ok.
+create_tabs() ->
+ _ = mnesia:delete_table(shaper),
+ _ = ets:new(shaper, [named_table, {read_concurrency, true}]),
+ _ = ets:new(shaper_rules, [named_table, {read_concurrency, true}]),
+ ok.
--spec new(atom()) -> shaper().
-new(none) ->
- none;
-new(Name) ->
- case ets:lookup(shaper, {Name, global}) of
- [#shaper{maxrate = R, burst_size = B}] ->
- p1_shaper:new(R, B);
- [] ->
- none
+-spec read_shaper_rules(atom(), global | binary()) -> [shaper_rate_rule()].
+read_shaper_rules(Name, Host) ->
+ case ets:lookup(shaper_rules, {Name, Host}) of
+ [{_, Rule}] -> Rule;
+ [] -> []
end.
--spec update(shaper(), integer()) -> {shaper(), integer()}.
-update(none, _Size) -> {none, 0};
-update(Shaper, Size) ->
- Result = p1_shaper:update(Shaper, Size),
- ?DEBUG("Shaper update:~n~s =>~n~s",
- [p1_shaper:pp(Shaper), p1_shaper:pp(Result)]),
- Result.
-
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({shaper, Name, {maxrate, N}}, Opts) ->
- [{shaper, [{Name, N}]} | Opts];
-transform_options({shaper, Name, none}, Opts) ->
- [{shaper, [{Name, none}]} | Opts];
-transform_options({shaper, List}, Opts) when is_list(List) ->
- R = lists:map(
- fun({Name, Args}) when is_list(Args) ->
- MaxRate = proplists:get_value(rate, Args, 1000),
- BurstSize = proplists:get_value(burst_size, Args, MaxRate),
- {Name, MaxRate, BurstSize};
- ({Name, Val}) ->
- {Name, Val, Val}
- end, List),
- [{shaper, R} | Opts];
-transform_options(Opt, Opts) ->
- [Opt | Opts].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(shaper) -> fun(V) -> V end;
-opt_type(_) -> [shaper].
+-spec read_shaper(atom() | shaper_rate()) -> none | shaper_rate().
+read_shaper(Name) when is_atom(Name), Name /= none, Name /= infinity ->
+ case ets:lookup(shaper, Name) of
+ [{_, Rate}] -> Rate;
+ [] -> none
+ end;
+read_shaper(Rate) ->
+ Rate.
+
+%%%===================================================================
+%%% Validators
+%%%===================================================================
+shaper_name() ->
+ econf:either(
+ econf:and_then(
+ econf:atom(),
+ fun(infinite) -> infinity;
+ (unlimited) -> infinity;
+ (A) -> A
+ end),
+ econf:pos_int()).
+
+shaper_validator() ->
+ econf:either(
+ econf:and_then(
+ econf:options(
+ #{rate => econf:pos_int(),
+ burst_size => econf:pos_int()},
+ [unique, {required, [rate]}, {return, map}]),
+ fun(#{rate := Rate} = Map) ->
+ {Rate, maps:get(burst_size, Map, Rate)}
+ end),
+ econf:pos_int(infinity)).
+
+%%%===================================================================
+%%% Aux
+%%%===================================================================
+reserved() ->
+ [none, infinite, unlimited, infinity].
+
+-spec resolve_shapers(atom(), [shaper_rule()], #{atom() => shaper_rate()}) -> [shaper_rate_rule()].
+resolve_shapers(ShaperRule, Rules, Shapers) ->
+ lists:filtermap(
+ fun({Name, Rule}) when is_atom(Name), Name /= none, Name /= infinity ->
+ try {true, {maps:get(Name, Shapers), Rule}}
+ catch _:{badkey, _} ->
+ ?WARNING_MSG(
+ "Shaper rule '~s' refers to unknown shaper: ~s",
+ [ShaperRule, Name]),
+ false
+ end;
+ (_) ->
+ true
+ end, Rules).
diff --git a/src/ejabberd_sip.erl b/src/ejabberd_sip.erl
index 226cad3e8..4a2270bec 100644
--- a/src/ejabberd_sip.erl
+++ b/src/ejabberd_sip.erl
@@ -45,8 +45,8 @@ start_link(_, _, _) ->
-else.
%% API
-export([tcp_init/2, udp_init/2, udp_recv/5, start/3,
- start_link/3, accept/1, listen_options/0]).
-
+ start_link/3, accept/1]).
+-export([listen_opt_type/1, listen_options/0]).
%%%===================================================================
%%% API
@@ -80,15 +80,13 @@ set_certfile(Opts) ->
{ok, CertFile} ->
[{certfile, CertFile}|Opts];
error ->
- case ejabberd_config:get_option({domain_certfile, ejabberd_config:get_myname()}) of
- undefined ->
- Opts;
- CertFile ->
- [{certfile, CertFile}|Opts]
- end
+ Opts
end
end.
+listen_opt_type(certfile) ->
+ econf:pem().
+
listen_options() ->
[{tls, false},
{certfile, undefined}].
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index d60daca63..b85dbd9cc 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -25,8 +25,6 @@
-module(ejabberd_sm).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-ifndef(GEN_SERVER).
@@ -83,7 +81,7 @@
]).
-export([init/1, handle_call/3, handle_cast/2,
- handle_info/2, terminate/2, code_change/3, opt_type/1]).
+ handle_info/2, terminate/2, code_change/3]).
-include("logger.hrl").
@@ -117,11 +115,12 @@
start_link() ->
?GEN_SERVER:start_link({local, ?MODULE}, ?MODULE, [], []).
--spec stop() -> ok.
+-spec stop() -> ok | {error, atom()}.
stop() ->
- supervisor:terminate_child(ejabberd_sup, ?MODULE),
- supervisor:delete_child(ejabberd_sup, ?MODULE),
- ok.
+ case supervisor:terminate_child(ejabberd_sup, ?MODULE) of
+ ok -> supervisor:delete_child(ejabberd_sup, ?MODULE);
+ Err -> Err
+ end.
-spec route(jid(), term()) -> ok.
%% @doc route arbitrary term to c2s process(es)
@@ -477,7 +476,7 @@ init([]) ->
ejabberd_hooks:add(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:add(host_down, ?MODULE, host_down, 60),
ejabberd_hooks:add(config_reloaded, ?MODULE, config_reloaded, 50),
- lists:foreach(fun host_up/1, ejabberd_config:get_myhosts()),
+ lists:foreach(fun host_up/1, ejabberd_option:hosts()),
ejabberd_commands:register_commands(get_commands_spec()),
{ok, #state{}};
{error, Why} ->
@@ -497,7 +496,7 @@ handle_info(Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
- lists:foreach(fun host_down/1, ejabberd_config:get_myhosts()),
+ lists:foreach(fun host_down/1, ejabberd_option:hosts()),
ejabberd_hooks:delete(host_up, ?MODULE, host_up, 50),
ejabberd_hooks:delete(host_down, ?MODULE, host_down, 60),
ejabberd_hooks:delete(config_reloaded, ?MODULE, config_reloaded, 50),
@@ -860,12 +859,11 @@ check_max_sessions(LUser, LServer) ->
%% Defaults to infinity
-spec get_max_user_sessions(binary(), binary()) -> infinity | non_neg_integer().
get_max_user_sessions(LUser, Host) ->
- case acl:match_rule(Host, max_user_sessions,
- jid:make(LUser, Host))
- of
- Max when is_integer(Max) -> Max;
- infinity -> infinity;
- _ -> ?MAX_USER_SESSIONS
+ case ejabberd_shaper:match(Host, max_user_sessions,
+ jid:make(LUser, Host)) of
+ Max when is_integer(Max) -> Max;
+ infinity -> infinity;
+ _ -> ?MAX_USER_SESSIONS
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -882,15 +880,13 @@ force_update_presence({LUser, LServer}) ->
-spec get_sm_backend(binary()) -> module().
get_sm_backend(Host) ->
- DBType = ejabberd_config:get_option(
- {sm_db_type, Host},
- ejabberd_config:default_ram_db(Host, ?MODULE)),
+ DBType = ejabberd_option:sm_db_type(Host),
list_to_atom("ejabberd_sm_" ++ atom_to_list(DBType)).
-spec get_sm_backends() -> [module()].
get_sm_backends() ->
- lists:usort([get_sm_backend(Host) || Host <- ejabberd_config:get_myhosts()]).
+ lists:usort([get_sm_backend(Host) || Host <- ejabberd_option:hosts()]).
-spec get_vh_by_backend(module()) -> [binary()].
@@ -898,7 +894,7 @@ get_vh_by_backend(Mod) ->
lists:filter(
fun(Host) ->
get_sm_backend(Host) == Mod
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
%%--------------------------------------------------------------------
%%% Cache stuff
@@ -914,15 +910,9 @@ init_cache() ->
-spec cache_opts() -> [proplists:property()].
cache_opts() ->
- MaxSize = ejabberd_config:get_option(
- sm_cache_size,
- ejabberd_config:cache_size(global)),
- CacheMissed = ejabberd_config:get_option(
- sm_cache_missed,
- ejabberd_config:cache_missed(global)),
- LifeTime = case ejabberd_config:get_option(
- sm_cache_life_time,
- ejabberd_config:cache_life_time(global)) of
+ MaxSize = ejabberd_option:sm_cache_size(),
+ CacheMissed = ejabberd_option:sm_cache_missed(),
+ LifeTime = case ejabberd_option:sm_cache_life_time() of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -949,10 +939,7 @@ clean_cache() ->
use_cache(Mod, LServer) ->
case erlang:function_exported(Mod, use_cache, 1) of
true -> Mod:use_cache(LServer);
- false ->
- ejabberd_config:get_option(
- {sm_use_cache, LServer},
- ejabberd_config:use_cache(LServer))
+ false -> ejabberd_option:sm_use_cache(LServer)
end.
-spec use_cache() -> boolean().
@@ -961,7 +948,7 @@ use_cache() ->
fun(Host) ->
Mod = get_sm_backend(Host),
use_cache(Mod, Host)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec cache_nodes(module(), binary()) -> [node()].
cache_nodes(Mod, LServer) ->
@@ -1041,16 +1028,3 @@ kick_user(User, Server, Resource) ->
make_sid() ->
{misc:unique_timestamp(), self()}.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(sm_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-opt_type(O) when O == sm_use_cache; O == sm_cache_missed ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(O) when O == sm_cache_size; O == sm_cache_life_time ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
-opt_type(_) ->
- [sm_db_type, sm_use_cache, sm_cache_size, sm_cache_missed,
- sm_cache_life_time].
diff --git a/src/ejabberd_sm_sql.erl b/src/ejabberd_sm_sql.erl
index 8c3efc9b3..71defb06c 100644
--- a/src/ejabberd_sm_sql.erl
+++ b/src/ejabberd_sm_sql.erl
@@ -24,7 +24,6 @@
-module(ejabberd_sm_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(ejabberd_sm).
diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl
index 3431e61b8..c7c2d2d4d 100644
--- a/src/ejabberd_sql.erl
+++ b/src/ejabberd_sql.erl
@@ -25,8 +25,6 @@
-module(ejabberd_sql).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-behaviour(p1_fsm).
@@ -65,8 +63,7 @@
code_change/4]).
-export([connecting/2, connecting/3,
- session_established/2, session_established/3,
- opt_type/1]).
+ session_established/2, session_established/3]).
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
@@ -86,24 +83,12 @@
-define(TOP_LEVEL_TXN, 0).
--define(PGSQL_PORT, 5432).
-
--define(MYSQL_PORT, 3306).
-
--define(MSSQL_PORT, 1433).
-
-define(MAX_TRANSACTION_RESTARTS, 10).
-define(KEEPALIVE_QUERY, [<<"SELECT 1;">>]).
-define(PREPARE_KEY, ejabberd_sql_prepare).
--ifdef(NEW_SQL_SCHEMA).
--define(USE_NEW_SCHEMA_DEFAULT, true).
--else.
--define(USE_NEW_SCHEMA_DEFAULT, false).
--endif.
-
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -128,13 +113,15 @@ start_link(Host, StartInterval) ->
[Host, StartInterval],
fsm_limit_opts() ++ (?FSMOPTS)).
--type sql_query() :: [sql_query() | binary()] | #sql_query{} |
- fun(() -> any()) | fun((atom(), _) -> any()).
+-type sql_query_simple() :: [sql_query() | binary()] | #sql_query{} |
+ fun(() -> any()) | fun((atom(), _) -> any()).
+-type sql_query() :: sql_query_simple() |
+ [{atom() | {atom(), any()}, sql_query_simple()}].
-type sql_query_result() :: {updated, non_neg_integer()} |
{error, binary()} |
- {selected, [binary()],
- [[binary()]]} |
- {selected, [any()]}.
+ {selected, [binary()], [[binary()]]} |
+ {selected, [any()]} |
+ ok.
-spec sql_query(binary(), sql_query()) -> sql_query_result().
@@ -300,39 +287,41 @@ sqlite_db(Host) ->
-spec sqlite_file(binary()) -> string().
sqlite_file(Host) ->
- case ejabberd_config:get_option({sql_database, Host}) of
+ case ejabberd_option:sql_database(Host) of
undefined ->
- {ok, Cwd} = file:get_cwd(),
- filename:join([Cwd, "sqlite", atom_to_list(node()),
- binary_to_list(Host), "ejabberd.db"]);
+ Path = ["sqlite", atom_to_list(node()),
+ binary_to_list(Host), "ejabberd.db"],
+ case file:get_cwd() of
+ {ok, Cwd} ->
+ filename:join([Cwd|Path]);
+ {error, Reason} ->
+ ?ERROR_MSG("Failed to get current directory: ~s",
+ [file:format_error(Reason)]),
+ filename:join(Path)
+ end;
File ->
binary_to_list(File)
end.
use_new_schema() ->
- ejabberd_config:get_option(new_sql_schema, ?USE_NEW_SCHEMA_DEFAULT).
+ ejabberd_option:new_sql_schema().
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
init([Host, StartInterval]) ->
process_flag(trap_exit, true),
- case ejabberd_config:get_option({sql_keepalive_interval, Host}) of
+ case ejabberd_option:sql_keepalive_interval(Host) of
undefined ->
ok;
KeepaliveInterval ->
- timer:apply_interval(KeepaliveInterval * 1000, ?MODULE,
+ timer:apply_interval(KeepaliveInterval, ?MODULE,
keep_alive, [Host, self()])
end,
[DBType | _] = db_opts(Host),
p1_fsm:send_event(self(), connect),
ejabberd_sql_sup:add_pid(Host, self()),
- QueueType = case ejabberd_config:get_option({sql_queue_type, Host}) of
- undefined ->
- ejabberd_config:default_queue_type(Host);
- Type ->
- Type
- end,
+ QueueType = ejabberd_option:sql_queue_type(Host),
{ok, connecting,
#state{db_type = DBType, host = Host,
pending_requests = p1_queue:new(QueueType, max_fsm_queue()),
@@ -995,11 +984,13 @@ log(Level, Format, Args) ->
end.
db_opts(Host) ->
- Type = ejabberd_config:get_option({sql_type, Host}, odbc),
- Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>),
- Timeout = timer:seconds(
- ejabberd_config:get_option({sql_connect_timeout, Host}, 5)),
- Transport = case ejabberd_config:get_option({sql_ssl, Host}, false) of
+ Type = case ejabberd_option:sql_type(Host) of
+ undefined -> odbc;
+ T -> T
+ end,
+ Server = ejabberd_option:sql_server(Host),
+ Timeout = ejabberd_option:sql_connect_timeout(Host),
+ Transport = case ejabberd_option:sql_ssl(Host) of
false -> tcp;
true -> ssl
end,
@@ -1010,19 +1001,13 @@ db_opts(Host) ->
sqlite ->
[sqlite, Host];
_ ->
- Port = ejabberd_config:get_option(
- {sql_port, Host},
- case Type of
- mssql -> ?MSSQL_PORT;
- mysql -> ?MYSQL_PORT;
- pgsql -> ?PGSQL_PORT
- end),
- DB = ejabberd_config:get_option({sql_database, Host},
- <<"ejabberd">>),
- User = ejabberd_config:get_option({sql_username, Host},
- <<"ejabberd">>),
- Pass = ejabberd_config:get_option({sql_password, Host},
- <<"">>),
+ Port = ejabberd_option:sql_port(Host),
+ DB = case ejabberd_option:sql_database(Host) of
+ undefined -> <<"ejabberd">>;
+ D -> D
+ end,
+ User = ejabberd_option:sql_username(Host),
+ Pass = ejabberd_option:sql_password(Host),
SSLOpts = get_ssl_opts(Transport, Host),
case Type of
mssql ->
@@ -1041,15 +1026,15 @@ warn_if_ssl_unsupported(ssl, Type) ->
?WARNING_MSG("SSL connection is not supported for ~s", [Type]).
get_ssl_opts(ssl, Host) ->
- Opts1 = case ejabberd_config:get_option({sql_ssl_certfile, Host}) of
+ Opts1 = case ejabberd_option:sql_ssl_certfile(Host) of
undefined -> [];
CertFile -> [{certfile, CertFile}]
end,
- Opts2 = case ejabberd_config:get_option({sql_ssl_cafile, Host}) of
+ Opts2 = case ejabberd_option:sql_ssl_cafile(Host) of
undefined -> Opts1;
CAFile -> [{cacertfile, CAFile}|Opts1]
end,
- case ejabberd_config:get_option({sql_ssl_verify, Host}, false) of
+ case ejabberd_option:sql_ssl_verify(Host) of
true ->
case lists:keymember(cacertfile, 1, Opts2) of
true ->
@@ -1068,9 +1053,12 @@ get_ssl_opts(tcp, _) ->
[].
init_mssql(Host) ->
- Server = ejabberd_config:get_option({sql_server, Host}, <<"localhost">>),
- Port = ejabberd_config:get_option({sql_port, Host}, ?MSSQL_PORT),
- DB = ejabberd_config:get_option({sql_database, Host}, <<"ejabberd">>),
+ Server = ejabberd_option:sql_server(Host),
+ Port = ejabberd_option:sql_port(Host),
+ DB = case ejabberd_option:sql_database(Host) of
+ undefined -> <<"ejabberd">>;
+ D -> D
+ end,
FreeTDS = io_lib:fwrite("[~s]~n"
"\thost = ~s~n"
"\tport = ~p~n"
@@ -1142,8 +1130,7 @@ fsm_limit_opts() ->
ejabberd_config:fsm_limit_opts([]).
query_timeout(LServer) ->
- timer:seconds(
- ejabberd_config:get_option({sql_query_timeout, LServer}, 60)).
+ ejabberd_option:sql_query_timeout(LServer).
%% ***IMPORTANT*** This error format requires extended_errors turned on.
extended_error({"08S01", _, Reason}) ->
@@ -1186,31 +1173,3 @@ check_error({error, Why}, Query) ->
{error, Err};
check_error(Result, _Query) ->
Result.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(sql_database) -> fun iolist_to_binary/1;
-opt_type(sql_keepalive_interval) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_password) -> fun iolist_to_binary/1;
-opt_type(sql_port) ->
- fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
-opt_type(sql_server) -> fun iolist_to_binary/1;
-opt_type(sql_username) -> fun iolist_to_binary/1;
-opt_type(sql_ssl) -> fun(B) when is_boolean(B) -> B end;
-opt_type(sql_ssl_verify) -> fun(B) when is_boolean(B) -> B end;
-opt_type(sql_ssl_certfile) -> fun ejabberd_pkix:try_certfile/1;
-opt_type(sql_ssl_cafile) -> fun ejabberd_pkix:try_certfile/1;
-opt_type(sql_query_timeout) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_connect_timeout) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_queue_type) ->
- fun(ram) -> ram; (file) -> file end;
-opt_type(new_sql_schema) -> fun(B) when is_boolean(B) -> B end;
-opt_type(_) ->
- [sql_database, sql_keepalive_interval,
- sql_password, sql_port, sql_server,
- sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile,
- sql_ssl_cafile, sql_queue_type, sql_query_timeout,
- sql_connect_timeout,
- new_sql_schema].
diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl
index 2497c2a74..0896b4b1a 100644
--- a/src/ejabberd_sql_pt.erl
+++ b/src/ejabberd_sql_pt.erl
@@ -28,9 +28,7 @@
%% API
-export([parse_transform/2, format_error/1]).
-%-export([parse/2]).
-
--include("ejabberd_sql_pt.hrl").
+-include("ejabberd_sql.hrl").
-record(state, {loc,
'query' = [],
@@ -66,10 +64,8 @@
%% Description:
%%--------------------------------------------------------------------
parse_transform(AST, _Options) ->
- %io:format("PT: ~p~nOpts: ~p~n", [AST, Options]),
put(warnings, []),
NewAST = top_transform(AST),
- %io:format("NewPT: ~p~n", [NewAST]),
NewAST ++ get(warnings).
@@ -141,7 +137,6 @@ transform(Form) ->
case erl_syntax:attribute_arguments(Form) of
[M | _] ->
Module = erl_syntax:atom_value(M),
- %io:format("module ~p~n", [Module]),
put(?MOD, Module),
Form;
_ ->
@@ -158,11 +153,7 @@ top_transform(Forms) when is_list(Forms) ->
lists:map(
fun(Form) ->
try
- Form2 = erl_syntax_lib:map(
- fun(Node) ->
- %io:format("asd ~p~n", [Node]),
- transform(Node)
- end, Form),
+ Form2 = erl_syntax_lib:map(fun transform/1, Form),
Form3 = erl_syntax:revert(Form2),
Form3
catch
@@ -517,7 +508,6 @@ parse_upsert(Fields) ->
"a constant string"})
end
end, {[], 0}, Fields),
- %io:format("upsert ~p~n", [{Fields, Fs}]),
Fs.
%% key | {Update}
diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl
index f16c23a00..b5553ec40 100644
--- a/src/ejabberd_sql_sup.erl
+++ b/src/ejabberd_sql_sup.erl
@@ -25,23 +25,14 @@
-module(ejabberd_sql_sup).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-export([start_link/1, init/1, add_pid/2, remove_pid/2,
- get_pids/1, get_random_pid/1, transform_options/1,
- reload/1, opt_type/1]).
+ get_pids/1, get_random_pid/1, reload/1]).
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
--define(PGSQL_PORT, 5432).
--define(MYSQL_PORT, 3306).
--define(DEFAULT_POOL_SIZE, 10).
--define(DEFAULT_SQL_START_INTERVAL, 30).
--define(CONNECT_TIMEOUT, 500).
-
-record(sql_pool, {host :: binary(),
pid :: pid()}).
@@ -57,7 +48,7 @@ start_link(Host) ->
?MODULE, [Host]).
init([Host]) ->
- Type = ejabberd_config:get_option({sql_type, Host}, odbc),
+ Type = ejabberd_option:sql_type(Host),
PoolSize = get_pool_size(Type, Host),
case Type of
sqlite ->
@@ -71,7 +62,7 @@ init([Host]) ->
[child_spec(I, Host) || I <- lists:seq(1, PoolSize)]}}.
reload(Host) ->
- Type = ejabberd_config:get_option({sql_type, Host}, odbc),
+ Type = ejabberd_option:sql_type(Host),
NewPoolSize = get_pool_size(Type, Host),
OldPoolSize = ets:select_count(
sql_pool,
@@ -125,12 +116,7 @@ remove_pid(Host, Pid) ->
-spec get_pool_size(atom(), binary()) -> pos_integer().
get_pool_size(SQLType, Host) ->
- PoolSize = ejabberd_config:get_option(
- {sql_pool_size, Host},
- case SQLType of
- sqlite -> 1;
- _ -> ?DEFAULT_POOL_SIZE
- end),
+ PoolSize = ejabberd_option:sql_pool_size(Host),
if PoolSize > 1 andalso SQLType == sqlite ->
?WARNING_MSG("it's not recommended to set sql_pool_size > 1 for "
"sqlite, because it may cause race conditions", []);
@@ -140,31 +126,10 @@ get_pool_size(SQLType, Host) ->
PoolSize.
child_spec(I, Host) ->
- StartInterval = ejabberd_config:get_option(
- {sql_start_interval, Host},
- ?DEFAULT_SQL_START_INTERVAL),
- {I, {ejabberd_sql, start_link, [Host, timer:seconds(StartInterval)]},
+ StartInterval = ejabberd_option:sql_start_interval(Host),
+ {I, {ejabberd_sql, start_link, [Host, StartInterval]},
transient, 2000, worker, [?MODULE]}.
-transform_options(Opts) ->
- lists:foldl(fun transform_options/2, [], Opts).
-
-transform_options({odbc_server, {Type, Server, Port, DB, User, Pass}}, Opts) ->
- [{sql_type, Type},
- {sql_server, Server},
- {sql_port, Port},
- {sql_database, DB},
- {sql_username, User},
- {sql_password, Pass}|Opts];
-transform_options({odbc_server, {mysql, Server, DB, User, Pass}}, Opts) ->
- transform_options({odbc_server, {mysql, Server, ?MYSQL_PORT, DB, User, Pass}}, Opts);
-transform_options({odbc_server, {pgsql, Server, DB, User, Pass}}, Opts) ->
- transform_options({odbc_server, {pgsql, Server, ?PGSQL_PORT, DB, User, Pass}}, Opts);
-transform_options({odbc_server, {sqlite, DB}}, Opts) ->
- transform_options({odbc_server, {sqlite, DB}}, Opts);
-transform_options(Opt, Opts) ->
- [Opt|Opts].
-
check_sqlite_db(Host) ->
DB = ejabberd_sql:sqlite_db(Host),
File = ejabberd_sql:sqlite_file(Host),
@@ -234,11 +199,3 @@ read_lines(Fd, File, Acc) ->
?ERROR_MSG("Failed read from lite.sql, reason: ~p", [Err]),
[]
end.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(sql_pool_size) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(sql_start_interval) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(_) ->
- [sql_pool_size, sql_start_interval].
diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl
index 159c576f4..e48183398 100644
--- a/src/ejabberd_stun.erl
+++ b/src/ejabberd_stun.erl
@@ -57,6 +57,7 @@ tcp_init(Socket, Opts) ->
ejabberd:start_app(stun),
stun:tcp_init(Socket, prepare_turn_opts(Opts)).
+-dialyzer({nowarn_function, udp_init/2}).
udp_init(Socket, Opts) ->
ejabberd:start_app(stun),
stun:udp_init(Socket, prepare_turn_opts(Opts)).
@@ -83,11 +84,11 @@ prepare_turn_opts(Opts) ->
prepare_turn_opts(Opts, _UseTurn = false) ->
set_certfile(Opts);
prepare_turn_opts(Opts, _UseTurn = true) ->
- NumberOfMyHosts = length(ejabberd_config:get_myhosts()),
+ NumberOfMyHosts = length(ejabberd_option:hosts()),
case proplists:get_value(turn_ip, Opts) of
undefined ->
- ?WARNING_MSG("option 'turn_ip' is undefined, "
- "more likely the TURN relay won't be working "
+ ?WARNING_MSG("Option 'turn_ip' is undefined, "
+ "most likely the TURN relay won't be working "
"properly", []);
_ ->
ok
@@ -98,11 +99,11 @@ prepare_turn_opts(Opts, _UseTurn = true) ->
Realm = case proplists:get_value(auth_realm, Opts) of
undefined when AuthType == user ->
if NumberOfMyHosts > 1 ->
- ?WARNING_MSG("you have several virtual "
+ ?WARNING_MSG("You have several virtual "
"hosts configured, but option "
"'auth_realm' is undefined and "
"'auth_type' is set to 'user', "
- "more likely the TURN relay won't "
+ "most likely the TURN relay won't "
"be working properly. Using ~s as "
"a fallback", [ejabberd_config:get_myname()]);
true ->
@@ -127,44 +128,32 @@ set_certfile(Opts) ->
{ok, CertFile} ->
[{certfile, CertFile}|Opts];
error ->
- case ejabberd_config:get_option({domain_certfile, Realm}) of
- undefined ->
- Opts;
- CertFile ->
- [{certfile, CertFile}|Opts]
- end
+ Opts
end
end.
listen_opt_type(use_turn) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
+listen_opt_type(ip) ->
+ econf:ipv4();
listen_opt_type(turn_ip) ->
- fun(S) ->
- {ok, Addr} = inet_parse:ipv4_address(binary_to_list(S)),
- Addr
- end;
+ econf:ipv4();
listen_opt_type(auth_type) ->
- fun(anonymous) -> anonymous;
- (user) -> user
- end;
+ econf:enum([anonymous, user]);
listen_opt_type(auth_realm) ->
- fun iolist_to_binary/1;
+ econf:binary();
listen_opt_type(turn_min_port) ->
- fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
+ econf:int(1025, 65535);
listen_opt_type(turn_max_port) ->
- fun(P) when is_integer(P), P > 1024, P < 65536 -> P end;
+ econf:int(1025, 65535);
listen_opt_type(turn_max_allocations) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
listen_opt_type(turn_max_permissions) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
listen_opt_type(server_name) ->
- fun iolist_to_binary/1.
+ econf:binary();
+listen_opt_type(certfile) ->
+ econf:pem().
listen_options() ->
[{shaper, none},
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index edf15e438..1868a85ce 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -30,7 +30,7 @@
-export([start_link/0, init/1]).
--define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
+-define(SHUTDOWN_TIMEOUT, timer:minutes(1)).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
@@ -53,10 +53,9 @@ init([]) ->
simple_supervisor(ejabberd_service),
worker(acl),
worker(ejabberd_shaper),
+ supervisor(ejabberd_db_sup),
supervisor(ejabberd_backend_sup),
supervisor(ejabberd_rdbms),
- supervisor(ejabberd_riak_sup),
- supervisor(ejabberd_redis_sup),
worker(ejabberd_iq),
worker(ejabberd_router),
worker(ejabberd_router_multicast),
diff --git a/src/ejabberd_system_monitor.erl b/src/ejabberd_system_monitor.erl
index e5af257a8..1a4e3c765 100644
--- a/src/ejabberd_system_monitor.erl
+++ b/src/ejabberd_system_monitor.erl
@@ -25,13 +25,12 @@
-module(ejabberd_system_monitor).
-behaviour(gen_event).
--behaviour(ejabberd_config).
-author('alexey@process-one.net').
-author('ekhramtsov@process-one.net').
%% API
--export([start/0, opt_type/1, config_reloaded/0]).
+-export([start/0, config_reloaded/0]).
%% gen_event callbacks
-export([init/1, handle_event/2, handle_call/2,
@@ -43,8 +42,8 @@
-define(CHECK_INTERVAL, timer:seconds(30)).
--record(state, {tref :: reference(),
- mref :: reference()}).
+-record(state, {tref :: undefined | reference(),
+ mref :: undefined | reference()}).
-record(proc_stat, {qlen :: non_neg_integer(),
memory :: non_neg_integer(),
initial_call :: mfa(),
@@ -134,7 +133,7 @@ handle_overload(State) ->
handle_overload(_State, Procs) ->
AppPids = get_app_pids(),
{TotalMsgs, ProcsNum, Apps, Stats} = overloaded_procs(AppPids, Procs),
- MaxMsgs = ejabberd_config:get_option(oom_queue, 10000),
+ MaxMsgs = ejabberd_option:oom_queue(),
if TotalMsgs >= MaxMsgs ->
SortedStats = lists:reverse(lists:keysort(#proc_stat.qlen, Stats)),
error_logger:warning_msg(
@@ -224,14 +223,14 @@ restart_timer(State) ->
TRef = erlang:start_timer(?CHECK_INTERVAL, self(), handle_overload),
State#state{tref = TRef}.
--spec format_apps(dict:dict()) -> io:data().
+-spec format_apps(dict:dict()) -> iodata().
format_apps(Apps) ->
AppList = lists:reverse(lists:keysort(2, dict:to_list(Apps))),
string:join(
[io_lib:format("~p (~b msgs)", [App, Msgs]) || {App, Msgs} <- AppList],
", ").
--spec format_top_procs([proc_stat()]) -> io:data().
+-spec format_top_procs([proc_stat()]) -> iodata().
format_top_procs(Stats) ->
Stats1 = lists:sublist(Stats, 5),
string:join(
@@ -241,7 +240,7 @@ format_top_procs(Stats) ->
end,Stats1),
io_lib:nl()).
--spec format_proc(proc_stat()) -> io:data().
+-spec format_proc(proc_stat()) -> iodata().
format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
current_function = CurrFun, ancestors = Ancs,
application = App}) ->
@@ -250,7 +249,7 @@ format_proc(#proc_stat{qlen = Len, memory = Mem, initial_call = InitCall,
"current_function = ~s, ancestors = ~w, application = ~w",
[Len, Mem, format_mfa(InitCall), format_mfa(CurrFun), Ancs, App]).
--spec format_mfa(mfa()) -> io:data().
+-spec format_mfa(mfa()) -> iodata().
format_mfa({M, F, A}) when is_atom(M), is_atom(F), is_integer(A) ->
io_lib:format("~s:~s/~b", [M, F, A]);
format_mfa(WTF) ->
@@ -258,7 +257,7 @@ format_mfa(WTF) ->
-spec kill([proc_stat()], non_neg_integer()) -> ok.
kill(Stats, Threshold) ->
- case ejabberd_config:get_option(oom_killer, true) of
+ case ejabberd_option:oom_killer() of
true ->
do_kill(Stats, Threshold);
false ->
@@ -308,7 +307,7 @@ kill_proc(Pid) ->
-spec set_oom_watermark() -> ok.
set_oom_watermark() ->
- WaterMark = ejabberd_config:get_option(oom_watermark, 80),
+ WaterMark = ejabberd_option:oom_watermark(),
memsup:set_sysmem_high_watermark(WaterMark/100).
-spec maybe_restart_app(atom()) -> any().
@@ -316,11 +315,3 @@ maybe_restart_app(lager) ->
ejabberd_logger:restart();
maybe_restart_app(_) ->
ok.
-
-opt_type(oom_killer) ->
- fun(B) when is_boolean(B) -> B end;
-opt_type(oom_watermark) ->
- fun(I) when is_integer(I), I>0, I<100 -> I end;
-opt_type(oom_queue) ->
- fun(I) when is_integer(I), I>0 -> I end;
-opt_type(_) -> [oom_killer, oom_watermark, oom_queue].
diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl
index a112eac35..5eaef7582 100644
--- a/src/ejabberd_web_admin.erl
+++ b/src/ejabberd_web_admin.erl
@@ -27,13 +27,11 @@
-module(ejabberd_web_admin).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-export([process/2, list_users/4,
list_users_in_diapason/4, pretty_print_xml/1,
- term_to_id/1, opt_type/1]).
+ term_to_id/1]).
-include("logger.hrl").
@@ -132,7 +130,7 @@ is_allowed_path([<<"admin">> | Path], JID) ->
is_allowed_path(Path, JID);
is_allowed_path(Path, JID) ->
{HostOfRule, AccessRule} = get_acl_rule(Path, 'GET'),
- acl:any_rules_allowed(HostOfRule, AccessRule, JID).
+ any_rules_allowed(HostOfRule, AccessRule, JID).
%% @spec(Path) -> URL
%% where Path = [string()]
@@ -186,8 +184,8 @@ process([<<"server">>, SHost | RPath] = Path,
AJID = get_jid(Auth, HostHTTP, Method),
process_admin(Host,
Request#request{path = RPath,
- auth = {auth_jid, Auth, AJID},
- us = {User, Server}});
+ us = {User, Server}},
+ AJID);
{unauthorized, <<"no-auth-provided">>} ->
{401,
[{<<"WWW-Authenticate">>,
@@ -218,8 +216,8 @@ process(RPath,
AJID = get_jid(Auth, HostHTTP, Method),
process_admin(global,
Request#request{path = RPath,
- auth = {auth_jid, Auth, AJID},
- us = {User, Server}});
+ us = {User, Server}},
+ AJID);
{unauthorized, <<"no-auth-provided">>} ->
{401,
[{<<"WWW-Authenticate">>,
@@ -262,8 +260,8 @@ get_auth_account(HostOfRule, AccessRule, User, Server,
Pass) ->
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true ->
- case acl:any_rules_allowed(HostOfRule, AccessRule,
- jid:make(User, Server))
+ case any_rules_allowed(HostOfRule, AccessRule,
+ jid:make(User, Server))
of
false -> {unauthorized, <<"unprivileged-account">>};
true -> {ok, {User, Server}}
@@ -342,7 +340,6 @@ make_xhtml(Els, Host, Node, Lang, JID) ->
?AC(<<"https://www.process-one.net/">>, <<"ProcessOne, leader in messaging and push solutions">>)]
)])])])]}}.
-direction(ltr) -> [{<<"dir">>, <<"ltr">>}];
direction(<<"he">>) -> [{<<"dir">>, <<"rtl">>}];
direction(_) -> [].
@@ -395,9 +392,7 @@ logo_fill() ->
%%%==================================
%%%% process_admin
-process_admin(global,
- #request{path = [], auth = {_, _, AJID},
- lang = Lang}) ->
+process_admin(global, #request{path = [], lang = Lang}, AJID) ->
make_xhtml((?H1GL((?T(<<"Administration">>)), <<"">>,
<<"Contents">>))
++
@@ -406,290 +401,65 @@ process_admin(global,
|| {MIU, MIN}
<- get_menu_items(global, cluster, Lang, AJID)])],
global, Lang, AJID);
-process_admin(Host,
- #request{path = [], auth = {_, _Auth, AJID},
- lang = Lang}) ->
+process_admin(Host, #request{path = [], lang = Lang}, AJID) ->
make_xhtml([?XCT(<<"h1">>, <<"Administration">>),
?XE(<<"ul">>,
[?LI([?ACT(MIU, MIN)])
|| {MIU, MIN}
<- get_menu_items(Host, cluster, Lang, AJID)])],
Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"style.css">>]}) ->
+process_admin(Host, #request{path = [<<"style.css">>]}, _) ->
{200,
[{<<"Content-Type">>, <<"text/css">>}, last_modified(),
cache_control_public()],
css(Host)};
-process_admin(_Host,
- #request{path = [<<"favicon.ico">>]}) ->
+process_admin(_Host, #request{path = [<<"favicon.ico">>]}, _) ->
{200,
[{<<"Content-Type">>, <<"image/x-icon">>},
last_modified(), cache_control_public()],
favicon()};
-process_admin(_Host,
- #request{path = [<<"logo.png">>]}) ->
+process_admin(_Host, #request{path = [<<"logo.png">>]}, _) ->
{200,
[{<<"Content-Type">>, <<"image/png">>}, last_modified(),
cache_control_public()],
logo()};
-process_admin(_Host,
- #request{path = [<<"logo-fill.png">>]}) ->
+process_admin(_Host, #request{path = [<<"logo-fill.png">>]}, _) ->
{200,
[{<<"Content-Type">>, <<"image/png">>}, last_modified(),
cache_control_public()],
logo_fill()};
-process_admin(_Host,
- #request{path = [<<"additions.js">>]}) ->
+process_admin(_Host, #request{path = [<<"additions.js">>]}, _) ->
{200,
[{<<"Content-Type">>, <<"text/javascript">>},
last_modified(), cache_control_public()],
additions_js()};
-process_admin(Host,
- #request{path = [<<"acls-raw">>], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang}) ->
- Res = case lists:keysearch(<<"acls">>, 1, Query) of
- {value, {_, String}} ->
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, NewACLs} ->
- case catch acl:add_list(Host, NewACLs, true) of
- ok -> ok;
- _ -> error
- end;
- _ -> error
- end;
- _ -> error
- end;
- _ -> nothing
- end,
- ACLs = lists:keysort(2,
- mnesia:dirty_select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}])),
- {NumLines, ACLsP} = term_to_paragraph(ACLs, 80),
- make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"acldefinition">>, <<"ACL Definition">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
- [?TEXTAREA(<<"acls">>,
- (integer_to_binary(lists:max([16,
- NumLines]))),
- <<"80">>, <<(iolist_to_binary(ACLsP))/binary, ".">>),
- ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{method = Method, path = [<<"acls">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Res = case Method of
- 'POST' ->
- case catch acl_parse_query(Host, Query) of
- {'EXIT', _} -> error;
- NewACLs ->
- ?INFO_MSG("NewACLs at ~s: ~p", [Host, NewACLs]),
- case catch acl:add_list(Host, NewACLs, true) of
- ok -> ok;
- _ -> error
- end
- end;
- _ -> nothing
- end,
- ACLs = lists:keysort(2,
- mnesia:dirty_select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}])),
- make_xhtml((?H1GL((?T(<<"Access Control Lists">>)),
- <<"acldefinition">>, <<"ACL Definition">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"p">>, direction(ltr), [?ACT(<<"../acls-raw/">>, <<"Raw">>)])] ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
- [acls_to_xhtml(ACLs), ?BR,
- ?INPUTT(<<"submit">>, <<"delete">>,
- <<"Delete Selected">>),
- ?C(<<" ">>),
- ?INPUTT(<<"submit">>, <<"submit">>,
- <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"access-raw">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
- SetAccess = fun (Rs) ->
- mnesia:transaction(fun () ->
- Os = mnesia:select(access,
- [{{access,
- {'$1',
- Host},
- '$2'},
- [],
- ['$_']}]),
- lists:foreach(fun (O) ->
- mnesia:delete_object(O)
- end,
- Os),
- lists:foreach(fun ({access,
- Name,
- Rules}) ->
- mnesia:write({access,
- {Name,
- Host},
- Rules})
- end,
- Rs)
- end)
- end,
- Res = case lists:keysearch(<<"access">>, 1, Query) of
- {value, {_, String}} ->
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, Rs} ->
- case SetAccess(Rs) of
- {atomic, _} -> ok;
- _ -> error
- end;
- _ -> error
- end;
- _ -> error
- end;
- _ -> nothing
- end,
- Access = mnesia:dirty_select(access,
- [{{access, {'$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- {NumLines, AccessP} = term_to_paragraph(lists:keysort(2,Access), 80),
- make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"accessrights">>, <<"Access Rights">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
- [?TEXTAREA(<<"access">>,
- (integer_to_binary(lists:max([16,
- NumLines]))),
- <<"80">>, <<(iolist_to_binary(AccessP))/binary, ".">>),
- ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{method = Method, path = [<<"access">>],
- q = Query, auth = {_, _Auth, AJID}, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Res = case Method of
- 'POST' ->
- case catch access_parse_query(Host, Query) of
- {'EXIT', _} -> error;
- ok -> ok
- end;
- _ -> nothing
- end,
- AccessRules = mnesia:dirty_select(access,
- [{{access, {'$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- make_xhtml((?H1GL((?T(<<"Access Rules">>)),
- <<"accessrights">>, <<"Access Rights">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"p">>, direction(ltr), [?ACT(<<"../access-raw/">>, <<"Raw">>)])]
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}]++direction(ltr),
- [access_rules_to_xhtml(AccessRules, Lang), ?BR,
- ?INPUTT(<<"submit">>, <<"delete">>,
- <<"Delete Selected">>)])],
- Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"access">>, SName], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang}) ->
- ?DEBUG("query: ~p", [Query]),
- Name = misc:binary_to_atom(SName),
- Res = case lists:keysearch(<<"rules">>, 1, Query) of
- {value, {_, String}} ->
- case parse_access_rule(String) of
- {ok, Rs} ->
- ejabberd_config:add_option({access, Name, Host},
- Rs),
- ok;
- _ -> error
- end;
- _ -> nothing
- end,
- Rules = ejabberd_config:get_option({access, Name, Host}, []),
- make_xhtml([?XC(<<"h1">>,
- (str:format(
- ?T(<<"~s access rule configuration">>),
- [SName])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [access_rule_to_xhtml(Rules), ?BR,
- ?INPUTT(<<"submit">>, <<"submit">>, <<"Submit">>)])],
- Host, Lang, AJID);
-process_admin(global,
- #request{path = [<<"vhosts">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
+process_admin(global, #request{path = [<<"vhosts">>], lang = Lang}, AJID) ->
Res = list_vhosts(Lang, AJID),
make_xhtml((?H1GL((?T(<<"Virtual Hosts">>)),
<<"virtualhosting">>, <<"Virtual Hosting">>))
++ Res,
global, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"users">>], q = Query,
- auth = {_, _Auth, AJID}, lang = Lang})
+process_admin(Host, #request{path = [<<"users">>], q = Query,
+ lang = Lang}, AJID)
when is_binary(Host) ->
Res = list_users(Host, Query, Lang, fun url_func/1),
make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host,
Lang, AJID);
-process_admin(Host,
- #request{path = [<<"users">>, Diap],
- auth = {_, _Auth, AJID}, lang = Lang})
+process_admin(Host, #request{path = [<<"users">>, Diap],
+ lang = Lang}, AJID)
when is_binary(Host) ->
Res = list_users_in_diapason(Host, Diap, Lang,
fun url_func/1),
make_xhtml([?XCT(<<"h1">>, <<"Users">>)] ++ Res, Host,
Lang, AJID);
-process_admin(Host,
- #request{path = [<<"online-users">>],
- auth = {_, _Auth, AJID}, lang = Lang})
+process_admin(Host, #request{path = [<<"online-users">>],
+ lang = Lang}, AJID)
when is_binary(Host) ->
Res = list_online_users(Host, Lang),
make_xhtml([?XCT(<<"h1">>, <<"Online Users">>)] ++ Res,
Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"last-activity">>],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang})
+process_admin(Host, #request{path = [<<"last-activity">>],
+ q = Query, lang = Lang}, AJID)
when is_binary(Host) ->
?DEBUG("query: ~p", [Query]),
Month = case lists:keysearch(<<"period">>, 1, Query) of
@@ -730,15 +500,12 @@ process_admin(Host,
<<"Show Integral Table">>)])]
++ Res,
Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"stats">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
+process_admin(Host, #request{path = [<<"stats">>], lang = Lang}, AJID) ->
Res = get_stats(Host, Lang),
make_xhtml([?XCT(<<"h1">>, <<"Statistics">>)] ++ Res,
Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"user">>, U],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
+process_admin(Host, #request{path = [<<"user">>, U],
+ q = Query, lang = Lang}, AJID) ->
case ejabberd_auth:user_exists(U, Host) of
true ->
Res = user_info(U, Host, Query, Lang),
@@ -747,14 +514,11 @@ process_admin(Host,
make_xhtml([?XCT(<<"h1">>, <<"Not Found">>)], Host,
Lang, AJID)
end;
-process_admin(Host,
- #request{path = [<<"nodes">>], auth = {_, _Auth, AJID},
- lang = Lang}) ->
+process_admin(Host, #request{path = [<<"nodes">>], lang = Lang}, AJID) ->
Res = get_nodes(Lang),
make_xhtml(Res, Host, Lang, AJID);
-process_admin(Host,
- #request{path = [<<"node">>, SNode | NPath],
- auth = {_, _Auth, AJID}, q = Query, lang = Lang}) ->
+process_admin(Host, #request{path = [<<"node">>, SNode | NPath],
+ q = Query, lang = Lang}, AJID) ->
case search_running_node(SNode) of
false ->
make_xhtml([?XCT(<<"h1">>, <<"Node not found">>)], Host,
@@ -765,9 +529,7 @@ process_admin(Host,
end;
%%%==================================
%%%% process_admin default case
-process_admin(Host,
- #request{lang = Lang, auth = {_, _Auth, AJID}} =
- Request) ->
+process_admin(Host, #request{lang = Lang} = Request, AJID) ->
Res = case Host of
global ->
ejabberd_hooks:run_fold(
@@ -785,286 +547,15 @@ process_admin(Host,
_ -> make_xhtml(Res, Host, Lang, AJID)
end.
-%%%==================================
-%%%% acl
-
-acls_to_xhtml(ACLs) ->
- ?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- (lists:map(fun ({acl, Name, Spec} = ACL) ->
- SName = iolist_to_binary(atom_to_list(Name)),
- ID = term_to_id(ACL),
- ?XE(<<"tr">>,
- ([?XE(<<"td">>,
- [?INPUT(<<"checkbox">>,
- <<"selected">>, ID)]),
- ?XC(<<"td">>, SName)]
- ++ acl_spec_to_xhtml(ID, Spec)))
- end,
- ACLs)
- ++
- [?XE(<<"tr">>,
- ([?X(<<"td">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"namenew">>, <<"">>)])]
- ++ acl_spec_to_xhtml(<<"new">>, {user, <<"">>})))]))]).
-
-acl_spec_to_text({user, {U, S}}) ->
- {user, <<U/binary, "@", S/binary>>};
-acl_spec_to_text({user, U}) -> {user, U};
-acl_spec_to_text({server, S}) -> {server, S};
-acl_spec_to_text({user_regexp, {RU, S}}) ->
- {user_regexp, <<RU/binary, "@", S/binary>>};
-acl_spec_to_text({user_regexp, RU}) ->
- {user_regexp, RU};
-acl_spec_to_text({server_regexp, RS}) ->
- {server_regexp, RS};
-acl_spec_to_text({node_regexp, {RU, RS}}) ->
- {node_regexp, <<RU/binary, "@", RS/binary>>};
-acl_spec_to_text({user_glob, {RU, S}}) ->
- {user_glob, <<RU/binary, "@", S/binary>>};
-acl_spec_to_text({user_glob, RU}) -> {user_glob, RU};
-acl_spec_to_text({server_glob, RS}) ->
- {server_glob, RS};
-acl_spec_to_text({node_glob, {RU, RS}}) ->
- {node_glob, <<RU/binary, "@", RS/binary>>};
-acl_spec_to_text(all) -> {all, <<"">>};
-acl_spec_to_text({ip, {IP, L}}) -> {ip, <<(misc:ip_to_list(IP))/binary, "/",
- (integer_to_binary(L))/binary>>};
-acl_spec_to_text(Spec) -> {raw, term_to_string(Spec)}.
-
-acl_spec_to_xhtml(ID, Spec) ->
- {Type, Str} = acl_spec_to_text(Spec),
- [acl_spec_select(ID, Type), ?ACLINPUT(Str)].
-
-acl_spec_select(ID, Opt) ->
- ?XE(<<"td">>,
- [?XAE(<<"select">>,
- [{<<"name">>, <<"type", ID/binary>>}],
- (lists:map(fun (O) ->
- Sel = if O == Opt ->
- [{<<"selected">>,
- <<"selected">>}];
- true -> []
- end,
- ?XAC(<<"option">>,
- (Sel ++
- [{<<"value">>,
- iolist_to_binary(atom_to_list(O))}]),
- (iolist_to_binary(atom_to_list(O))))
- end,
- [user, server, user_regexp, server_regexp, node_regexp,
- user_glob, server_glob, node_glob, all, ip, raw])))]).
-
-%% @spec (T::any()) -> StringLine::string()
-term_to_string(T) ->
- StringParagraph =
- (str:format("~1000000p", [T])),
- ejabberd_regexp:greplace(StringParagraph, <<"\\n ">>,
- <<"">>).
-
-%% @spec (T::any(), Cols::integer()) -> {NumLines::integer(), Paragraph::string()}
-term_to_paragraph(T, Cols) ->
- Paragraph = iolist_to_binary(io_lib:print(T, 1, Cols, -1)),
- FieldList = ejabberd_regexp:split(Paragraph, <<"\n">>),
- NumLines = length(FieldList),
- {NumLines, Paragraph}.
-
term_to_id(T) -> base64:encode((term_to_binary(T))).
-acl_parse_query(Host, Query) ->
- ACLs = mnesia:dirty_select(acl,
- [{{acl, {'$1', Host}, '$2'}, [],
- [{{acl, '$1', '$2'}}]}]),
- case lists:keysearch(<<"submit">>, 1, Query) of
- {value, _} -> acl_parse_submit(ACLs, Query);
- _ ->
- case lists:keysearch(<<"delete">>, 1, Query) of
- {value, _} -> acl_parse_delete(ACLs, Query)
- end
- end.
-
-acl_parse_submit(ACLs, Query) ->
- NewACLs = lists:map(fun ({acl, Name, Spec} = ACL) ->
- ID = term_to_id(ACL),
- case {lists:keysearch(<<"type", ID/binary>>, 1,
- Query),
- lists:keysearch(<<"value", ID/binary>>, 1,
- Query)}
- of
- {{value, {_, T}}, {value, {_, V}}} ->
- {Type, Str} = acl_spec_to_text(Spec),
- case
- {iolist_to_binary(atom_to_list(Type)),
- Str}
- of
- {T, V} -> ACL;
- _ ->
- NewSpec = string_to_spec(T, V),
- {acl, Name, NewSpec}
- end;
- _ -> ACL
- end
- end,
- ACLs),
- NewACL = case {lists:keysearch(<<"namenew">>, 1, Query),
- lists:keysearch(<<"typenew">>, 1, Query),
- lists:keysearch(<<"valuenew">>, 1, Query)}
- of
- {{value, {_, <<"">>}}, _, _} -> [];
- {{value, {_, N}}, {value, {_, T}}, {value, {_, V}}} ->
- NewName = misc:binary_to_atom(N),
- NewSpec = string_to_spec(T, V),
- [{acl, NewName, NewSpec}];
- _ -> []
- end,
- NewACLs ++ NewACL.
-
-string_to_spec(<<"user">>, Val) ->
- string_to_spec2(user, Val);
-string_to_spec(<<"server">>, Val) -> {server, Val};
-string_to_spec(<<"user_regexp">>, Val) ->
- string_to_spec2(user_regexp, Val);
-string_to_spec(<<"server_regexp">>, Val) ->
- {server_regexp, Val};
-string_to_spec(<<"node_regexp">>, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jid:decode(Val),
- {node_regexp, U, S};
-string_to_spec(<<"user_glob">>, Val) ->
- string_to_spec2(user_glob, Val);
-string_to_spec(<<"server_glob">>, Val) ->
- {server_glob, Val};
-string_to_spec(<<"node_glob">>, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jid:decode(Val),
- {node_glob, U, S};
-string_to_spec(<<"ip">>, Val) ->
- [IPs, Ms] = str:tokens(Val, <<"/">>),
- {ok, IP} = inet_parse:address(binary_to_list(IPs)),
- {ip, {IP, binary_to_integer(Ms)}};
-string_to_spec(<<"all">>, _) -> all;
-string_to_spec(<<"raw">>, Val) ->
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<Val/binary, ".">>)),
- {ok, NewSpec} = erl_parse:parse_term(Tokens),
- NewSpec.
-
-string_to_spec2(ACLName, Val) ->
- #jid{luser = U, lserver = S, resource = <<"">>} =
- jid:decode(Val),
- case U of
- <<"">> -> {ACLName, S};
- _ -> {ACLName, {U, S}}
- end.
-
-acl_parse_delete(ACLs, Query) ->
- NewACLs = lists:filter(fun ({acl, _Name, _Spec} =
- ACL) ->
- ID = term_to_id(ACL),
- not lists:member({<<"selected">>, ID}, Query)
- end,
- ACLs),
- NewACLs.
-
-access_rules_to_xhtml(AccessRules, Lang) ->
- ?XAE(<<"table">>, [],
- [?XE(<<"tbody">>,
- (lists:map(fun ({access, Name, Rules} = Access) ->
- SName = iolist_to_binary(atom_to_list(Name)),
- ID = term_to_id(Access),
- ?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUT(<<"checkbox">>,
- <<"selected">>, ID)]),
- ?XE(<<"td">>,
- [?AC(<<SName/binary, "/">>, SName)]),
- ?XC(<<"td">>, (term_to_string(Rules)))])
- end,
- lists:keysort(2,AccessRules))
- ++
- [?XE(<<"tr">>,
- [?X(<<"td">>),
- ?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"namenew">>, <<"">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>, <<"addnew">>,
- <<"Add New">>)])])]))]).
-
-access_parse_query(Host, Query) ->
- AccessRules = mnesia:dirty_select(access,
- [{{access, {'$1', Host}, '$2'}, [],
- [{{access, '$1', '$2'}}]}]),
- case lists:keysearch(<<"addnew">>, 1, Query) of
- {value, _} ->
- access_parse_addnew(AccessRules, Host, Query);
- _ ->
- case lists:keysearch(<<"delete">>, 1, Query) of
- {value, _} ->
- access_parse_delete(AccessRules, Host, Query)
- end
- end.
-
-access_parse_addnew(_AccessRules, Host, Query) ->
- case lists:keysearch(<<"namenew">>, 1, Query) of
- {value, {_, String}} when String /= <<"">> ->
- Name = misc:binary_to_atom(String),
- ejabberd_config:add_option({access, Name, Host},
- []),
- ok
- end.
-
-access_parse_delete(AccessRules, Host, Query) ->
- lists:foreach(fun ({access, Name, _Rules} =
- AccessRule) ->
- ID = term_to_id(AccessRule),
- case lists:member({<<"selected">>, ID}, Query) of
- true ->
- mnesia:transaction(fun () ->
- mnesia:delete({access,
- {Name,
- Host}})
- end);
- _ -> ok
- end
- end,
- AccessRules),
- ok.
-
-access_rule_to_xhtml(Rules) ->
- Text = lists:flatmap(fun ({Access, ACL} = _Rule) ->
- SAccess = element_to_list(Access),
- SACL = atom_to_list(ACL),
- [SAccess, " \t", SACL, "\n"]
- end,
- Rules),
- ?XAC(<<"textarea">>,
- [{<<"name">>, <<"rules">>}, {<<"rows">>, <<"16">>},
- {<<"cols">>, <<"80">>}],
- list_to_binary(Text)).
-
-parse_access_rule(Text) ->
- Strings = str:tokens(Text, <<"\r\n">>),
- case catch lists:flatmap(fun (String) ->
- case str:tokens(String, <<" \t">>) of
- [Access, ACL] ->
- [{list_to_element(Access),
- misc:binary_to_atom(ACL)}];
- [] -> []
- end
- end,
- Strings)
- of
- {'EXIT', _Reason} -> error;
- Rs -> {ok, Rs}
- end.
-
%%%==================================
%%%% list_vhosts
list_vhosts(Lang, JID) ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
HostsAllowed = lists:filter(fun (Host) ->
- acl:any_rules_allowed(Host,
+ any_rules_allowed(Host,
[configure, webadmin_view],
JID)
end,
@@ -1262,7 +753,7 @@ get_offlinemsg_module(Server) ->
end.
get_lastactivity_menuitem_list(Server) ->
- case gen_mod:get_module_opt(Server, mod_last, db_type) of
+ case mod_last_opt:db_type(Server) of
mnesia -> [{<<"last-activity">>, <<"Last Activity">>}];
_ -> []
end.
@@ -1282,7 +773,7 @@ get_stats(global, Lang) ->
ejabberd_auth:count_users(Host)
+ Total
end,
- 0, ejabberd_config:get_myhosts()),
+ 0, ejabberd_option:hosts()),
OutS2SNumber = ejabberd_s2s:outgoing_s2s_number(),
InS2SNumber = ejabberd_s2s:incoming_s2s_number(),
[?XAE(<<"table">>, [],
@@ -1579,8 +1070,6 @@ get_node(global, Node, [], Query, Lang) ->
[?XE(<<"ul">>,
([?LI([?ACT(<<Base/binary, "db/">>, <<"Database">>)]),
?LI([?ACT(<<Base/binary, "backup/">>, <<"Backup">>)]),
- ?LI([?ACT(<<Base/binary, "ports/">>,
- <<"Listened Ports">>)]),
?LI([?ACT(<<Base/binary, "stats/">>,
<<"Statistics">>)]),
?LI([?ACT(<<Base/binary, "update/">>, <<"Update">>)])]
@@ -1594,10 +1083,7 @@ get_node(Host, Node, [], _Query, Lang) ->
Base = get_base_path(Host, Node),
MenuItems2 = make_menu_items(Host, Node, Base, Lang),
[?XC(<<"h1">>, (str:format(?T(<<"Node ~p">>), [Node]))),
- ?XE(<<"ul">>,
- ([?LI([?ACT(<<Base/binary, "modules/">>,
- <<"Modules">>)])]
- ++ MenuItems2))];
+ ?XE(<<"ul">>, MenuItems2)];
get_node(global, Node, [<<"db">>], Query, Lang) ->
case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of
{badrpc, _Reason} ->
@@ -1830,68 +1316,6 @@ get_node(global, Node, [<<"backup">>], Query, Lang) ->
?XE(<<"td">>,
[?INPUTT(<<"submit">>, <<"import_dir">>,
<<"OK">>)])])])])])];
-get_node(global, Node, [<<"ports">>], Query, Lang) ->
- Ports = ejabberd_cluster:call(Node, ejabberd_config,
- get_local_option, [listen,
- {ejabberd_listener, validate_cfg},
- []]),
- Res = case catch node_ports_parse_query(Node, Ports,
- Query)
- of
- submitted -> ok;
- {'EXIT', _Reason} -> error;
- {is_added, ok} -> ok;
- {is_added, {error, Reason}} ->
- {error, (str:format("~p", [Reason]))};
- _ -> nothing
- end,
- NewPorts = lists:sort(ejabberd_cluster:call(Node, ejabberd_config,
- get_local_option,
- [listen,
- {ejabberd_listener, validate_cfg},
- []])),
- H1String = <<(?T(<<"Listened Ports at ">>))/binary,
- (iolist_to_binary(atom_to_list(Node)))/binary>>,
- (?H1GL(H1String, <<"listeningports">>, <<"Listening Ports">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- {error, ReasonT} ->
- [?XRES(<<(?T(<<"Error">>))/binary, ": ",
- ReasonT/binary>>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [node_ports_to_xhtml(NewPorts, Lang)])];
-get_node(Host, Node, [<<"modules">>], Query, Lang)
- when is_binary(Host) ->
- Modules = ejabberd_cluster:call(Node, gen_mod,
- loaded_modules_with_opts, [Host]),
- Res = case catch node_modules_parse_query(Host, Node,
- Modules, Query)
- of
- submitted -> ok;
- {'EXIT', Reason} -> ?ERROR_MSG("~p~n", [Reason]), error;
- _ -> nothing
- end,
- NewModules = lists:sort(ejabberd_cluster:call(Node, gen_mod,
- loaded_modules_with_opts, [Host])),
- H1String = (str:format(?T(<<"Modules at ~p">>), [Node])),
- (?H1GL(H1String, <<"modulesoverview">>,
- <<"Modules Overview">>))
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- error -> [?XREST(<<"Bad format">>)];
- nothing -> []
- end
- ++
- [?XAE(<<"form">>,
- [{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
- [node_modules_to_xhtml(NewModules, Lang)])];
get_node(global, Node, [<<"stats">>], _Query, Lang) ->
UpTime = ejabberd_cluster:call(Node, erlang, statistics,
[wall_clock]),
@@ -2170,252 +1594,6 @@ node_backup_parse_query(Node, Query) ->
<<"import_piefxis_file">>, <<"export_piefxis_dir">>,
<<"export_piefxis_host_dir">>, <<"export_sql_file">>]).
-node_ports_to_xhtml(Ports, Lang) ->
- ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}],
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Port">>), ?XCT(<<"td">>, <<"IP">>),
- ?XCT(<<"td">>, <<"Protocol">>),
- ?XCT(<<"td">>, <<"Module">>),
- ?XCT(<<"td">>, <<"Options">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun ({PortIP, Module, Opts} = _E) ->
- {_Port, SPort, _TIP, SIP, SSPort, NetProt,
- OptsClean} =
- get_port_data(PortIP, Opts),
- SModule =
- iolist_to_binary(atom_to_list(Module)),
- {NumLines, SOptsClean} =
- term_to_paragraph(OptsClean, 40),
- ?XE(<<"tr">>,
- [?XAE(<<"td">>, [{<<"size">>, <<"6">>}],
- [?C(SPort)]),
- ?XAE(<<"td">>, [{<<"size">>, <<"15">>}],
- [?C(SIP)]),
- ?XAE(<<"td">>, [{<<"size">>, <<"4">>}],
- [?C((iolist_to_binary(atom_to_list(NetProt))))]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>,
- <<"module", SSPort/binary>>,
- SModule, <<"15">>)]),
- ?XAE(<<"td">>, direction(ltr),
- [?TEXTAREA(<<"opts", SSPort/binary>>,
- (integer_to_binary(NumLines)),
- <<"35">>, SOptsClean)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"add", SSPort/binary>>,
- <<"Restart">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"delete", SSPort/binary>>,
- <<"Stop">>)])])
- end,
- Ports)
- ++
- [?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"portnew">>, <<"">>,
- <<"6">>)]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"ipnew">>, <<"0.0.0.0">>,
- <<"15">>)]),
- ?XE(<<"td">>, [make_netprot_html(<<"tcp">>)]),
- ?XE(<<"td">>,
- [?INPUTS(<<"text">>, <<"modulenew">>, <<"">>,
- <<"15">>)]),
- ?XAE(<<"td">>, direction(ltr),
- [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"35">>,
- <<"[]">>)]),
- ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
- [?INPUTT(<<"submit">>, <<"addnew">>,
- <<"Start">>)])])]))]).
-
-make_netprot_html(NetProt) ->
- ?XAE(<<"select">>, [{<<"name">>, <<"netprotnew">>}],
- (lists:map(fun (O) ->
- Sel = if O == NetProt ->
- [{<<"selected">>, <<"selected">>}];
- true -> []
- end,
- ?XAC(<<"option">>, (Sel ++ [{<<"value">>, O}]), O)
- end,
- [<<"tcp">>, <<"udp">>]))).
-
-get_port_data(PortIP, Opts) ->
- {Port, IPT, _IPV, NetProt, OptsClean} =
- ejabberd_listener:parse_listener_portip(PortIP, Opts),
- IPS = misc:ip_to_list(IPT),
- SPort = integer_to_binary(Port),
- SSPort = list_to_binary(
- lists:map(fun (N) ->
- io_lib:format("~.16b", [N])
- end,
- binary_to_list(
- erlang:md5(
- [SPort, IPS, atom_to_list(NetProt)])))),
- {Port, SPort, IPT, IPS, SSPort, NetProt, OptsClean}.
-
-node_ports_parse_query(Node, Ports, Query) ->
- lists:foreach(fun ({PortIpNetp, Module1, Opts1}) ->
- {Port, _SPort, TIP, _SIP, SSPort, NetProt,
- _OptsClean} =
- get_port_data(PortIpNetp, Opts1),
- case lists:keysearch(<<"add", SSPort/binary>>, 1,
- Query)
- of
- {value, _} ->
- PortIpNetp2 = {Port, TIP, NetProt},
- {{value, {_, SModule}}, {value, {_, SOpts}}} =
- {lists:keysearch(<<"module",
- SSPort/binary>>,
- 1, Query),
- lists:keysearch(<<"opts", SSPort/binary>>,
- 1, Query)},
- Module = misc:binary_to_atom(SModule),
- {ok, Tokens, _} =
- erl_scan:string(binary_to_list(SOpts) ++ "."),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- ejabberd_cluster:call(Node, ejabberd_listener,
- delete_listener,
- [PortIpNetp2, Module1]),
- R = ejabberd_cluster:call(Node, ejabberd_listener,
- add_listener,
- [PortIpNetp2, Module, Opts]),
- throw({is_added, R});
- _ ->
- case lists:keysearch(<<"delete",
- SSPort/binary>>,
- 1, Query)
- of
- {value, _} ->
- ejabberd_cluster:call(Node, ejabberd_listener,
- delete_listener,
- [PortIpNetp, Module1]),
- throw(submitted);
- _ -> ok
- end
- end
- end,
- Ports),
- case lists:keysearch(<<"addnew">>, 1, Query) of
- {value, _} ->
- {{value, {_, SPort}}, {value, {_, STIP}},
- {value, {_, SNetProt}}, {value, {_, SModule}},
- {value, {_, SOpts}}} =
- {lists:keysearch(<<"portnew">>, 1, Query),
- lists:keysearch(<<"ipnew">>, 1, Query),
- lists:keysearch(<<"netprotnew">>, 1, Query),
- lists:keysearch(<<"modulenew">>, 1, Query),
- lists:keysearch(<<"optsnew">>, 1, Query)},
- {ok, Toks, _} = erl_scan:string(binary_to_list(<<SPort/binary, ".">>)),
- {ok, Port2} = erl_parse:parse_term(Toks),
- {ok, ToksIP, _} = erl_scan:string(binary_to_list(<<STIP/binary, ".">>)),
- STIP2 = case erl_parse:parse_term(ToksIP) of
- {ok, IPTParsed} -> IPTParsed;
- {error, _} -> STIP
- end,
- Module = misc:binary_to_atom(SModule),
- NetProt2 = misc:binary_to_atom(SNetProt),
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- {Port2, _SPort, IP2, _SIP, _SSPort, NetProt2,
- OptsClean} =
- get_port_data({Port2, STIP2, NetProt2}, Opts),
- R = ejabberd_cluster:call(Node, ejabberd_listener, add_listener,
- [{Port2, IP2, NetProt2}, Module, OptsClean]),
- throw({is_added, R});
- _ -> ok
- end.
-
-node_modules_to_xhtml(Modules, Lang) ->
- ?XAE(<<"table">>, [{<<"class">>, <<"withtextareas">>}],
- [?XE(<<"thead">>,
- [?XE(<<"tr">>,
- [?XCT(<<"td">>, <<"Module">>),
- ?XCT(<<"td">>, <<"Options">>)])]),
- ?XE(<<"tbody">>,
- (lists:map(fun ({Module, Opts} = _E) ->
- SModule =
- iolist_to_binary(atom_to_list(Module)),
- {NumLines, SOpts} = term_to_paragraph(Opts,
- 40),
- ?XE(<<"tr">>,
- [?XC(<<"td">>, SModule),
- ?XAE(<<"td">>, direction(ltr),
- [?TEXTAREA(<<"opts", SModule/binary>>,
- (integer_to_binary(NumLines)),
- <<"40">>, SOpts)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"restart",
- SModule/binary>>,
- <<"Restart">>)]),
- ?XE(<<"td">>,
- [?INPUTT(<<"submit">>,
- <<"stop", SModule/binary>>,
- <<"Stop">>)])])
- end,
- Modules)
- ++
- [?XE(<<"tr">>,
- [?XE(<<"td">>,
- [?INPUT(<<"text">>, <<"modulenew">>, <<"">>)]),
- ?XAE(<<"td">>, direction(ltr),
- [?TEXTAREA(<<"optsnew">>, <<"2">>, <<"40">>,
- <<"[]">>)]),
- ?XAE(<<"td">>, [{<<"colspan">>, <<"2">>}],
- [?INPUTT(<<"submit">>, <<"start">>,
- <<"Start">>)])])]))]).
-
-node_modules_parse_query(Host, Node, Modules, Query) ->
- lists:foreach(fun ({Module, _Opts1}) ->
- SModule = iolist_to_binary(atom_to_list(Module)),
- case lists:keysearch(<<"restart", SModule/binary>>, 1,
- Query)
- of
- {value, _} ->
- {value, {_, SOpts}} = lists:keysearch(<<"opts",
- SModule/binary>>,
- 1, Query),
- {ok, Tokens, _} =
- erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- NewMods = lists:keystore(Module, 1, ejabberd_config:get_option(modules), {Module, Opts}),
- ejabberd_cluster:call(Node, gen_mod, stop_module,
- [Host, Module]),
- ejabberd_cluster:call(Node, ejabberd_config, add_option,
- [modules, NewMods]),
- ejabberd_cluster:call(Node, gen_mod, start_module,
- [Host, Module]),
- throw(submitted);
- _ ->
- case lists:keysearch(<<"stop", SModule/binary>>,
- 1, Query)
- of
- {value, _} ->
- ejabberd_cluster:call(Node, gen_mod, stop_module,
- [Host, Module]),
- throw(submitted);
- _ -> ok
- end
- end
- end,
- Modules),
- case lists:keysearch(<<"start">>, 1, Query) of
- {value, _} ->
- {{value, {_, SModule}}, {value, {_, SOpts}}} =
- {lists:keysearch(<<"modulenew">>, 1, Query),
- lists:keysearch(<<"optsnew">>, 1, Query)},
- Module = misc:binary_to_atom(SModule),
- {ok, Tokens, _} = erl_scan:string(binary_to_list(<<SOpts/binary, ".">>)),
- {ok, Opts} = erl_parse:parse_term(Tokens),
- ejabberd_cluster:call(Node, gen_mod, start_module,
- [Host, Module, Opts]),
- throw(submitted);
- _ -> ok
- end.
-
node_update_parse_query(Node, Query) ->
case lists:keysearch(<<"update">>, 1, Query) of
{value, _} ->
@@ -2495,16 +1673,6 @@ pretty_print_xml(#xmlel{name = Name, attrs = Attrs,
end
end].
-element_to_list(X) when is_atom(X) ->
- iolist_to_binary(atom_to_list(X));
-element_to_list(X) when is_integer(X) ->
- integer_to_binary(X).
-
-list_to_element(Bin) ->
- {ok, Tokens, _} = erl_scan:string(binary_to_list(Bin)),
- [{_, _, Element}] = Tokens,
- Element.
-
url_func({user_diapason, From, To}) ->
<<(integer_to_binary(From))/binary, "-",
(integer_to_binary(To))/binary, "/">>;
@@ -2576,8 +1744,7 @@ make_host_node_menu(_, cluster, _Lang, _JID) ->
{<<"">>, <<"">>, []};
make_host_node_menu(Host, Node, Lang, JID) ->
HostNodeBase = get_base_path(Host, Node),
- HostNodeFixed = [{<<"modules/">>, <<"Modules">>}] ++
- get_menu_items_hook({hostnode, Host, Node}, Lang),
+ HostNodeFixed = get_menu_items_hook({hostnode, Host, Node}, Lang),
HostNodeBasePath = url_to_path(HostNodeBase),
HostNodeFixed2 = [Tuple
|| Tuple <- HostNodeFixed,
@@ -2589,9 +1756,7 @@ make_host_menu(global, _HostNodeMenu, _Lang, _JID) ->
{<<"">>, <<"">>, []};
make_host_menu(Host, HostNodeMenu, Lang, JID) ->
HostBase = get_base_path(Host, cluster),
- HostFixed = [{<<"acls">>, <<"Access Control Lists">>},
- {<<"access">>, <<"Access Rules">>},
- {<<"users">>, <<"Users">>},
+ HostFixed = [{<<"users">>, <<"Users">>},
{<<"online-users">>, <<"Online Users">>}]
++
get_lastactivity_menuitem_list(Host) ++
@@ -2610,7 +1775,6 @@ make_node_menu(global, Node, Lang) ->
NodeBase = get_base_path(global, Node),
NodeFixed = [{<<"db/">>, <<"Database">>},
{<<"backup/">>, <<"Backup">>},
- {<<"ports/">>, <<"Listened Ports">>},
{<<"stats/">>, <<"Statistics">>},
{<<"update/">>, <<"Update">>}]
++ get_menu_items_hook({node, Node}, Lang),
@@ -2621,9 +1785,7 @@ make_node_menu(_Host, _Node, _Lang) ->
make_server_menu(HostMenu, NodeMenu, Lang, JID) ->
Base = get_base_path(global, cluster),
- Fixed = [{<<"acls">>, <<"Access Control Lists">>},
- {<<"access">>, <<"Access Rules">>},
- {<<"vhosts">>, <<"Virtual Hosts">>, HostMenu},
+ Fixed = [{<<"vhosts">>, <<"Virtual Hosts">>, HostMenu},
{<<"nodes">>, <<"Nodes">>, NodeMenu},
{<<"stats">>, <<"Statistics">>}]
++ get_menu_items_hook(server, Lang),
@@ -2696,11 +1858,10 @@ make_menu_item(item, 3, URI, Name, Lang) ->
?LI([?XAE(<<"div">>, [{<<"id">>, <<"navitemsubsub">>}],
[?ACT(URI, Name)])]).
-%%%==================================
-
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(access_readonly) -> fun acl:access_rules_validator/1;
-opt_type(_) -> [access_readonly].
+any_rules_allowed(Host, Access, Entity) ->
+ lists:any(
+ fun(Rule) ->
+ allow == acl:match_rule(Host, Rule, Entity)
+ end, Access).
%%% vim: set foldmethod=marker foldmarker=%%%%,%%%=:
diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl
index 2dde8add2..1767a1603 100644
--- a/src/ejabberd_websocket.erl
+++ b/src/ejabberd_websocket.erl
@@ -37,12 +37,11 @@
%%%----------------------------------------------------------------------
-module(ejabberd_websocket).
--behaviour(ejabberd_config).
-protocol({rfc, 6455}).
-author('ecestari@process-one.net').
--export([socket_handoff/5, opt_type/1]).
+-export([socket_handoff/5]).
-include("logger.hrl").
@@ -429,27 +428,4 @@ websocket_close(Socket, WsHandleLoopPid, SocketMode, _CloseCode) ->
SocketMode:close(Socket).
get_origin() ->
- ejabberd_config:get_option(websocket_origin, []).
-
-opt_type(websocket_ping_interval) ->
- fun (I) when is_integer(I), I >= 0 -> I end;
-opt_type(websocket_timeout) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-opt_type(websocket_origin) ->
- fun Verify(V) when is_binary(V) ->
- Verify([V]);
- Verify([]) ->
- [];
- Verify([<<"null">> | R]) ->
- [<<"null">> | Verify(R)];
- Verify([null | R]) ->
- [<<"null">> | Verify(R)];
- Verify([V | R]) when is_binary(V) ->
- URIs = [_|_] = lists:filtermap(
- fun(<<>>) -> false;
- (URI) -> {true, misc:try_url(URI)}
- end, re:split(V, "\\s+")),
- [str:join(URIs, <<" ">>) | Verify(R)]
- end;
-opt_type(_) ->
- [websocket_ping_interval, websocket_timeout, websocket_origin].
+ ejabberd_option:websocket_origin().
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index 067b26803..2c299d7f1 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -36,7 +36,7 @@
-author('badlop@process-one.net').
-export([start/3, start_link/3, handler/2, process/2, accept/1,
- transform_listen_option/2, listen_opt_type/1, listen_options/0]).
+ listen_opt_type/1, listen_options/0]).
-include("logger.hrl").
-include("ejabberd_http.hrl").
@@ -233,7 +233,8 @@ process(_, _) ->
%% -----------------------------
%% Access verification
%% -----------------------------
-
+-spec extract_auth([{user | server | token | password, binary()}]) ->
+ map() | {error, not_found | expired | invalid_auth}.
extract_auth(AuthList) ->
?DEBUG("AUTHLIST ~p", [AuthList]),
try get_attrs([user, server, token], AuthList) of
@@ -306,10 +307,6 @@ handler(#state{get_auth = true, auth = noauth, ip = IP} = State,
build_fault_response(-118,
"Invalid oauth token",
[]);
- {error, Value} ->
- build_fault_response(-118,
- "Invalid authentication data: ~p",
- [Value]);
Auth ->
handler(State#state{get_auth = false, auth = Auth#{ip => IP, caller_module => ?MODULE}},
{call, Method, Arguments})
@@ -341,15 +338,10 @@ handler(_State,
handler(State, {call, Command, []}) ->
handler(State, {call, Command, [{struct, []}]});
handler(State,
- {call, Command, [{struct, AttrL}]} = Payload) ->
- case ejabberd_commands:get_command_format(Command, State#state.auth) of
- {error, command_unknown} ->
- build_fault_response(-112, "Unknown call: ~p",
- [Payload]);
- {ArgsF, ResultF} ->
- try_do_command(State#state.access_commands,
- State#state.auth, Command, AttrL, ArgsF, ResultF)
- end;
+ {call, Command, [{struct, AttrL}]}) ->
+ {ArgsF, ResultF} = ejabberd_commands:get_command_format(Command, State#state.auth),
+ try_do_command(State#state.access_commands,
+ State#state.auth, Command, AttrL, ArgsF, ResultF);
%% If no other guard matches
handler(_State, Payload) ->
build_fault_response(-112, "Unknown call: ~p",
@@ -400,14 +392,8 @@ build_fault_response(Code, ParseString, ParseArgs) ->
do_command(AccessCommands, Auth, Command, AttrL, ArgsF,
ResultF) ->
ArgsFormatted = format_args(AttrL, ArgsF),
- Auth2 = case AccessCommands of
- V when is_list(V) ->
- Auth#{extra_permissions => AccessCommands};
- _ ->
- Auth
- end,
- Result =
- ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth2),
+ Auth2 = Auth#{extra_permissions => AccessCommands},
+ Result = ejabberd_commands:execute_command2(Command, ArgsFormatted, Auth2),
ResultFormatted = format_result(Result, ResultF),
{command_result, ResultFormatted}.
@@ -555,17 +541,6 @@ make_status(false) -> 1;
make_status(error) -> 1;
make_status(_) -> 1.
-transform_listen_option({access_commands, ACOpts}, Opts) ->
- NewACOpts = lists:map(
- fun({AName, ACmds, AOpts}) ->
- {AName, [{commands, ACmds}, {options, AOpts}]};
- (Opt) ->
- Opt
- end, ACOpts),
- [{access_commands, NewACOpts}|Opts];
-transform_listen_option(Opt, Opts) ->
- [Opt|Opts].
-
listen_opt_type(access_commands) ->
fun(Opts) ->
lists:map(
diff --git a/src/eldap_utils.erl b/src/eldap_utils.erl
index 47e18aac3..40771d4ad 100644
--- a/src/eldap_utils.erl
+++ b/src/eldap_utils.erl
@@ -25,14 +25,12 @@
-module(eldap_utils).
--behaviour(ejabberd_config).
-author('mremond@process-one.net').
-export([generate_subfilter/1, find_ldap_attrs/2, check_filter/1,
get_ldap_attr/2, get_user_part/2, make_filter/2,
- get_state/2, case_insensitive_match/2, get_config/2,
- decode_octet_string/3, uids_domain_subst/2, opt_type/1,
- options/1]).
+ get_state/2, case_insensitive_match/2,
+ decode_octet_string/3, uids_domain_subst/2]).
-include("logger.hrl").
-include("eldap.hrl").
@@ -160,110 +158,54 @@ get_state(Server, Module) ->
%% we look from alias domain (%d) and make the substitution
%% with the actual host domain
%% This help when you need to configure many virtual domains.
--spec uids_domain_subst(binary(), [{binary(), binary()}]) ->
+-spec uids_domain_subst(binary(), [{binary(), binary()}]) ->
[{binary(), binary()}].
uids_domain_subst(Host, UIDs) ->
lists:map(fun({U,V}) ->
{U, eldap_filter:do_sub(V,[{<<"%d">>, Host}])};
- (A) -> A
+ (A) -> A
end,
UIDs).
--spec get_config(binary(), list()) -> eldap_config().
-
-get_config(Host, Opts) ->
- Servers = get_opt(ldap_servers, Host, Opts, [<<"localhost">>]),
- Backups = get_opt(ldap_backups, Host, Opts, []),
- Encrypt = get_opt(ldap_encrypt, Host, Opts, none),
- TLSVerify = get_opt(ldap_tls_verify, Host, Opts, false),
- TLSCertFile = get_opt(ldap_tls_certfile, Host, Opts),
- TLSCAFile = get_opt(ldap_tls_cacertfile, Host, Opts),
- TLSDepth = get_opt(ldap_tls_depth, Host, Opts),
- Port = case get_opt(ldap_port, Host, Opts) of
- undefined ->
- case Encrypt of
- tls -> ?LDAPS_PORT;
- starttls -> ?LDAP_PORT;
- _ -> ?LDAP_PORT
- end;
- P ->
- P
- end,
- RootDN = get_opt(ldap_rootdn, Host, Opts, <<"">>),
- Password = get_opt(ldap_password, Host, Opts, <<"">>),
- Base = get_opt(ldap_base, Host, Opts, <<"">>),
- OldDerefAliases = get_opt(deref_aliases, Host, Opts, unspecified),
- DerefAliases =
- if OldDerefAliases == unspecified ->
- get_opt(ldap_deref_aliases, Host, Opts, never);
- true ->
- ?WARNING_MSG("Option 'deref_aliases' is deprecated. "
- "The option is still supported "
- "but it is better to fix your config: "
- "use 'ldap_deref_aliases' instead.", []),
- OldDerefAliases
- end,
- #eldap_config{servers = Servers,
- backups = Backups,
- tls_options = [{encrypt, Encrypt},
- {tls_verify, TLSVerify},
- {tls_certfile, TLSCertFile},
- {tls_cacertfile, TLSCAFile},
- {tls_depth, TLSDepth}],
- port = Port,
- dn = RootDN,
- password = Password,
- base = Base,
- deref_aliases = DerefAliases}.
-
-get_opt(Opt, Host, Opts) ->
- get_opt(Opt, Host, Opts, undefined).
-
-get_opt(Opt, Host, Opts, Default) ->
- case proplists:get_value(Opt, Opts) of
- undefined -> ejabberd_config:get_option({Opt, Host}, Default);
- Value -> Value
- end.
-
-%%----------------------------------------
+%%----------------------------------------
%% Borrowed from asn1rt_ber_bin_v2.erl
%%----------------------------------------
%%% The tag-number for universal types
--define(N_BOOLEAN, 1).
--define(N_INTEGER, 2).
+-define(N_BOOLEAN, 1).
+-define(N_INTEGER, 2).
-define(N_BIT_STRING, 3).
-define(N_OCTET_STRING, 4).
--define(N_NULL, 5).
--define(N_OBJECT_IDENTIFIER, 6).
--define(N_OBJECT_DESCRIPTOR, 7).
--define(N_EXTERNAL, 8).
--define(N_REAL, 9).
--define(N_ENUMERATED, 10).
--define(N_EMBEDDED_PDV, 11).
--define(N_SEQUENCE, 16).
--define(N_SET, 17).
+-define(N_NULL, 5).
+-define(N_OBJECT_IDENTIFIER, 6).
+-define(N_OBJECT_DESCRIPTOR, 7).
+-define(N_EXTERNAL, 8).
+-define(N_REAL, 9).
+-define(N_ENUMERATED, 10).
+-define(N_EMBEDDED_PDV, 11).
+-define(N_SEQUENCE, 16).
+-define(N_SET, 17).
-define(N_NumericString, 18).
-define(N_PrintableString, 19).
-define(N_TeletexString, 20).
-define(N_VideotexString, 21).
-define(N_IA5String, 22).
--define(N_UTCTime, 23).
--define(N_GeneralizedTime, 24).
+-define(N_UTCTime, 23).
+-define(N_GeneralizedTime, 24).
-define(N_GraphicString, 25).
-define(N_VisibleString, 26).
-define(N_GeneralString, 27).
-define(N_UniversalString, 28).
-define(N_BMPString, 30).
-decode_octet_string(Buffer, Range, Tags) ->
+decode_octet_string(Buffer, Range, Tags) ->
% NewTags = new_tags(HasTag,#tag{class=?UNIVERSAL,number=?N_OCTET_STRING}),
decode_restricted_string(Buffer, Range, Tags).
decode_restricted_string(Tlv, Range, TagsIn) ->
Val = match_tags(Tlv, TagsIn),
- Val2 =
+ Val2 =
case Val of
PartList = [_H|_T] -> % constructed val
collect_parts(PartList);
@@ -287,12 +229,12 @@ check_and_convert_restricted_string(Val, Range) ->
NewVal;
{{Lb,_Ub},_Ext=[Min|_]} when StrLen >= Lb; StrLen >= Min ->
NewVal;
- {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
+ {{Lb1,Ub1},{Lb2,Ub2}} when StrLen >= Lb1, StrLen =< Ub1;
StrLen =< Ub2, StrLen >= Lb2 ->
NewVal;
StrLen -> % fixed length constraint
NewVal;
- {_,_} ->
+ {_,_} ->
exit({error,{asn1,{length,Range,Val}}});
_Len when is_integer(_Len) ->
exit({error,{asn1,{length,Range,Val}}});
@@ -300,9 +242,9 @@ check_and_convert_restricted_string(Val, Range) ->
NewVal
end.
-%%----------------------------------------
-%% Decode the in buffer to bits
-%%----------------------------------------
+%%----------------------------------------
+%% Decode the in buffer to bits
+%%----------------------------------------
match_tags({T,V},[T]) ->
V;
match_tags({T,V}, [T|Tt]) ->
@@ -328,91 +270,7 @@ collect_parts([{_T,V}|Rest],Acc) ->
collect_parts([],Acc) ->
list_to_binary(lists:reverse(Acc)).
-collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
+collect_parts_bit([{?N_BIT_STRING,<<Unused,Bits/binary>>}|Rest],Acc,Uacc) ->
collect_parts_bit(Rest,[Bits|Acc],Unused+Uacc);
collect_parts_bit([],Acc,Uacc) ->
list_to_binary([Uacc|lists:reverse(Acc)]).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(deref_aliases) ->
- fun(unspecified) -> unspecified;
- (never) -> never;
- (searching) -> searching;
- (finding) -> finding;
- (always) -> always
- end;
-opt_type(ldap_backups) ->
- fun (L) -> [iolist_to_binary(H) || H <- L] end;
-opt_type(ldap_base) -> fun iolist_to_binary/1;
-opt_type(ldap_deref_aliases) ->
- fun (never) -> never;
- (searching) -> searching;
- (finding) -> finding;
- (always) -> always
- end;
-opt_type(ldap_encrypt) ->
- fun (tls) -> tls;
- (starttls) -> starttls;
- (none) -> none
- end;
-opt_type(ldap_password) -> fun iolist_to_binary/1;
-opt_type(ldap_port) ->
- fun(undefined) -> undefined;
- (I) when is_integer(I), I > 0 -> I
- end;
-opt_type(ldap_rootdn) -> fun iolist_to_binary/1;
-opt_type(ldap_servers) ->
- fun (L) -> [iolist_to_binary(H) || H <- L] end;
-opt_type(ldap_tls_certfile) ->
- fun(undefined) -> undefined;
- (S) -> binary_to_list(ejabberd_pkix:try_certfile(S))
- end;
-opt_type(ldap_tls_cacertfile) ->
- fun(undefined) -> undefined;
- (S) -> binary_to_list(misc:try_read_file(S))
- end;
-opt_type(ldap_tls_depth) ->
- fun(undefined) -> undefined;
- (I) when is_integer(I), I >= 0 -> I
- end;
-opt_type(ldap_tls_verify) ->
- fun (hard) -> hard;
- (soft) -> soft;
- (false) -> false
- end;
-opt_type(ldap_filter) ->
- fun(<<"">>) -> <<"">>;
- (F) -> check_filter(F)
- end;
-opt_type(ldap_uids) ->
- fun (Us) ->
- lists:map(fun ({U, P}) ->
- {iolist_to_binary(U), iolist_to_binary(P)};
- ({U}) -> {iolist_to_binary(U)};
- (U) -> {iolist_to_binary(U)}
- end,
- lists:flatten(Us))
- end;
-opt_type(_) ->
- [deref_aliases, ldap_backups, ldap_base, ldap_uids,
- ldap_deref_aliases, ldap_encrypt, ldap_password,
- ldap_port, ldap_rootdn, ldap_servers, ldap_filter,
- ldap_tls_certfile, ldap_tls_cacertfile, ldap_tls_depth,
- ldap_tls_verify].
-
-options(_) ->
- [{deref_aliases, unspecified},
- {ldap_backups, []},
- {ldap_base, <<"">>},
- {ldap_uids, [{<<"uid">>, <<"%u">>}]},
- {ldap_deref_aliases, never},
- {ldap_encrypt, none},
- {ldap_password, <<"">>},
- {ldap_port, undefined},
- {ldap_rootdn, <<"">>},
- {ldap_servers, [<<"localhost">>]},
- {ldap_filter, <<"">>},
- {ldap_tls_certfile, undefined},
- {ldap_tls_cacertfile, undefined},
- {ldap_tls_depth, undefined},
- {ldap_tls_verify, false}].
diff --git a/src/elixir_logger_backend.erl b/src/elixir_logger_backend.erl
index 2bb8889af..794af3171 100644
--- a/src/elixir_logger_backend.erl
+++ b/src/elixir_logger_backend.erl
@@ -22,9 +22,10 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%-------------------------------------------------------------------
-
-module(elixir_logger_backend).
+-ifdef(ELIXIR_ENABLED).
+
-behaviour(gen_event).
-export([init/1, handle_call/2, handle_event/2, handle_info/2, terminate/2,
@@ -57,7 +58,7 @@ handle_event({log, LagerMsg}, State) ->
notify(Mode, {MsgLevel, GroupLeader, {'Elixir.Logger', Message, Timestamp, Metadata}}),
{ok, State};
_ ->
- {ok, State}
+ {ok, State}
end;
handle_event(_Msg, State) ->
{ok, State}.
@@ -110,7 +111,7 @@ timestamp(Time, UTCLog) ->
false -> calendar:now_to_local_time(Time)
end,
{Date, {Hours, Minutes, Seconds, Micro div 1000}}.
-
+
severity_to_level(debug) -> debug;
severity_to_level(info) -> info;
@@ -120,3 +121,5 @@ severity_to_level(error) -> error;
severity_to_level(critical) -> error;
severity_to_level(alert) -> error;
severity_to_level(emergency) -> error.
+
+-endif.
diff --git a/src/ext_mod.erl b/src/ext_mod.erl
index 328bb829e..607f7b080 100644
--- a/src/ext_mod.erl
+++ b/src/ext_mod.erl
@@ -25,7 +25,6 @@
-module(ext_mod).
--behaviour(ejabberd_config).
-behaviour(gen_server).
-author("Christophe Romain <christophe.romain@process-one.net>").
@@ -34,7 +33,7 @@
installed_command/0, installed/0, installed/1,
install/1, uninstall/1, upgrade/0, upgrade/1, add_paths/0,
add_sources/1, add_sources/2, del_sources/1, modules_dir/0,
- config_dir/0, opt_type/1, get_commands_spec/0]).
+ config_dir/0, get_commands_spec/0]).
-export([compile_erlang_file/2, compile_elixir_file/2]).
@@ -221,7 +220,7 @@ install(Package) when is_binary(Package) ->
case compile_and_install(Module, Attrs) of
ok ->
code:add_patha(module_ebin_dir(Module)),
- ejabberd_config:reload_file(),
+ ejabberd_config:reload(),
case erlang:function_exported(Module, post_install, 0) of
true -> Module:post_install();
_ -> ok
@@ -243,12 +242,12 @@ uninstall(Package) when is_binary(Package) ->
_ -> ok
end,
[catch gen_mod:stop_module(Host, Module)
- || Host <- ejabberd_config:get_myhosts()],
+ || Host <- ejabberd_option:hosts()],
code:purge(Module),
code:delete(Module),
code:del_path(module_ebin_dir(Module)),
delete_path(module_lib_dir(Module)),
- ejabberd_config:reload_file();
+ ejabberd_config:reload();
false ->
{error, not_installed}
end.
@@ -464,7 +463,7 @@ short_spec({Module, Attrs}) when is_atom(Module), is_list(Attrs) ->
{Module, proplists:get_value(summary, Attrs, "")}.
is_contrib_allowed() ->
- ejabberd_config:get_option(allow_contrib_modules, true).
+ ejabberd_option:allow_contrib_modules().
%% -- build functions
@@ -597,6 +596,7 @@ compile_erlang_file(Dest, File, ErlOptions) ->
{error, E, W} -> {error, {compilation_failed, File, E, W}}
end.
+-ifdef(ELIXIR_ENABLED).
compile_elixir_file(Dest, File) when is_list(Dest) and is_list(File) ->
compile_elixir_file(list_to_binary(Dest), list_to_binary(File));
@@ -606,6 +606,10 @@ compile_elixir_file(Dest, File) ->
catch
_ -> {error, {compilation_failed, File}}
end.
+-else.
+compile_elixir_file(_, File) ->
+ {error, {compilation_failed, File}}.
+-endif.
install(Module, Spec, SrcDir, LibDir) ->
{ok, CurDir} = file:get_cwd(),
@@ -682,11 +686,3 @@ format({Key, Val}) when is_binary(Val) ->
{Key, binary_to_list(Val)};
format({Key, Val}) -> % TODO: improve Yaml parsing
{Key, Val}.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(allow_contrib_modules) ->
- fun (false) -> false;
- (no) -> false;
- (_) -> true
- end;
-opt_type(_) -> [allow_contrib_modules].
diff --git a/src/extauth.erl b/src/extauth.erl
index c617e6c26..9d9a59de2 100644
--- a/src/extauth.erl
+++ b/src/extauth.erl
@@ -78,11 +78,11 @@ remove_user(User, Server, Password) ->
-spec prog_name(binary()) -> string() | undefined.
prog_name(Host) ->
- ejabberd_config:get_option({extauth_program, Host}).
+ ejabberd_option:extauth_program(Host).
-spec pool_name(binary()) -> atom().
pool_name(Host) ->
- case ejabberd_config:get_option({extauth_pool_name, Host}) of
+ case ejabberd_option:extauth_pool_name(Host) of
undefined ->
list_to_atom("extauth_pool_" ++ binary_to_list(Host));
Name ->
@@ -95,7 +95,7 @@ worker_name(Pool, N) ->
-spec pool_size(binary()) -> pos_integer().
pool_size(Host) ->
- case ejabberd_config:get_option({extauth_pool_size, Host}) of
+ case ejabberd_option:extauth_pool_size(Host) of
undefined ->
try erlang:system_info(logical_processors)
catch _:_ -> 1
diff --git a/src/gen_iq_handler.erl b/src/gen_iq_handler.erl
index 38aa32e36..cd94f0575 100644
--- a/src/gen_iq_handler.erl
+++ b/src/gen_iq_handler.erl
@@ -27,12 +27,9 @@
-author('alexey@process-one.net').
--behaviour(ejabberd_config).
-
%% API
-export([add_iq_handler/5, remove_iq_handler/3, handle/1, handle/2,
- check_type/1, transform_module_options/1,
- opt_type/1, start/1, get_features/2]).
+ start/1, get_features/2]).
%% Deprecated functions
-export([add_iq_handler/6, handle/5, iqdisc/1]).
-deprecated([{add_iq_handler, 6}, {handle, 5}, {iqdisc, 1}]).
@@ -135,29 +132,10 @@ process_iq(Module, Function, #iq{lang = Lang, sub_els = [El]} = IQ) ->
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang))
end.
--spec check_type(any()) -> no_queue.
-check_type(_Type) ->
- ?WARNING_MSG("Option 'iqdisc' is deprecated and has no effect anymore", []),
- no_queue.
-
-spec iqdisc(binary() | global) -> no_queue.
iqdisc(_Host) ->
no_queue.
--spec transform_module_options([{atom(), any()}]) -> [{atom(), any()}].
-
-transform_module_options(Opts) ->
- lists:map(
- fun({iqdisc, {queues, N}}) ->
- {iqdisc, N};
- (Opt) ->
- Opt
- end, Opts).
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(iqdisc) -> fun check_type/1;
-opt_type(_) -> [iqdisc].
-
%%====================================================================
%% Deprecated API
%%====================================================================
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index b972285f5..f0a35e316 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -22,39 +22,25 @@
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-
-module(gen_mod).
-
--behaviour(ejabberd_config).
-behaviour(supervisor).
-
-author('alexey@process-one.net').
-export([init/1, start_link/0, start_child/3, start_child/4,
stop_child/1, stop_child/2, config_reloaded/0]).
-export([start_module/2, stop_module/2, stop_module_keep_config/2,
- get_opt/2, get_opt_hosts/2, opt_type/1, is_equal_opt/3,
- get_module_opt/3, get_module_opt_host/3,
+ get_opt/2, set_opt/3, get_opt_hosts/1, is_equal_opt/3,
+ get_module_opt/3, get_module_opt_hosts/2,
loaded_modules/1, loaded_modules_with_opts/1,
get_hosts/2, get_module_proc/2, is_loaded/2, is_loaded_elsewhere/2,
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
- db_mod/2, db_mod/3, ram_db_mod/2, ram_db_mod/3,
- is_db_configured/2]).
+ db_mod/2, ram_db_mod/2]).
+-export([validate/2]).
%% Deprecated functions
--export([get_opt/3, get_opt/4, get_module_opt/4, get_module_opt/5,
- get_opt_host/3, get_opt_hosts/3, db_type/2, db_type/3,
- ram_db_type/2, ram_db_type/3, update_module_opts/3]).
--deprecated([{get_opt, 3},
- {get_opt, 4},
- {get_opt_host, 3},
- {get_opt_hosts, 3},
- {get_module_opt, 4},
- {get_module_opt, 5},
- {db_type, 2},
- {db_type, 3},
- {ram_db_type, 2},
- {ram_db_type, 3}]).
+%% update_module/3 is used by test suite ONLY
+-export([update_module/3]).
+-deprecated([{update_module, 3}]).
-include("logger.hrl").
-include_lib("stdlib/include/ms_transform.hrl").
@@ -65,14 +51,14 @@
opts = [] :: opts() | '_' | '$2',
order = 0 :: integer()}).
--type opts() :: [{atom(), any()}].
+-type opts() :: #{atom() => term()}.
-type db_type() :: atom().
-callback start(binary(), opts()) -> ok | {ok, pid()} | {error, term()}.
-callback stop(binary()) -> any().
-callback reload(binary(), opts(), opts()) -> ok | {ok, pid()}.
--callback mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
--callback mod_options(binary()) -> opts().
+-callback mod_opt_type(atom()) -> econf:validator().
+-callback mod_options(binary()) -> [{atom(), term()} | atom()].
-callback depends(binary(), opts()) -> [{module(), hard | soft}].
-optional_callbacks([mod_opt_type/1, reload/3]).
@@ -103,18 +89,18 @@ init([]) ->
{read_concurrency, true}]),
{ok, {{one_for_one, 10, 1}, []}}.
--spec start_child(module(), binary() | global, opts()) -> ok | {error, any()}.
+-spec start_child(module(), binary(), opts()) -> {ok, pid()} | {error, any()}.
start_child(Mod, Host, Opts) ->
start_child(Mod, Host, Opts, get_module_proc(Host, Mod)).
--spec start_child(module(), binary() | global, opts(), atom()) -> ok | {error, any()}.
+-spec start_child(module(), binary(), opts(), atom()) -> {ok, pid()} | {error, any()}.
start_child(Mod, Host, Opts, Proc) ->
Spec = {Proc, {?GEN_SERVER, start_link,
[{local, Proc}, Mod, [Host, Opts], []]},
transient, timer:minutes(1), worker, [Mod]},
supervisor:start_child(ejabberd_gen_mod_sup, Spec).
--spec stop_child(module(), binary() | global) -> ok | {error, any()}.
+-spec stop_child(module(), binary()) -> ok | {error, any()}.
stop_child(Mod, Host) ->
stop_child(get_module_proc(Host, Mod)).
@@ -124,73 +110,22 @@ stop_child(Proc) ->
supervisor:delete_child(ejabberd_gen_mod_sup, Proc).
-spec start_modules() -> any().
-
-%% Start all the modules in all the hosts
start_modules() ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
?INFO_MSG("Loading modules for ~s", [format_hosts_list(Hosts)]),
lists:foreach(fun start_modules/1, Hosts).
-get_modules_options(Host) ->
- sort_modules(Host, ejabberd_config:get_option({modules, Host}, [])).
-
-sort_modules(Host, ModOpts) ->
- G = digraph:new([acyclic]),
- lists:foreach(
- fun({Mod, Opts}) ->
- digraph:add_vertex(G, Mod, Opts),
- Deps = try Mod:depends(Host, Opts) catch _:undef -> [] end,
- lists:foreach(
- fun({DepMod, Type}) ->
- case lists:keyfind(DepMod, 1, ModOpts) of
- false when Type == hard ->
- ErrTxt = io_lib:format(
- "Failed to load module '~s' "
- "because it depends on module '~s' "
- "which is not found in the config",
- [Mod, DepMod]),
- ?ERROR_MSG(ErrTxt, []),
- digraph:del_vertex(G, Mod),
- maybe_halt_ejabberd();
- false when Type == soft ->
- ?WARNING_MSG("Module '~s' is recommended for "
- "module '~s' but is not found in "
- "the config",
- [DepMod, Mod]);
- {DepMod, DepOpts} ->
- digraph:add_vertex(G, DepMod, DepOpts),
- case digraph:add_edge(G, DepMod, Mod) of
- {error, {bad_edge, Path}} ->
- ?WARNING_MSG("Cyclic dependency detected "
- "between modules: ~p",
- [Path]);
- _ ->
- ok
- end
- end
- end, Deps)
- end, ModOpts),
- {Result, _} = lists:mapfoldl(
- fun(V, Order) ->
- {M, O} = digraph:vertex(G, V),
- {{M, O, Order}, Order+1}
- end, 1, digraph_utils:topsort(G)),
- digraph:delete(G),
- Result.
-
-spec start_modules(binary()) -> ok.
-
start_modules(Host) ->
- Modules = get_modules_options(Host),
+ Modules = ejabberd_option:modules(Host),
lists:foreach(
fun({Module, Opts, Order}) ->
start_module(Host, Module, Opts, Order)
end, Modules).
-spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}.
-
start_module(Host, Module) ->
- Modules = get_modules_options(Host),
+ Modules = ejabberd_option:modules(Host),
case lists:keyfind(Module, 1, Modules) of
{_, Opts, Order} ->
start_module(Host, Module, Opts, Order);
@@ -200,42 +135,30 @@ start_module(Host, Module) ->
-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}.
start_module(Host, Module, Opts, Order) ->
- start_module(Host, Module, Opts, Order, true).
-
--spec start_module(binary(), atom(), opts(), integer(), boolean()) -> ok | {ok, pid()}.
-start_module(Host, Module, Opts0, Order, NeedValidation) ->
?DEBUG("Loading ~s at ~s", [Module, Host]),
- Res = if NeedValidation ->
- validate_opts(Host, Module, Opts0);
- true ->
- {ok, Opts0}
- end,
- case Res of
- {ok, Opts} ->
- store_options(Host, Module, Opts, Order),
- try case Module:start(Host, Opts) of
- ok -> ok;
- {ok, Pid} when is_pid(Pid) -> {ok, Pid};
- Err -> erlang:error({bad_return, Module, Err})
- end
- catch ?EX_RULE(Class, Reason, Stack) ->
- StackTrace = ?EX_STACK(Stack),
- ets:delete(ejabberd_modules, {Module, Host}),
- ErrorText = format_module_error(
- Module, start, 2,
- Opts, Class, Reason,
- StackTrace),
- ?CRITICAL_MSG(ErrorText, []),
- maybe_halt_ejabberd(),
- erlang:raise(Class, Reason, StackTrace)
- end;
- {error, _ErrorText} ->
- maybe_halt_ejabberd()
+ store_options(Host, Module, Opts, Order),
+ try case Module:start(Host, Opts) of
+ ok -> ok;
+ {ok, Pid} when is_pid(Pid) -> {ok, Pid};
+ Err ->
+ ets:delete(ejabberd_modules, {Module, Host}),
+ erlang:error({bad_return, Module, Err})
+ end
+ catch ?EX_RULE(Class, Reason, Stack) ->
+ StackTrace = ?EX_STACK(Stack),
+ ets:delete(ejabberd_modules, {Module, Host}),
+ ErrorText = format_module_error(
+ Module, start, 2,
+ Opts, Class, Reason,
+ StackTrace),
+ ?CRITICAL_MSG(ErrorText, []),
+ maybe_halt_ejabberd(),
+ erlang:raise(Class, Reason, StackTrace)
end.
-spec reload_modules(binary()) -> ok.
reload_modules(Host) ->
- NewMods = get_modules_options(Host),
+ NewMods = ejabberd_option:modules(Host),
OldMods = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
fun({Mod, _Opts}) ->
@@ -258,13 +181,10 @@ reload_modules(Host) ->
lists:foreach(
fun({Mod, OldOpts}) ->
case lists:keyfind(Mod, 1, NewMods) of
- {_, NewOpts0, Order} ->
- case validate_opts(Host, Mod, NewOpts0) of
- {ok, OldOpts} ->
- ok;
- {ok, NewOpts} ->
+ {_, NewOpts, Order} ->
+ if OldOpts /= NewOpts ->
reload_module(Host, Mod, NewOpts, OldOpts, Order);
- {error, _} ->
+ true ->
ok
end;
_ ->
@@ -296,29 +216,22 @@ reload_module(Host, Module, NewOpts, OldOpts, Order) ->
?WARNING_MSG("Module ~s doesn't support reloading "
"and will be restarted", [Module]),
stop_module(Host, Module),
- start_module(Host, Module, NewOpts, Order, false)
+ start_module(Host, Module, NewOpts, Order)
end.
+-spec update_module(binary(), module(), opts()) -> ok | {ok, pid()}.
+update_module(Host, Module, Opts) ->
+ [#ejabberd_module{opts = OldOpts, order = Order}] =
+ ets:lookup(ejabberd_modules, {Module, Host}),
+ NewOpts = maps:merge(OldOpts, Opts),
+ reload_module(Host, Module, NewOpts, OldOpts, Order).
+
-spec store_options(binary(), module(), opts(), integer()) -> true.
store_options(Host, Module, Opts, Order) ->
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
opts = Opts, order = Order}).
--spec update_module_opts(binary(), module(), opts()) -> ok | {ok, pid()} | error.
-update_module_opts(Host, Module, NewValues) ->
- case ets:lookup(ejabberd_modules, {Module, Host}) of
- [#ejabberd_module{opts = Opts, order = Order}] ->
- NewOpts = lists:foldl(
- fun({K, _} = KV, Acc) ->
- lists:keystore(K, 1, Acc, KV)
- end, Opts, NewValues),
- reload_module(Host, Module, NewOpts, Opts, Order);
- Other ->
- ?WARNING_MSG("Unable to update module opts: (~p, ~p) -> ~p",
- [Host, Module, Other])
- end.
-
maybe_halt_ejabberd() ->
case is_app_running(ejabberd) of
false ->
@@ -336,15 +249,13 @@ is_app_running(AppName) ->
application:which_applications(Timeout)).
-spec stop_modules() -> ok.
-
stop_modules() ->
lists:foreach(
fun(Host) ->
stop_modules(Host)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec stop_modules(binary()) -> ok.
-
stop_modules(Host) ->
Modules = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
@@ -353,7 +264,6 @@ stop_modules(Host) ->
end, Modules).
-spec stop_module(binary(), atom()) -> error | {aborted, any()} | {atomic, any()}.
-
stop_module(Host, Module) ->
case stop_module_keep_config(Host, Module) of
error -> error;
@@ -361,7 +271,6 @@ stop_module(Host, Module) ->
end.
-spec stop_module_keep_config(binary(), atom()) -> error | ok.
-
stop_module_keep_config(Host, Module) ->
?DEBUG("Stopping ~s at ~s", [Module, Host]),
case catch Module:stop(Host) of
@@ -400,475 +309,53 @@ wait_for_stop1(MonitorReference) ->
after 5000 -> ok
end.
--type check_fun() :: fun((any()) -> any()) | {module(), atom()}.
-
-spec get_opt(atom(), opts()) -> any().
get_opt(Opt, Opts) ->
- case lists:keyfind(Opt, 1, Opts) of
- {_, Val} -> Val;
- false ->
- ?DEBUG("Attempt to read unspecified option ~s", [Opt]),
- undefined
- end.
-
--spec get_opt(atom(), opts(), check_fun() | any()) -> any().
-
-get_opt(Opt, Opts, F) when is_function(F) ->
- get_opt(Opt, Opts, undefined);
-get_opt(Opt, Opts, Default) ->
- case lists:keyfind(Opt, 1, Opts) of
- false ->
- Default;
- {_, Val} ->
- Val
- end.
+ maps:get(Opt, Opts).
--spec get_opt(atom() | {atom(), binary()}, opts(), check_fun(), any()) -> any().
-get_opt(Opt, Opts, _, Default) ->
- get_opt(Opt, Opts, Default).
+-spec set_opt(atom(), term(), opts()) -> opts().
+set_opt(Opt, Val, Opts) ->
+ maps:put(Opt, Val, Opts).
-spec get_module_opt(global | binary(), atom(), atom()) -> any().
-
+get_module_opt(global, Module, Opt) ->
+ get_module_opt(ejabberd_config:get_myname(), Module, Opt);
get_module_opt(Host, Module, Opt) ->
- get_module_opt(Host, Module, Opt, undefined).
-
--spec get_module_opt(global | binary(), atom(), atom(), any()) -> any().
-
-get_module_opt(Host, Module, Opt, F) when is_function(F) ->
- get_module_opt(Host, Module, Opt, undefined);
-get_module_opt(global, Module, Opt, Default) ->
- Hosts = ejabberd_config:get_myhosts(),
- [Value | Values] = lists:map(fun (Host) ->
- get_module_opt(Host, Module, Opt,
- Default)
- end,
- Hosts),
- Same_all = lists:all(fun (Other_value) ->
- Other_value == Value
- end,
- Values),
- case Same_all of
- true -> Value;
- false -> Default
- end;
-get_module_opt(Host, Module, Opt, Default) ->
- OptsList = ets:lookup(ejabberd_modules, {Module, Host}),
- case OptsList of
- [] -> Default;
- [#ejabberd_module{opts = Opts} | _] ->
- get_opt(Opt, Opts, Default)
- end.
-
--spec get_module_opt(global | binary(), atom(), atom(), check_fun(), any()) -> any().
-get_module_opt(Host, Module, Opt, _, Default) ->
- get_module_opt(Host, Module, Opt, Default).
-
--spec get_module_opt_host(global | binary(), atom(), binary()) -> binary().
-
-get_module_opt_host(Host, Module, Default) ->
- Val = get_module_opt(Host, Module, host, Default),
- ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-
--spec get_opt_host(binary(), opts(), binary()) -> binary().
-
-get_opt_host(Host, Opts, Default) ->
- Val = get_opt(host, Opts, Default),
- ejabberd_regexp:greplace(Val, <<"@HOST@">>, Host).
-
--spec get_opt_hosts(binary(), opts()) -> [binary()].
-get_opt_hosts(Host, Opts) ->
- get_opt_hosts(Host, Opts, undefined).
-
--spec get_opt_hosts(binary(), opts(), binary()) -> [binary()].
-get_opt_hosts(Host, Opts, Default) ->
- Vals = case get_opt(hosts, Opts) of
- L when L == [] orelse L == undefined ->
- case get_opt(host, Opts) of
- undefined -> [Default];
- H -> [H]
- end;
- L ->
- L
- end,
- [ejabberd_regexp:greplace(V, <<"@HOST@">>, Host) || V <- Vals].
-
--spec get_validators(binary(), {module(), [module()]}) -> list() | undef.
-get_validators(Host, {Module, SubMods}) ->
- Validators =
- dict:to_list(
- lists:foldl(
- fun(Mod, D) ->
- try list_known_opts(Host, Mod) of
- Os ->
- lists:foldl(
- fun({Opt, SubOpt} = O, Acc) ->
- SubF = Mod:mod_opt_type(O),
- F = try Mod:mod_opt_type(Opt)
- catch _:_ -> fun(X) -> X end
- end,
- dict:append_list(
- Opt, [F, {SubOpt, [SubF]}], Acc);
- (O, Acc) ->
- F = Mod:mod_opt_type(O),
- dict:store(O, [F], Acc)
- end, D, Os)
- catch _:undef ->
- D
- end
- end, dict:new(), [Module|SubMods])),
- case Validators of
- [] ->
- case have_validators(Module) of
- false ->
- case code:ensure_loaded(Module) of
- {module, _} ->
- ?WARNING_MSG("Third-party module '~s' doesn't export "
- "options validator; consider to upgrade "
- "the module", [Module]);
- _ ->
- %% Silently ignore this, the error will be
- %% generated later
- ok
- end,
- undef;
- true ->
- []
- end;
- _ ->
- Validators
- end.
-
--spec have_validators(module()) -> boolean().
-have_validators(Module) ->
- erlang:function_exported(Module, mod_options, 1)
- orelse erlang:function_exported(Module, mod_opt_type, 1).
-
--spec validate_opts(binary(), module(), opts()) -> {ok, opts()} | {error, string()}.
-validate_opts(Host, Module, Opts0) ->
- SubMods = get_submodules(Host, Module, Opts0),
- DefaultOpts = lists:flatmap(
- fun(M) ->
- try M:mod_options(Host)
- catch _:undef -> []
- end
- end, [Module|SubMods]),
- try
- Opts = merge_opts(Opts0, DefaultOpts, Module),
- {ok, case get_validators(Host, {Module, SubMods}) of
- undef ->
- Opts;
- Validators ->
- Opts1 = validate_opts(Host, Module, Opts, Validators),
- remove_duplicated_opts(Opts1)
- end}
- catch _:{missing_required_option, Opt} ->
- ErrTxt = io_lib:format("Module '~s' is missing required option '~s'",
- [Module, Opt]),
- module_error(ErrTxt);
- _:{invalid_option, Opt, Val} ->
- ErrTxt = io_lib:format("Invalid value for option '~s' of "
- "module ~s: ~s",
- [Opt, Module, misc:format_val({yaml, Val})]),
- module_error(ErrTxt);
- _:{invalid_option, Opt, Val, Reason} ->
- ErrTxt = io_lib:format("Invalid value for option '~s' of "
- "module ~s (~s): ~s",
- [Opt, Module, Reason, misc:format_val({yaml, Val})]),
- module_error(ErrTxt);
- _:{unknown_option, Opt, []} ->
- ErrTxt = io_lib:format("Unknown option '~s' of module '~s': "
- "the module doesn't have any options",
- [Opt, Module]),
- module_error(ErrTxt);
- _:{unknown_option, Opt, KnownOpts} ->
- ErrTxt = io_lib:format("Unknown option '~s' of module '~s',"
- " did you mean '~s'?"
- " Available options are: ~s",
- [Opt, Module,
- misc:best_match(Opt, KnownOpts),
- misc:join_atoms(KnownOpts, <<", ">>)]),
- module_error(ErrTxt)
- end.
-
--spec module_error(iolist()) -> {error, iolist()}.
-module_error(ErrTxt) ->
- ?ERROR_MSG(ErrTxt, []),
- {error, ErrTxt}.
-
--spec err_invalid_option(atom(), any()) -> no_return().
-err_invalid_option(Opt, Val) ->
- erlang:error({invalid_option, Opt, Val}).
-
--spec err_invalid_option(atom(), any(), iolist()) -> no_return().
-err_invalid_option(Opt, Val, Reason) ->
- erlang:error({invalid_option, Opt, Val, Reason}).
-
--spec err_unknown_option(atom(), [atom()]) -> no_return().
-err_unknown_option(Opt, KnownOpts) ->
- erlang:error({unknown_option, Opt, KnownOpts}).
-
--spec err_missing_required_option(atom()) -> no_return().
-err_missing_required_option(Opt) ->
- erlang:error({missing_required_option, Opt}).
-
-validate_opts(Host, Module, Opts, Validators) when is_list(Opts) ->
- lists:flatmap(
- fun({Opt, Val}) when is_atom(Opt) ->
- case lists:keyfind(Opt, 1, Validators) of
- {_, L} ->
- case lists:partition(fun is_function/1, L) of
- {[VFun|_], []} ->
- validate_opt(Opt, Val, VFun);
- {[VFun|_], SubValidators} ->
- try validate_opts(Host, Module, Val, SubValidators) of
- SubOpts ->
- validate_opt(Opt, SubOpts, VFun)
- catch _:bad_option ->
- err_invalid_option(Opt, Val)
- end
- end;
- false ->
- err_unknown_option(Opt, [K || {K, _} <- Validators])
- end;
- (_) ->
- erlang:error(bad_option)
- end, Opts);
-validate_opts(_, _, _, _) ->
- erlang:error(bad_option).
-
--spec validate_opt(atom(), any(), check_fun()) -> [{atom(), any()}].
-validate_opt(Opt, Val, VFun) ->
- try VFun(Val) of
- NewVal -> [{Opt, NewVal}]
- catch {invalid_syntax, Error} ->
- err_invalid_option(Opt, Val, Error);
- _:R when R /= undef ->
- err_invalid_option(Opt, Val)
- end.
-
--spec list_known_opts(binary(), module()) -> [atom() | {atom(), atom()}].
-list_known_opts(Host, Module) ->
- try Module:mod_options(Host) of
- DefaultOpts ->
- lists:flatmap(
- fun({Opt, [{A, _}|_] = Vals}) when is_atom(A) ->
- [{Opt, Val} || {Val, _} <- Vals];
- ({Opt, _}) -> [Opt];
- (Opt) -> [Opt]
- end, DefaultOpts)
- catch _:undef ->
- Module:mod_opt_type('')
- end.
-
--spec merge_opts(opts(), opts(), module()) -> opts().
-merge_opts(Opts, DefaultOpts, Module) ->
- Result =
- lists:foldr(
- fun({Opt, Default}, Acc) ->
- case lists:keyfind(Opt, 1, Opts) of
- {_, Val} ->
- case Default of
- [{A, _}|_] when is_atom(A) andalso is_list(Val) ->
- case is_opt_list(Val) of
- true ->
- [{Opt, merge_opts(Val, Default, Module)}|Acc];
- false ->
- err_invalid_option(Opt, Val)
- end;
- Val ->
- [{Opt, Default}|Acc];
- _ ->
- [{Opt, Val}, {Opt, Default}|Acc]
- end;
- _ ->
- [{Opt, Default}|Acc]
- end;
- (Opt, Acc) ->
- case lists:keyfind(Opt, 1, Opts) of
- {_, Val} ->
- [{Opt, Val}|Acc];
- false ->
- err_missing_required_option(Opt)
- end
- end, [], DefaultOpts),
- lists:foldl(
- fun({Opt, Val}, Acc) ->
- case lists:keymember(Opt, 1, Result) of
- true -> Acc;
- false -> [{Opt, Val}|Acc]
- end
- end, Result, Opts).
-
-remove_duplicated_opts([{Opt, Val}, {Opt, _Default}|Opts]) ->
- [{Opt, Val}|remove_duplicated_opts(Opts)];
-remove_duplicated_opts([{Opt, [{SubOpt, _}|_] = SubOpts}|Opts])
- when is_atom(SubOpt) ->
- [{Opt, remove_duplicated_opts(SubOpts)}|remove_duplicated_opts(Opts)];
-remove_duplicated_opts([OptVal|Opts]) ->
- [OptVal|remove_duplicated_opts(Opts)];
-remove_duplicated_opts([]) ->
- [].
-
--spec get_submodules(binary(), module(), opts()) -> [module()].
-get_submodules(Host, Module, Opts) ->
- try Module:mod_options(Host) of
- DefaultOpts ->
- Mod1 = case lists:keyfind(db_type, 1, DefaultOpts) of
- {_, T1} ->
- DBType = proplists:get_value(db_type, Opts, T1),
- [db_mod(DBType, Module)];
- false ->
- []
- end,
- Mod2 = case lists:keyfind(ram_db_type, 1, DefaultOpts) of
- {_, T2} ->
- RamDBType = proplists:get_value(ram_db_type, Opts, T2),
- [ram_db_mod(RamDBType, Module)];
- false ->
- []
- end,
- Mod1 ++ Mod2
- catch _:undef ->
- []
- end.
-
--spec format_module_error(atom(), start | reload, non_neg_integer(), opts(),
- error | exit | throw, any(),
- [erlang:stack_item()]) -> iolist().
-format_module_error(Module, Fun, Arity, Opts, Class, Reason, St) ->
- IsLoaded = code:ensure_loaded(Module) == {module, Module},
- IsCallbackExported = erlang:function_exported(Module, Fun, Arity),
- case {Class, Reason} of
- {error, undef} when not IsLoaded ->
- io_lib:format("Failed to ~s unknown module ~s, "
- "did you mean ~s? Hint: "
- "make sure there is no typo and ~s.beam "
- "exists inside either ~s or ~s "
- "directory",
- [Fun, Module,
- misc:best_match(
- Module, ejabberd_config:get_modules()),
- Module,
- filename:dirname(code:which(?MODULE)),
- ext_mod:modules_dir()]);
- {error, undef} when not IsCallbackExported ->
- io_lib:format("Failed to ~s module ~s because "
- "it doesn't export ~s/~B callback: "
- "is it really an ejabberd module?",
- [Fun, Module, Fun, Arity]);
- {error, {bad_return, Module, {error, _} = Err}} ->
- io_lib:format("Failed to ~s module ~s: ~s",
- [Fun, Module, misc:format_val(Err)]);
- {error, {bad_return, Module, Ret}} ->
- io_lib:format("Module ~s returned unexpected value from ~s/~B:~n"
- "** Error: ~p~n"
- "** Hint: this is either not an ejabberd module "
- "or it implements ejabbed API incorrectly",
- [Module, Fun, Arity, Ret]);
- _ ->
- io_lib:format("Internal error of module ~s has "
- "occured during ~s:~n"
- "** Options: ~p~n"
- "** Class: ~p~n"
- "** Reason: ~p~n"
- "** Stacktrace: ~p",
- [Module, Fun, Opts, Class, Reason, St])
- end.
-
-format_hosts_list([Host]) ->
- Host;
-format_hosts_list([H1, H2]) ->
- [H1, " and ", H2];
-format_hosts_list([H1, H2, H3]) ->
- [H1, ", ", H2, " and ", H3];
-format_hosts_list([H1, H2|Hs]) ->
- io_lib:format("~s, ~s and ~B more hosts",
- [H1, H2, length(Hs)]).
-
--spec db_type(binary() | global, module()) -> db_type();
- (opts(), module()) -> db_type().
-
-db_type(Opts, Module) when is_list(Opts) ->
- db_type(global, Opts, Module);
-db_type(Host, Module) when is_atom(Module) ->
- case get_module_opt(Host, Module, db_type) of
- undefined ->
- ejabberd_config:default_db(Host, Module);
- Type ->
- Type
- end.
+ Opts = ets:lookup_element(ejabberd_modules, {Module, Host}, 3),
+ get_opt(Opt, Opts).
--spec db_type(binary() | global, opts(), module()) -> db_type().
+-spec get_module_opt_hosts(binary(), module()) -> [binary()].
+get_module_opt_hosts(Host, Module) ->
+ Opts = ets:lookup_element(ejabberd_modules, {Module, Host}, 3),
+ get_opt_hosts(Opts).
-db_type(Host, Opts, Module) ->
- case get_opt(db_type, Opts) of
- undefined ->
- ejabberd_config:default_db(Host, Module);
- Type ->
- Type
+-spec get_opt_hosts(opts()) -> [binary()].
+get_opt_hosts(Opts) ->
+ case get_opt(hosts, Opts) of
+ L when L == [] orelse L == undefined ->
+ [get_opt(host, Opts)];
+ L ->
+ L
end.
--spec db_mod(binary() | global | db_type(), module()) -> module().
-
-db_mod(Type, Module) when is_atom(Type) ->
- list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type));
-db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
- db_mod(db_type(Host, Module), Module).
+-spec db_mod(binary() | global | db_type() | opts(), module()) -> module().
+db_mod(T, M) ->
+ db_mod(db_type, T, M).
--spec db_mod(binary() | global, opts(), module()) -> module().
+-spec ram_db_mod(binary() | global | db_type() | opts(), module()) -> module().
+ram_db_mod(T, M) ->
+ db_mod(ram_db_type, T, M).
-db_mod(Host, Opts, Module) when is_list(Opts) ->
- db_mod(db_type(Host, Opts, Module), Module).
-
--spec ram_db_type(binary() | global, module()) -> db_type();
- (opts(), module()) -> db_type().
-ram_db_type(Opts, Module) when is_list(Opts) ->
- ram_db_type(global, Opts, Module);
-ram_db_type(Host, Module) when is_atom(Module) ->
- case get_module_opt(Host, Module, ram_db_type) of
- undefined ->
- ejabberd_config:default_ram_db(Host, Module);
- Type ->
- Type
- end.
-
--spec ram_db_type(binary() | global, opts(), module()) -> db_type().
-ram_db_type(Host, Opts, Module) ->
- case get_opt(ram_db_type, Opts) of
- undefined ->
- ejabberd_config:default_ram_db(Host, Module);
- Type ->
- Type
- end.
-
--spec ram_db_mod(binary() | global | db_type(), module()) -> module().
-ram_db_mod(Type, Module) when is_atom(Type), Type /= global ->
- list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type));
-ram_db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
- ram_db_mod(ram_db_type(Host, Module), Module).
-
--spec ram_db_mod(binary() | global, opts(), module()) -> module().
-ram_db_mod(Host, Opts, Module) when is_list(Opts) ->
- ram_db_mod(ram_db_type(Host, Opts, Module), Module).
-
-is_db_configured(Type, Host) ->
- lists:any(
- fun(#ejabberd_module{module_host = {_, H}, opts = Opts})
- when H == Host orelse Host == global ->
- case lists:keyfind(db_type, 1, Opts) of
- {_, Type} -> true;
- _ ->
- case lists:keyfind(ram_db_type, 1, Opts) of
- {_, Type} -> true;
- _ -> false
- end
- end;
- (_) ->
- false
- end, ets:tab2list(ejabberd_modules)).
+-spec db_mod(db_type | ram_db_type,
+ binary() | global | db_type() | opts(), module()) -> module().
+db_mod(Opt, Host, Module) when is_binary(Host) orelse Host == global ->
+ db_mod(Opt, get_module_opt(Host, Module, Opt), Module);
+db_mod(Opt, Opts, Module) when is_map(Opts) ->
+ db_mod(Opt, get_opt(Opt, Opts), Module);
+db_mod(_Opt, Type, Module) when is_atom(Type) ->
+ list_to_atom(atom_to_list(Module) ++ "_" ++ atom_to_list(Type)).
-spec loaded_modules(binary()) -> [atom()].
-
loaded_modules(Host) ->
Mods = ets:select(
ejabberd_modules,
@@ -880,7 +367,6 @@ loaded_modules(Host) ->
[Mod || {Mod, _} <- lists:keysort(2, Mods)].
-spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}].
-
loaded_modules_with_opts(Host) ->
Mods = ets:select(
ejabberd_modules,
@@ -892,13 +378,12 @@ loaded_modules_with_opts(Host) ->
[{Mod, Opts} || {Mod, Opts, _} <- lists:keysort(3, Mods)].
-spec get_hosts(opts(), binary()) -> [binary()].
-
get_hosts(Opts, Prefix) ->
case get_opt(hosts, Opts) of
undefined ->
case get_opt(host, Opts) of
undefined ->
- [<<Prefix/binary, Host/binary>> || Host <- ejabberd_config:get_myhosts()];
+ [<<Prefix/binary, Host/binary>> || Host <- ejabberd_option:hosts()];
Host ->
[Host]
end;
@@ -915,7 +400,6 @@ get_module_proc(Host, Base) ->
latin1).
-spec is_loaded(binary(), atom()) -> boolean().
-
is_loaded(Host, Module) ->
ets:member(ejabberd_modules, {Module, Host}).
@@ -930,13 +414,9 @@ is_loaded_elsewhere(Host, Module) ->
-spec config_reloaded() -> ok.
config_reloaded() ->
- lists:foreach(
- fun(Host) ->
- reload_modules(Host)
- end, ejabberd_config:get_myhosts()).
+ lists:foreach(fun reload_modules/1, ejabberd_option:hosts()).
--spec is_equal_opt(atom(), opts(), opts()) ->
- true | {false, any(), any()}.
+-spec is_equal_opt(atom(), opts(), opts()) -> true | {false, any(), any()}.
is_equal_opt(Opt, NewOpts, OldOpts) ->
NewVal = get_opt(Opt, NewOpts),
OldVal = get_opt(Opt, OldOpts),
@@ -946,28 +426,187 @@ is_equal_opt(Opt, NewOpts, OldOpts) ->
true
end.
--spec is_opt_list(term()) -> boolean().
-is_opt_list([]) ->
- true;
-is_opt_list(L) when is_list(L) ->
- lists:all(
- fun({Opt, _Val}) -> is_atom(Opt);
- (_) -> false
- end, L);
-is_opt_list(_) ->
- false.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(modules) ->
- fun(Mods) ->
- lists:map(
- fun({M, A}) when is_atom(M) ->
- case is_opt_list(A) of
- true -> {M, A};
- false ->
- ?ERROR_MSG("Malformed configuration format of module ~s", [M]),
- erlang:error(badarg)
- end
- end, Mods)
- end;
-opt_type(_) -> [modules].
+%%%===================================================================
+%%% Formatters
+%%%===================================================================
+-spec format_module_error(atom(), start | reload, non_neg_integer(), opts(),
+ error | exit | throw, any(),
+ [erlang:stack_item()]) -> iolist().
+format_module_error(Module, Fun, Arity, Opts, Class, Reason, St) ->
+ case {Class, Reason} of
+ {error, {bad_return, Module, {error, _} = Err}} ->
+ io_lib:format("Failed to ~s module ~s: ~s",
+ [Fun, Module, misc:format_val(Err)]);
+ {error, {bad_return, Module, Ret}} ->
+ io_lib:format("Module ~s returned unexpected value from ~s/~B:~n"
+ "** Error: ~p~n"
+ "** Hint: this is either not an ejabberd module "
+ "or it implements ejabbed API incorrectly",
+ [Module, Fun, Arity, Ret]);
+ _ ->
+ io_lib:format("Internal error of module ~s has "
+ "occured during ~s:~n"
+ "** Options: ~p~n"
+ "** Class: ~p~n"
+ "** Reason: ~p~n"
+ "** Stacktrace: ~p",
+ [Module, Fun, Opts, Class, Reason, St])
+ end.
+
+-spec format_hosts_list([binary()]) -> iolist().
+format_hosts_list([Host]) ->
+ Host;
+format_hosts_list([H1, H2]) ->
+ [H1, " and ", H2];
+format_hosts_list([H1, H2, H3]) ->
+ [H1, ", ", H2, " and ", H3];
+format_hosts_list([H1, H2|Hs]) ->
+ io_lib:format("~s, ~s and ~B more hosts",
+ [H1, H2, length(Hs)]).
+
+-spec format_cycle([atom()]) -> iolist().
+format_cycle([M1]) ->
+ atom_to_list(M1);
+format_cycle([M1, M2]) ->
+ [atom_to_list(M1), " and ", atom_to_list(M2)];
+format_cycle([M|Ms]) ->
+ atom_to_list(M) ++ ", " ++ format_cycle(Ms).
+
+%%%===================================================================
+%%% Validation
+%%%===================================================================
+-spec validator(binary()) -> econf:validator().
+validator(Host) ->
+ econf:options(
+ #{modules =>
+ econf:and_then(
+ econf:map(
+ econf:beam([{start, 2}, {stop, 1},
+ {mod_options, 1}, {depends, 2}]),
+ econf:options(
+ #{db_type => econf:atom(),
+ ram_db_type => econf:atom(),
+ '_' => econf:any()})),
+ fun(L) ->
+ Validators = maps:from_list(
+ lists:map(
+ fun({Mod, Opts}) ->
+ {Mod, validator(Host, Mod, Opts)}
+ end, L)),
+ Validator = econf:options(Validators, [unique]),
+ Validator(L)
+ end)}).
+
+-spec validator(binary(), module(), [{atom(), term()}]) -> econf:validator().
+validator(Host, Module, Opts) ->
+ {Required, {DefaultOpts1, Validators}} =
+ lists:mapfoldl(
+ fun({M, DefOpts}, {DAcc, VAcc}) ->
+ lists:mapfoldl(
+ fun({Opt, Def}, {DAcc1, VAcc1}) ->
+ {[], {DAcc1#{Opt => Def},
+ VAcc1#{Opt => get_opt_type(Module, M, Opt)}}};
+ (Opt, {DAcc1, VAcc1}) ->
+ {[Opt], {DAcc1,
+ VAcc1#{Opt => get_opt_type(Module, M, Opt)}}}
+ end, {DAcc, VAcc}, DefOpts)
+ end, {#{}, #{}}, get_defaults(Host, Module, Opts)),
+ econf:and_then(
+ econf:options(
+ Validators,
+ [{required, lists:usort(lists:flatten(Required))},
+ {return, map}, unique]),
+ fun(Opts1) ->
+ maps:merge(DefaultOpts1, Opts1)
+ end).
+
+-spec validate(binary(), [{module(), opts()}]) ->
+ {ok, [{module(), opts(), integer()}]} |
+ econf:error_return().
+validate(Host, ModOpts) ->
+ case econf:validate(validator(Host), [{modules, ModOpts}]) of
+ {ok, [{modules, ModOpts1}]} ->
+ try sort_modules(Host, ModOpts1)
+ catch throw:{?MODULE, Reason} ->
+ {error, Reason, [modules]}
+ end;
+ {error, _, _} = Err ->
+ Err
+ end.
+
+-spec get_defaults(binary(), module(), [{atom(), term()}]) ->
+ [{module(), [{atom(), term()} | atom()]}].
+get_defaults(Host, Module, Opts) ->
+ DefaultOpts = Module:mod_options(Host),
+ [{Module, DefaultOpts}|
+ lists:filtermap(
+ fun({Opt, T1}) when Opt == db_type; Opt == ram_db_type ->
+ T2 = proplists:get_value(Opt, Opts, T1),
+ DBMod = db_mod(Opt, T2, Module),
+ case code:ensure_loaded(DBMod) of
+ {module, _} ->
+ case erlang:function_exported(DBMod, mod_options, 1) of
+ true ->
+ {true, {DBMod, DBMod:mod_options(Host)}};
+ false ->
+ false
+ end;
+ _ ->
+ false
+ end;
+ (_) ->
+ false
+ end, DefaultOpts)].
+
+-spec get_opt_type(module(), module(), atom()) -> econf:validator().
+get_opt_type(Mod, SubMod, Opt) ->
+ try SubMod:mod_opt_type(Opt)
+ catch _:_ -> Mod:mod_opt_type(Opt)
+ end.
+
+-spec sort_modules(binary(), [{module(), opts()}]) -> {ok, [{module(), opts(), integer()}]}.
+sort_modules(Host, ModOpts) ->
+ G = digraph:new([acyclic]),
+ lists:foreach(
+ fun({Mod, Opts}) ->
+ digraph:add_vertex(G, Mod, Opts),
+ Deps = Mod:depends(Host, Opts),
+ lists:foreach(
+ fun({DepMod, Type}) ->
+ case lists:keyfind(DepMod, 1, ModOpts) of
+ false when Type == hard ->
+ throw({?MODULE, {missing_module_dep, Mod, DepMod}});
+ false when Type == soft ->
+ warn_soft_dep_fail(DepMod, Mod);
+ {DepMod, DepOpts} ->
+ digraph:add_vertex(G, DepMod, DepOpts),
+ case digraph:add_edge(G, DepMod, Mod) of
+ {error, {bad_edge, Path}} ->
+ warn_cyclic_dep(Path);
+ _ ->
+ ok
+ end
+ end
+ end, Deps)
+ end, ModOpts),
+ {Result, _} = lists:mapfoldl(
+ fun(V, Order) ->
+ {M, O} = digraph:vertex(G, V),
+ {{M, O, Order}, Order+1}
+ end, 1, digraph_utils:topsort(G)),
+ digraph:delete(G),
+ {ok, Result}.
+
+-spec warn_soft_dep_fail(module(), module()) -> ok.
+warn_soft_dep_fail(DepMod, Mod) ->
+ ?WARNING_MSG("Module ~s is recommended for module "
+ "~s but is not found in the config",
+ [DepMod, Mod]).
+
+-spec warn_cyclic_dep([module()]) -> ok.
+warn_cyclic_dep(Path) ->
+ ?WARNING_MSG("Cyclic dependency detected between modules ~s. "
+ "This is either a bug, or the modules are not "
+ "supposed to work together in this configuration. "
+ "The modules will still be loaded though",
+ [format_cycle(Path)]).
diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl
index 624b2fd07..e455a4292 100644
--- a/src/gen_pubsub_node.erl
+++ b/src/gen_pubsub_node.erl
@@ -184,7 +184,7 @@
{result, {[pubsubItem()], undefined | rsm_set()}}.
-callback get_last_items(nodeIdx(), jid(), undefined | rsm_set()) ->
- {result, {[pubsubItem()], undefined | rsm_set()}}.
+ {result, [pubsubItem()]}.
-callback get_item(NodeIdx :: nodeIdx(),
ItemId :: itemId(),
diff --git a/src/gen_pubsub_nodetree.erl b/src/gen_pubsub_nodetree.erl
index aba78a89a..813ed71ce 100644
--- a/src/gen_pubsub_nodetree.erl
+++ b/src/gen_pubsub_nodetree.erl
@@ -96,7 +96,7 @@
Parents :: [nodeId()]) ->
{ok, NodeIdx::nodeIdx()} |
{error, stanza_error()} |
- {error, {virtual, {host(), nodeId()}}}.
+ {error, {virtual, {host(), nodeId()} | nodeId()}}.
-callback delete_node(Host :: host(),
NodeId :: nodeId()) ->
diff --git a/src/misc.erl b/src/misc.erl
index 4f683a431..0ebcbef78 100644
--- a/src/misc.erl
+++ b/src/misc.erl
@@ -39,7 +39,8 @@
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1,
intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
- is_mucsub_message/1, best_match/2]).
+ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
+ parse_ip_mask/1, match_ip_mask/3]).
%% Deprecated functions
-export([decode_base64/1, encode_base64/1]).
@@ -206,10 +207,10 @@ hex_to_base64(Hex) ->
url_encode(A) ->
url_encode(A, <<>>).
--spec expand_keyword(binary(), binary(), binary()) -> binary().
+-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
expand_keyword(Keyword, Input, Replacement) ->
- Parts = binary:split(Input, Keyword, [global]),
- str:join(Parts, Replacement).
+ re:replace(Input, Keyword, Replacement,
+ [{return, binary}, global]).
binary_to_atom(Bin) ->
erlang:binary_to_atom(Bin, utf8).
@@ -412,7 +413,7 @@ format_val(Term) ->
_ -> [io_lib:nl(), S]
end.
--spec cancel_timer(reference()) -> ok.
+-spec cancel_timer(reference() | undefined) -> ok.
cancel_timer(TRef) when is_reference(TRef) ->
case erlang:cancel_timer(TRef) of
false ->
@@ -425,18 +426,106 @@ cancel_timer(TRef) when is_reference(TRef) ->
cancel_timer(_) ->
ok.
--spec best_match(atom(), [atom()]) -> atom().
+-spec best_match(atom(), [atom()]) -> atom();
+ (binary(), [binary()]) -> binary().
best_match(Pattern, []) ->
Pattern;
best_match(Pattern, Opts) ->
- String = atom_to_list(Pattern),
+ F = if is_atom(Pattern) -> fun atom_to_list/1;
+ is_binary(Pattern) -> fun binary_to_list/1
+ end,
+ String = F(Pattern),
{Ds, _} = lists:mapfoldl(
fun(Opt, Cache) ->
- {Distance, Cache1} = ld(String, atom_to_list(Opt), Cache),
+ {Distance, Cache1} = ld(String, F(Opt), Cache),
{{Distance, Opt}, Cache1}
end, #{}, Opts),
element(2, lists:min(Ds)).
+-spec pmap(fun((T1) -> T2), [T1]) -> [T2].
+pmap(Fun, [_,_|_] = List) ->
+ Self = self(),
+ lists:map(
+ fun({Pid, Ref}) ->
+ receive
+ {Pid, Ret} ->
+ receive
+ {'DOWN', Ref, _, _, _} ->
+ Ret
+ end;
+ {'DOWN', Ref, _, _, Reason} ->
+ exit(Reason)
+ end
+ end, [spawn_monitor(
+ fun() -> Self ! {self(), Fun(X)} end)
+ || X <- List]);
+pmap(Fun, List) ->
+ lists:map(Fun, List).
+
+-spec peach(fun((T) -> any()), [T]) -> ok.
+peach(Fun, [_,_|_] = List) ->
+ Self = self(),
+ lists:foreach(
+ fun({Pid, Ref}) ->
+ receive
+ Pid ->
+ receive
+ {'DOWN', Ref, _, _, _} ->
+ ok
+ end;
+ {'DOWN', Ref, _, _, Reason} ->
+ exit(Reason)
+ end
+ end, [spawn_monitor(
+ fun() -> Fun(X), Self ! self() end)
+ || X <- List]);
+peach(Fun, List) ->
+ lists:foreach(Fun, List).
+
+format_exception(Level, Class, Reason, Stacktrace) ->
+ erl_error:format_exception(
+ Level, Class, Reason, Stacktrace,
+ fun(_M, _F, _A) -> false end,
+ fun(Term, I) ->
+ io_lib:print(Term, I, 80, -1)
+ end).
+
+-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
+ {ok, {inet:ip6_address(), 0..128}} |
+ error.
+parse_ip_mask(S) ->
+ case econf:validate(econf:ip_mask(), S) of
+ {ok, _} = Ret -> Ret;
+ _ -> error
+ end.
+
+-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
+match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (32 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({_, _, _, _, _, _, _, _} = IP,
+ {_, _, _, _, _, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (128 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({_, _, _, _} = IP,
+ {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (128 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
+ {_, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (32 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask(_, _, _) ->
+ false.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -515,3 +604,11 @@ ld([_|ST] = S, [_|TT] = T, Cache) ->
L = 1 + lists:min([L1, L2, L3]),
{L, maps:put({S, T}, L, C3)}
end.
+
+-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
+ip_to_integer({IP1, IP2, IP3, IP4}) ->
+ IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
+ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
+ IP8}) ->
+ IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
+ bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.
diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl
index f6a61fe03..528da91d6 100644
--- a/src/mod_adhoc.erl
+++ b/src/mod_adhoc.erl
@@ -93,8 +93,7 @@ reload(_Host, _NewOpts, _OldOpts) ->
get_local_commands(Acc, _From,
#jid{server = Server, lserver = LServer} = _To, <<"">>,
Lang) ->
- Display = gen_mod:get_module_opt(LServer, ?MODULE,
- report_commands_node),
+ Display = mod_adhoc_opt:report_commands_node(LServer),
case Display of
false -> Acc;
_ ->
@@ -121,8 +120,7 @@ get_local_commands(Acc, _From, _To, _Node, _Lang) ->
get_sm_commands(Acc, _From,
#jid{lserver = LServer} = To, <<"">>, Lang) ->
- Display = gen_mod:get_module_opt(LServer, ?MODULE,
- report_commands_node),
+ Display = mod_adhoc_opt:report_commands_node(LServer),
case Display of
false -> Acc;
_ ->
@@ -275,7 +273,7 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(report_commands_node) ->
- fun (B) when is_boolean(B) -> B end.
+ econf:bool().
mod_options(_Host) ->
[{report_commands_node, false}].
diff --git a/src/mod_adhoc_opt.erl b/src/mod_adhoc_opt.erl
new file mode 100644
index 000000000..bb805a447
--- /dev/null
+++ b/src/mod_adhoc_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_adhoc_opt).
+
+-export([report_commands_node/1]).
+
+-spec report_commands_node(gen_mod:opts() | global | binary()) -> boolean().
+report_commands_node(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(report_commands_node, Opts);
+report_commands_node(Host) ->
+ gen_mod:get_module_opt(Host, mod_adhoc, report_commands_node).
+
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 739e53341..a066dc487 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -1534,7 +1534,7 @@ stats(Name) ->
case Name of
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
<<"processes">> -> length(erlang:processes());
- <<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_config:get_myhosts());
+ <<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ejabberd_option:hosts());
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
end.
diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl
index 0212dab25..4f45afa94 100644
--- a/src/mod_admin_update_sql.erl
+++ b/src/mod_admin_update_sql.erl
@@ -81,7 +81,7 @@ update_sql() ->
_ ->
update_sql(Host)
end
- end, ejabberd_config:get_myhosts()),
+ end, ejabberd_option:hosts()),
ok.
-record(state, {host :: binary(),
@@ -90,7 +90,7 @@ update_sql() ->
update_sql(Host) ->
LHost = jid:nameprep(Host),
- DBType = ejabberd_config:get_option({sql_type, LHost}, undefined),
+ DBType = ejabberd_option:sql_type(LHost),
IsSupported =
case DBType of
pgsql -> true;
diff --git a/src/mod_announce.erl b/src/mod_announce.erl
index 6d11cde22..cee9384d6 100644
--- a/src/mod_announce.erl
+++ b/src/mod_announce.erl
@@ -85,8 +85,8 @@ stop(Host) ->
gen_mod:stop_child(?MODULE, Host).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -102,7 +102,7 @@ depends(_Host, _Opts) ->
%%====================================================================
init([Host, Opts]) ->
process_flag(trap_exit, true),
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(local_send_to_resource_hook, Host,
@@ -663,7 +663,7 @@ announce_motd(#message{to = To} = Packet) ->
announce_motd(To#jid.lserver, Packet).
announce_all_hosts_motd(Packet) ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
[announce_motd(Host, Packet) || Host <- Hosts].
announce_motd(Host, Packet) ->
@@ -678,7 +678,7 @@ announce_motd_update(#message{to = To} = Packet) ->
announce_motd_update(To#jid.lserver, Packet).
announce_all_hosts_motd_update(Packet) ->
- Hosts = ejabberd_config:get_myhosts(),
+ Hosts = ejabberd_option:hosts(),
[announce_motd_update(Host, Packet) || Host <- Hosts].
announce_motd_update(LServer, Packet) ->
@@ -696,7 +696,7 @@ announce_all_hosts_motd_delete(_Packet) ->
fun(Host) ->
Mod = gen_mod:db_mod(Host, ?MODULE),
delete_motd(Mod, Host)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec send_motd({presence(), ejabberd_c2s:state()}) -> {presence(), ejabberd_c2s:state()}.
send_motd({_, #{pres_last := _}} = Acc) ->
@@ -829,7 +829,7 @@ send_announcement_to_all(Host, SubjectS, BodyS) ->
-spec get_access(global | binary()) -> atom().
get_access(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, access).
+ mod_announce_opt:access(Host).
-spec add_store_hint(stanza()) -> stanza().
add_store_hint(El) ->
@@ -853,9 +853,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_announce_opt:cache_size(Opts),
+ CacheMissed = mod_announce_opt:cache_missed(Opts),
+ LifeTime = case mod_announce_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -865,7 +865,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_announce_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -897,19 +897,23 @@ import(LServer, {sql, _}, DBType, Tab, List) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, List).
-mod_opt_type(access) -> fun acl:access_rules_validator/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(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_opt_type(access) ->
+ econf:acl();
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
[{access, none},
{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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_announce_mnesia.erl b/src/mod_announce_mnesia.erl
index ea7c54f36..c5a2a2d9a 100644
--- a/src/mod_announce_mnesia.erl
+++ b/src/mod_announce_mnesia.erl
@@ -98,10 +98,10 @@ set_motd_user(LUser, LServer) ->
end,
transaction(F).
-need_transform(#motd{server = S}) when is_list(S) ->
+need_transform({motd, S, _}) when is_list(S) ->
?INFO_MSG("Mnesia table 'motd' will be converted to binary", []),
true;
-need_transform(#motd_users{us = {U, S}}) when is_list(U) orelse is_list(S) ->
+need_transform({motd_users, {U, S}, _}) when is_list(U) orelse is_list(S) ->
?INFO_MSG("Mnesia table 'motd_users' will be converted to binary", []),
true;
need_transform(_) ->
diff --git a/src/mod_announce_opt.erl b/src/mod_announce_opt.erl
new file mode 100644
index 000000000..7292378a5
--- /dev/null
+++ b/src/mod_announce_opt.erl
@@ -0,0 +1,48 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_announce_opt).
+
+-export([access/1]).
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, access).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_announce, use_cache).
+
diff --git a/src/mod_announce_sql.erl b/src/mod_announce_sql.erl
index 60c3edcf6..dc05afe20 100644
--- a/src/mod_announce_sql.erl
+++ b/src/mod_announce_sql.erl
@@ -26,7 +26,6 @@
-behaviour(mod_announce).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl
index e706a23c3..047b33e82 100644
--- a/src/mod_avatar.erl
+++ b/src/mod_avatar.erl
@@ -35,7 +35,8 @@
-include("logger.hrl").
-include("pubsub.hrl").
--type convert_rules() :: {default | eimp:img_type(), eimp:img_type()}.
+-opaque convert_rule() :: {default | eimp:img_type(), eimp:img_type()}.
+-export_type([convert_rule/0]).
%%%===================================================================
%%% API
@@ -75,7 +76,7 @@ pubsub_publish_item(LServer, ?NS_AVATAR_METADATA,
#avatar_meta{info = []} ->
delete_vcard_avatar(From);
#avatar_meta{info = Info} ->
- Rules = get_converting_rules(LServer),
+ Rules = mod_avatar_opt:convert(LServer),
case get_meta_info(Info, Rules) of
#avatar_info{type = MimeType, id = ID, url = <<"">>} = I ->
case get_avatar_data(Host, ID) of
@@ -168,7 +169,7 @@ get_sm_features(Acc, _From, _To, _Node, _Lang) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
--spec get_meta_info([avatar_info()], convert_rules()) -> avatar_info().
+-spec get_meta_info([avatar_info()], [convert_rule()]) -> avatar_info().
get_meta_info(Info, Rules) ->
case lists:foldl(
fun(_, #avatar_info{} = Acc) ->
@@ -317,7 +318,7 @@ publish_avatar(#iq{from = JID} = IQ, Meta, MimeType, Data, ItemID) ->
{error, eimp:error_reason() | base64_error} |
pass.
convert_avatar(LUser, LServer, VCard) ->
- case get_converting_rules(LServer) of
+ case mod_avatar_opt:convert(LServer) of
[] ->
pass;
Rules ->
@@ -329,19 +330,19 @@ convert_avatar(LUser, LServer, VCard) ->
end
end.
--spec convert_avatar(binary(), binary(), binary(), convert_rules()) ->
- {ok, eimp:img_type(), binary()} |
+-spec convert_avatar(binary(), binary(), binary(), [convert_rule()]) ->
+ {ok, binary(), binary()} |
{error, eimp:error_reason()} |
pass.
convert_avatar(LUser, LServer, Data, Rules) ->
Type = get_type(Data),
NewType = convert_to_type(Type, Rules),
- if NewType == undefined orelse Type == NewType ->
+ if NewType == undefined ->
pass;
true ->
?DEBUG("Converting avatar of ~s@~s: ~s -> ~s",
[LUser, LServer, Type, NewType]),
- RateLimit = gen_mod:get_module_opt(LServer, ?MODULE, rate_limit),
+ RateLimit = mod_avatar_opt:rate_limit(LServer),
Opts = [{limit_by, {LUser, LServer}},
{rate_limit, RateLimit}],
case eimp:convert(Data, NewType, Opts) of
@@ -401,15 +402,11 @@ stop_with_error(Lang, Reason) ->
Txt = eimp:format_error(Reason),
{stop, xmpp:err_internal_server_error(Txt, Lang)}.
--spec get_converting_rules(binary()) -> convert_rules().
-get_converting_rules(LServer) ->
- gen_mod:get_module_opt(LServer, ?MODULE, convert).
-
-spec get_type(binary()) -> eimp:img_type() | unknown.
get_type(Data) ->
eimp:get_type(Data).
--spec convert_to_type(eimp:img_type() | unknown, convert_rules()) ->
+-spec convert_to_type(eimp:img_type() | unknown, [convert_rule()]) ->
eimp:img_type() | undefined.
convert_to_type(unknown, _Rules) ->
undefined;
@@ -417,6 +414,8 @@ convert_to_type(Type, Rules) ->
case proplists:get_value(Type, Rules) of
undefined ->
proplists:get_value(default, Rules);
+ Type ->
+ undefined;
T ->
T
end.
@@ -435,38 +434,23 @@ decode_mime_type(MimeType) ->
encode_mime_type(Type) ->
<<"image/", (atom_to_binary(Type, latin1))/binary>>.
--spec fail(atom()) -> no_return().
-fail(Format) ->
- FormatS = case Format of
- webp -> "WebP";
- png -> "PNG";
- jpeg -> "JPEG";
- gif -> "GIF";
- _ -> ""
- end,
- if FormatS /= "" ->
- ?WARNING_MSG("ejabberd is not compiled with ~s support", [FormatS]);
- true ->
- ok
- end,
- erlang:error(badarg).
-
-mod_opt_type({convert, From}) ->
- fun(To) when is_atom(To), To /= From ->
- case eimp:is_supported(From) orelse From == default of
- false ->
- fail(From);
- true ->
- case eimp:is_supported(To) orelse To == undefined of
- false -> fail(To);
- true -> To
- end
- end
- end;
+mod_opt_type(convert) ->
+ Formats = eimp:supported_formats(),
+ econf:and_then(
+ fun(_) when Formats == [] ->
+ econf:fail(eimp_error);
+ (V) ->
+ V
+ end,
+ econf:options(
+ maps:from_list(
+ [{Type, econf:enum(Formats)}
+ || Type <- [default|Formats]])));
mod_opt_type(rate_limit) ->
- fun(I) when is_integer(I), I > 0 -> I end.
+ econf:pos_int().
+-spec mod_options(binary()) -> [{convert, [?MODULE:convert_rule()]} |
+ {atom(), any()}].
mod_options(_) ->
[{rate_limit, 10},
- {convert,
- [{T, undefined} || T <- [default|eimp:supported_formats()]]}].
+ {convert, []}].
diff --git a/src/mod_avatar_opt.erl b/src/mod_avatar_opt.erl
new file mode 100644
index 000000000..187ed16db
--- /dev/null
+++ b/src/mod_avatar_opt.erl
@@ -0,0 +1,20 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_avatar_opt).
+
+-export([convert/1]).
+-export([rate_limit/1]).
+
+-spec convert(gen_mod:opts() | global | binary()) -> [mod_avatar:convert_rule()].
+convert(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(convert, Opts);
+convert(Host) ->
+ gen_mod:get_module_opt(Host, mod_avatar, convert).
+
+-spec rate_limit(gen_mod:opts() | global | binary()) -> pos_integer().
+rate_limit(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(rate_limit, Opts);
+rate_limit(Host) ->
+ gen_mod:get_module_opt(Host, mod_avatar, rate_limit).
+
diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl
index 486bea7fd..80504c9fb 100644
--- a/src/mod_block_strangers.erl
+++ b/src/mod_block_strangers.erl
@@ -90,8 +90,8 @@ filter_subscription(Acc, #presence{meta = #{captcha := passed}}) ->
filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
id = SID, type = subscribe} = Pres) ->
LServer = To#jid.lserver,
- case gen_mod:get_module_opt(LServer, ?MODULE, drop) andalso
- gen_mod:get_module_opt(LServer, ?MODULE, captcha) andalso
+ case mod_block_strangers_opt:drop(LServer) andalso
+ mod_block_strangers_opt:captcha(LServer) andalso
need_check(Pres) of
true ->
case check_subscription(From, To) of
@@ -106,7 +106,7 @@ filter_subscription(Acc, #presence{from = From, to = To, lang = Lang,
Msg = #message{from = BTo, to = From,
id = ID, body = Body,
sub_els = CaptchaEls},
- case gen_mod:get_module_opt(LServer, ?MODULE, log) of
+ case mod_block_strangers_opt:log(LServer) of
true ->
?INFO_MSG("Challenge subscription request "
"from stranger ~s to ~s with "
@@ -151,8 +151,8 @@ check_message(#message{from = From, to = To, lang = Lang} = Msg) ->
true ->
case check_subscription(From, To) of
false ->
- Drop = gen_mod:get_module_opt(LServer, ?MODULE, drop),
- Log = gen_mod:get_module_opt(LServer, ?MODULE, log),
+ Drop = mod_block_strangers_opt:drop(LServer),
+ Log = mod_block_strangers_opt:log(LServer),
if
Log ->
?INFO_MSG("~s message from stranger ~s to ~s",
@@ -199,8 +199,8 @@ need_check(Pkt) ->
_ ->
false
end,
- AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
+ AllowLocalUsers = mod_block_strangers_opt:allow_local_users(LServer),
+ Access = mod_block_strangers_opt:access(LServer),
not (IsSelf orelse IsEmpty
orelse acl:match_rule(LServer, Access, From) == allow
orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
@@ -215,7 +215,7 @@ check_subscription(From, To) ->
false;
false ->
%% Check if the contact's server is in the roster
- gen_mod:get_module_opt(LocalServer, ?MODULE, allow_transports)
+ mod_block_strangers_opt:allow_transports(LocalServer)
andalso mod_roster:is_subscribed(jid:make(RemoteServer), To);
true ->
true
@@ -230,19 +230,18 @@ sets_bare_member({U, S, <<"">>} = LBJID, Set) ->
depends(_Host, _Opts) ->
[].
+mod_opt_type(access) ->
+ econf:acl();
mod_opt_type(drop) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(log) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
+mod_opt_type(captcha) ->
+ econf:bool();
mod_opt_type(allow_local_users) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(allow_transports) ->
- fun (B) when is_boolean(B) -> B end;
-mod_opt_type(captcha) ->
- fun (B) when is_boolean(B) -> B end;
-mod_opt_type(access) ->
- fun acl:access_rules_validator/1.
-
+ econf:bool().
mod_options(_) ->
[{access, none},
diff --git a/src/mod_block_strangers_opt.erl b/src/mod_block_strangers_opt.erl
new file mode 100644
index 000000000..33dc6cc09
--- /dev/null
+++ b/src/mod_block_strangers_opt.erl
@@ -0,0 +1,48 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_block_strangers_opt).
+
+-export([access/1]).
+-export([allow_local_users/1]).
+-export([allow_transports/1]).
+-export([captcha/1]).
+-export([drop/1]).
+-export([log/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, access).
+
+-spec allow_local_users(gen_mod:opts() | global | binary()) -> boolean().
+allow_local_users(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(allow_local_users, Opts);
+allow_local_users(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, allow_local_users).
+
+-spec allow_transports(gen_mod:opts() | global | binary()) -> boolean().
+allow_transports(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(allow_transports, Opts);
+allow_transports(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, allow_transports).
+
+-spec captcha(gen_mod:opts() | global | binary()) -> boolean().
+captcha(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(captcha, Opts);
+captcha(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, captcha).
+
+-spec drop(gen_mod:opts() | global | binary()) -> boolean().
+drop(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(drop, Opts);
+drop(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, drop).
+
+-spec log(gen_mod:opts() | global | binary()) -> boolean().
+log(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(log, Opts);
+log(Host) ->
+ gen_mod:get_module_opt(Host, mod_block_strangers, log).
+
diff --git a/src/mod_bosh.erl b/src/mod_bosh.erl
index 03bdc6e15..d8268e2be 100644
--- a/src/mod_bosh.erl
+++ b/src/mod_bosh.erl
@@ -115,10 +115,9 @@ find_session(SID) ->
end
end.
-start(Host, Opts) ->
- start_jiffy(Opts),
- Mod = gen_mod:ram_db_mod(global, ?MODULE),
- init_cache(Mod),
+start(Host, _Opts) ->
+ Mod = gen_mod:ram_db_mod(Host, ?MODULE),
+ init_cache(Host, Mod),
Mod:init(),
clean_cache(),
TmpSup = gen_mod:get_module_proc(Host, ?MODULE),
@@ -132,30 +131,15 @@ stop(Host) ->
supervisor:terminate_child(ejabberd_gen_mod_sup, TmpSup),
supervisor:delete_child(ejabberd_gen_mod_sup, TmpSup).
-reload(_Host, NewOpts, _OldOpts) ->
- start_jiffy(NewOpts),
+reload(Host, _NewOpts, _OldOpts) ->
Mod = gen_mod:ram_db_mod(global, ?MODULE),
- init_cache(Mod),
+ init_cache(Host, Mod),
Mod:init(),
ok.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-start_jiffy(Opts) ->
- case gen_mod:get_opt(json, Opts) of
- false ->
- ok;
- true ->
- case catch ejabberd:start_app(jiffy) of
- ok ->
- ok;
- Err ->
- ?WARNING_MSG("Failed to start JSON codec (jiffy): ~p. "
- "JSON support will be disabled", [Err])
- end
- end.
-
get_type(Hdrs) ->
try
{_, S} = lists:keyfind('Content-Type', 1, Hdrs),
@@ -170,31 +154,36 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(json) ->
- fun (false) -> false;
- (true) -> true
- end;
+ econf:and_then(
+ econf:bool(),
+ fun(false) -> false;
+ (true) ->
+ ejabberd:start_app(jiffy),
+ true
+ end);
mod_opt_type(max_concat) ->
- fun (unlimited) -> unlimited;
- (N) when is_integer(N), N > 0 -> N
- end;
+ econf:pos_int(unlimited);
mod_opt_type(max_inactivity) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(max_pause) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(prebind) ->
- fun (B) when is_boolean(B) -> B end;
-mod_opt_type(ram_db_type) ->
- fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+ econf:bool();
mod_opt_type(queue_type) ->
- fun(ram) -> ram; (file) -> file 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
- end.
-
+ econf:well_known(queue_type, ?MODULE);
+mod_opt_type(ram_db_type) ->
+ econf:well_known(ram_db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
+
+-spec mod_options(binary()) -> [{json, boolean()} |
+ {atom(), term()}].
mod_options(Host) ->
[{json, false},
{max_concat, unlimited},
@@ -202,29 +191,33 @@ mod_options(Host) ->
{max_pause, 120},
{prebind, false},
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
- {queue_type, ejabberd_config:default_queue_type(Host)},
- {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)}].
+ {queue_type, ejabberd_option:queue_type(Host)},
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
%%%----------------------------------------------------------------------
%%% Cache stuff
%%%----------------------------------------------------------------------
--spec init_cache(module()) -> ok.
-init_cache(Mod) ->
- case use_cache(Mod) of
+-spec init_cache(binary(), module()) -> ok.
+init_cache(Host, Mod) ->
+ case use_cache(Mod, Host) of
true ->
- ets_cache:new(?BOSH_CACHE, cache_opts());
+ ets_cache:new(?BOSH_CACHE, cache_opts(Host));
false ->
ets_cache:delete(?BOSH_CACHE)
end.
-spec use_cache(module()) -> boolean().
use_cache(Mod) ->
+ use_cache(Mod, global).
+
+-spec use_cache(module(), global | binary()) -> boolean().
+use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 0) of
true -> Mod:use_cache();
- false -> gen_mod:get_module_opt(global, ?MODULE, use_cache)
+ false -> mod_bosh_opt:use_cache(Host)
end.
-spec cache_nodes(module()) -> [node()].
@@ -243,11 +236,11 @@ delete_cache(Mod, SID) ->
ok
end.
--spec cache_opts() -> [proplists:property()].
-cache_opts() ->
- MaxSize = gen_mod:get_module_opt(global, ?MODULE, cache_size),
- CacheMissed = gen_mod:get_module_opt(global, ?MODULE, cache_missed),
- LifeTime = case gen_mod:get_module_opt(global, ?MODULE, cache_life_time) of
+-spec cache_opts(binary()) -> [proplists:property()].
+cache_opts(Host) ->
+ MaxSize = mod_bosh_opt:cache_size(Host),
+ CacheMissed = mod_bosh_opt:cache_missed(Host),
+ LifeTime = case mod_bosh_opt:cache_life_time(Host) of
infinity -> infinity;
I -> timer:seconds(I)
end,
diff --git a/src/mod_bosh_opt.erl b/src/mod_bosh_opt.erl
new file mode 100644
index 000000000..44b908515
--- /dev/null
+++ b/src/mod_bosh_opt.erl
@@ -0,0 +1,83 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_bosh_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([json/1]).
+-export([max_concat/1]).
+-export([max_inactivity/1]).
+-export([max_pause/1]).
+-export([prebind/1]).
+-export([queue_type/1]).
+-export([ram_db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, cache_size).
+
+-spec json(gen_mod:opts() | global | binary()) -> boolean().
+json(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(json, Opts);
+json(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, json).
+
+-spec max_concat(gen_mod:opts() | global | binary()) -> 'unlimited' | pos_integer().
+max_concat(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_concat, Opts);
+max_concat(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, max_concat).
+
+-spec max_inactivity(gen_mod:opts() | global | binary()) -> pos_integer().
+max_inactivity(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_inactivity, Opts);
+max_inactivity(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, max_inactivity).
+
+-spec max_pause(gen_mod:opts() | global | binary()) -> pos_integer().
+max_pause(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_pause, Opts);
+max_pause(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, max_pause).
+
+-spec prebind(gen_mod:opts() | global | binary()) -> boolean().
+prebind(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(prebind, Opts);
+prebind(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, prebind).
+
+-spec queue_type(gen_mod:opts() | global | binary()) -> 'file' | 'ram'.
+queue_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_type, Opts);
+queue_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, queue_type).
+
+-spec ram_db_type(gen_mod:opts() | global | binary()) -> atom().
+ram_db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ram_db_type, Opts);
+ram_db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, ram_db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_bosh, use_cache).
+
diff --git a/src/mod_bosh_riak.erl b/src/mod_bosh_riak.erl
index 7ebd1bd6f..082141747 100644
--- a/src/mod_bosh_riak.erl
+++ b/src/mod_bosh_riak.erl
@@ -53,7 +53,7 @@ find_session(SID) ->
%%% Internal functions
%%%===================================================================
bosh_schema() ->
- {record_info(fields, bosh), #bosh{}}.
+ {record_info(fields, bosh), #bosh{sid = <<>>, pid = self()}}.
clean_table() ->
?DEBUG("Cleaning Riak 'bosh' table...", []),
diff --git a/src/mod_bosh_sql.erl b/src/mod_bosh_sql.erl
index 4ec65e779..328211b83 100644
--- a/src/mod_bosh_sql.erl
+++ b/src/mod_bosh_sql.erl
@@ -26,7 +26,6 @@
-module(mod_bosh_sql).
-behaviour(mod_bosh).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/0, open_session/2, close_session/1, find_session/1]).
diff --git a/src/mod_caps.erl b/src/mod_caps.erl
index 96b44cd68..75c4d6356 100644
--- a/src/mod_caps.erl
+++ b/src/mod_caps.erl
@@ -252,8 +252,8 @@ depends(_Host, _Opts) ->
[].
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if OldMod /= NewMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -263,7 +263,7 @@ reload(Host, NewOpts, OldOpts) ->
init([Host, Opts]) ->
process_flag(trap_exit, true),
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
init_cache(Mod, Host, Opts),
Mod:init(Host, Opts),
ejabberd_hooks:add(c2s_presence_in, Host, ?MODULE,
@@ -497,13 +497,13 @@ init_cache(Mod, Host, Opts) ->
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)
+ false -> mod_caps_opt:use_cache(Host)
end.
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
+ MaxSize = mod_caps_opt:cache_size(Opts),
+ CacheMissed = mod_caps_opt:cache_missed(Opts),
+ LifeTime = case mod_caps_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -544,17 +544,20 @@ import_next(LServer, DBType, NodePair) ->
Mod:import(LServer, NodePair, Features),
import_next(LServer, DBType, ets:next(caps_features_tmp, NodePair)).
-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_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end.
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_caps_opt.erl b/src/mod_caps_opt.erl
new file mode 100644
index 000000000..9af68f115
--- /dev/null
+++ b/src/mod_caps_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_caps_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_caps, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_caps, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_caps, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_caps, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_caps, use_cache).
+
diff --git a/src/mod_caps_sql.erl b/src/mod_caps_sql.erl
index b0829156e..f32759564 100644
--- a/src/mod_caps_sql.erl
+++ b/src/mod_caps_sql.erl
@@ -26,7 +26,6 @@
-behaviour(mod_caps).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, caps_read/2, caps_write/3, export/1, import/3]).
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index 72c098570..4fe954043 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -36,7 +36,7 @@
-export([user_send_packet/1, user_receive_packet/1,
iq_handler/1, disco_features/5,
- is_carbon_copy/1, mod_opt_type/1, depends/2,
+ is_carbon_copy/1, depends/2,
mod_options/1]).
-export([c2s_copy_session/2, c2s_session_opened/1, c2s_session_resumed/1]).
%% For debugging purposes
@@ -219,15 +219,14 @@ send_copies(JID, To, Packet, Direction)->
%TargetJIDs = lists:delete(JID, [ jid:make({U, S, CCRes}) || CCRes <- list(U, S) ]),
end,
- lists:map(fun({Dest, _Version}) ->
- {_, _, Resource} = jid:tolower(Dest),
- ?DEBUG("Sending: ~p =/= ~p", [R, Resource]),
- Sender = jid:make({U, S, <<>>}),
- %{xmlelement, N, A, C} = Packet,
- New = build_forward_packet(JID, Packet, Sender, Dest, Direction),
- ejabberd_router:route(xmpp:set_from_to(New, Sender, Dest))
- end, TargetJIDs),
- ok.
+ lists:foreach(
+ fun({Dest, _Version}) ->
+ {_, _, Resource} = jid:tolower(Dest),
+ ?DEBUG("Sending: ~p =/= ~p", [R, Resource]),
+ Sender = jid:make({U, S, <<>>}),
+ New = build_forward_packet(JID, Packet, Sender, Dest, Direction),
+ ejabberd_router:route(xmpp:set_from_to(New, Sender, Dest))
+ end, TargetJIDs).
-spec build_forward_packet(jid(), message(), jid(), jid(), direction()) -> message().
build_forward_packet(JID, #message{type = T} = Msg, Sender, Dest, Direction) ->
@@ -299,19 +298,5 @@ list(User, Server) ->
depends(_Host, _Opts) ->
[].
-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(_) ->
- [{ram_db_type, deprecated},
- {use_cache, deprecated},
- {cache_size, deprecated},
- {cache_missed, deprecated},
- {cache_life_time, deprecated}].
+ [].
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index e87a2635b..0b9bab34d 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -58,9 +58,9 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> ok.
start(Host, Opts) ->
- QueuePresence = gen_mod:get_opt(queue_presence, Opts),
- QueueChatStates = gen_mod:get_opt(queue_chat_states, Opts),
- QueuePEP = gen_mod:get_opt(queue_pep, Opts),
+ QueuePresence = mod_client_state_opt:queue_presence(Opts),
+ QueueChatStates = mod_client_state_opt:queue_chat_states(Opts),
+ QueuePEP = mod_client_state_opt:queue_pep(Opts),
if QueuePresence; QueueChatStates; QueuePEP ->
register_hooks(Host),
if QueuePresence ->
@@ -83,9 +83,9 @@ start(Host, Opts) ->
-spec stop(binary()) -> ok.
stop(Host) ->
- QueuePresence = gen_mod:get_module_opt(Host, ?MODULE, queue_presence),
- QueueChatStates = gen_mod:get_module_opt(Host, ?MODULE, queue_chat_states),
- QueuePEP = gen_mod:get_module_opt(Host, ?MODULE, queue_pep),
+ QueuePresence = mod_client_state_opt:queue_presence(Host),
+ QueueChatStates = mod_client_state_opt:queue_chat_states(Host),
+ QueuePEP = mod_client_state_opt:queue_pep(Host),
if QueuePresence; QueueChatStates; QueuePEP ->
unregister_hooks(Host),
if QueuePresence ->
@@ -108,9 +108,9 @@ stop(Host) ->
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
reload(Host, NewOpts, _OldOpts) ->
- QueuePresence = gen_mod:get_opt(queue_presence, NewOpts),
- QueueChatStates = gen_mod:get_opt(queue_chat_states, NewOpts),
- QueuePEP = gen_mod:get_opt(queue_pep, NewOpts),
+ QueuePresence = mod_client_state_opt:queue_presence(NewOpts),
+ QueueChatStates = mod_client_state_opt:queue_chat_states(NewOpts),
+ QueuePEP = mod_client_state_opt:queue_pep(NewOpts),
if QueuePresence; QueueChatStates; QueuePEP ->
register_hooks(Host);
true ->
@@ -138,13 +138,13 @@ reload(Host, NewOpts, _OldOpts) ->
filter_pep, 50)
end.
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(queue_presence) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(queue_chat_states) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(queue_pep) ->
- fun(B) when is_boolean(B) -> B end.
+ econf:bool().
mod_options(_) ->
[{queue_presence, true},
diff --git a/src/mod_client_state_opt.erl b/src/mod_client_state_opt.erl
new file mode 100644
index 000000000..ff286dc15
--- /dev/null
+++ b/src/mod_client_state_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_client_state_opt).
+
+-export([queue_chat_states/1]).
+-export([queue_pep/1]).
+-export([queue_presence/1]).
+
+-spec queue_chat_states(gen_mod:opts() | global | binary()) -> boolean().
+queue_chat_states(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_chat_states, Opts);
+queue_chat_states(Host) ->
+ gen_mod:get_module_opt(Host, mod_client_state, queue_chat_states).
+
+-spec queue_pep(gen_mod:opts() | global | binary()) -> boolean().
+queue_pep(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_pep, Opts);
+queue_pep(Host) ->
+ gen_mod:get_module_opt(Host, mod_client_state, queue_pep).
+
+-spec queue_presence(gen_mod:opts() | global | binary()) -> boolean().
+queue_presence(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_presence, Opts);
+queue_presence(Host) ->
+ gen_mod:get_module_opt(Host, mod_client_state, queue_presence).
+
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index 36ea75141..915fa4a87 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -180,10 +180,6 @@ get_local_identity(Acc, _From, _To, Node, Lang) ->
Lang);
?NS_ADMINL(<<"get-online-users-num">>) ->
?INFO_COMMAND(<<"Get Number of Online Users">>, Lang);
- [<<"config">>, <<"acls">>] ->
- ?INFO_COMMAND(<<"Access Control Lists">>, Lang);
- [<<"config">>, <<"access">>] ->
- ?INFO_COMMAND(<<"Access Rules">>, Lang);
_ -> Acc
end.
@@ -508,10 +504,6 @@ get_local_items(_Host, [], Server, Lang) ->
<<"outgoing s2s">>),
?NODE(<<"Running Nodes">>, <<"running nodes">>),
?NODE(<<"Stopped Nodes">>, <<"stopped nodes">>)]};
-get_local_items(_Host, [<<"config">>], Server, Lang) ->
- {result,
- [?NODE(<<"Access Control Lists">>, <<"config/acls">>),
- ?NODE(<<"Access Rules">>, <<"config/access">>)]};
get_local_items(_Host, [<<"config">>, _], _Server,
_Lang) ->
{result, []};
@@ -1076,42 +1068,6 @@ get_form(_Host,
#xdata_field{var = <<"announcement">>,
type = 'text-multi',
label = ?T(Lang, <<"Message body">>)}]}};
-get_form(Host, [<<"config">>, <<"acls">>], Lang) ->
- ACLs = str:tokens(
- str:format("~p.",
- [mnesia:dirty_select(
- acl,
- ets:fun2ms(
- fun({acl, {Name, H}, Spec}) when H == Host ->
- {acl, Name, Spec}
- end))]),
- <<"\n">>),
- {result,
- #xdata{title = ?T(Lang, <<"Access Control List Configuration">>),
- type = form,
- fields = [?HFIELD(),
- #xdata_field{type = 'text-multi',
- label = ?T(Lang, <<"Access Control Lists">>),
- var = <<"acls">>,
- values = ACLs}]}};
-get_form(Host, [<<"config">>, <<"access">>], Lang) ->
- Accs = str:tokens(
- str:format("~p.",
- [mnesia:dirty_select(
- access,
- ets:fun2ms(
- fun({access, {Name, H}, Acc}) when H == Host ->
- {access, Name, Acc}
- end))]),
- <<"\n">>),
- {result,
- #xdata{title = ?T(Lang, <<"Access Configuration">>),
- type = form,
- fields = [?HFIELD(),
- #xdata_field{type = 'text-multi',
- label = ?T(Lang, <<"Access Rules">>),
- var = <<"access">>,
- values = Accs}]}};
get_form(_Host, ?NS_ADMINL(<<"add-user">>), Lang) ->
{result,
#xdata{title = ?T(Lang, <<"Add User">>),
@@ -1447,75 +1403,6 @@ set_form(From, Host,
[<<"running nodes">>, ENode, <<"shutdown">>], _Lang,
XData) ->
stop_node(From, Host, ENode, stop, XData);
-set_form(_From, Host, [<<"config">>, <<"acls">>], Lang,
- XData) ->
- case xmpp_util:get_xdata_values(<<"acls">>, XData) of
- [] ->
- Txt = <<"No 'acls' found in data form">>,
- {error, xmpp:err_bad_request(Txt, Lang)};
- Strings ->
- String = lists:foldl(fun (S, Res) ->
- <<Res/binary, S/binary, "\n">>
- end, <<"">>, Strings),
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, ACLs} ->
- acl:add_list(Host, ACLs, true),
- {result, undefined};
- _ ->
- Txt = <<"Parse failed">>,
- {error, xmpp:err_bad_request(Txt, Lang)}
- end;
- _ ->
- {error, xmpp:err_bad_request(<<"Scan failed">>, Lang)}
- end
- end;
-set_form(_From, Host, [<<"config">>, <<"access">>],
- Lang, XData) ->
- SetAccess =
- fun(Rs) ->
- mnesia:transaction(
- fun () ->
- Os = mnesia:select(
- access,
- ets:fun2ms(
- fun({access, {_, H}, _} = O) when H == Host ->
- O
- end)),
- lists:foreach(fun mnesia:delete_object/1, Os),
- lists:foreach(
- fun({access, Name, Rules}) ->
- mnesia:write({access, {Name, Host}, Rules})
- end, Rs)
- end)
- end,
- case xmpp_util:get_xdata_values(<<"access">>, XData) of
- [] ->
- Txt = <<"No 'access' found in data form">>,
- {error, xmpp:err_bad_request(Txt, Lang)};
- Strings ->
- String = lists:foldl(fun (S, Res) ->
- <<Res/binary, S/binary, "\n">>
- end, <<"">>, Strings),
- case erl_scan:string(binary_to_list(String)) of
- {ok, Tokens, _} ->
- case erl_parse:parse_term(Tokens) of
- {ok, Rs} ->
- case SetAccess(Rs) of
- {atomic, _} ->
- {result, undefined};
- _ ->
- {error, xmpp:err_bad_request()}
- end;
- _ ->
- Txt = <<"Parse failed">>,
- {error, xmpp:err_bad_request(Txt, Lang)}
- end;
- _ ->
- {error, xmpp:err_bad_request(<<"Scan failed">>, Lang)}
- end
- end;
set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang,
XData) ->
AccountString = get_value(<<"accountjid">>, XData),
@@ -1524,7 +1411,7 @@ set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang,
AccountJID = jid:decode(AccountString),
User = AccountJID#jid.luser,
Server = AccountJID#jid.lserver,
- true = lists:member(Server, ejabberd_config:get_myhosts()),
+ true = lists:member(Server, ejabberd_option:hosts()),
true = Server == Host orelse
get_permission_level(From) == global,
case ejabberd_auth:try_register(User, Server, Password) of
diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl
index 92d238c0d..e45ad293d 100644
--- a/src/mod_delegation.erl
+++ b/src/mod_delegation.erl
@@ -61,15 +61,24 @@ reload(_Host, _NewOpts, _OldOpts) ->
ok.
mod_opt_type(namespaces) ->
- fun(L) ->
- lists:map(
- fun({NS, Opts}) ->
- Attrs = proplists:get_value(filtering, Opts, []),
- Access = proplists:get_value(access, Opts, none),
- {NS, Attrs, Access}
- end, L)
- end.
-
+ econf:and_then(
+ econf:map(
+ econf:binary(),
+ econf:options(
+ #{filtering => econf:list(econf:binary()),
+ access => econf:acl()})),
+ fun(L) ->
+ lists:map(
+ fun({NS, Opts}) ->
+ Attrs = proplists:get_value(filtering, Opts, []),
+ Access = proplists:get_value(access, Opts, none),
+ {NS, Attrs, Access}
+ end, L)
+ end).
+
+-spec mod_options(binary()) -> [{namespaces,
+ [{binary(), [binary()], acl:acl()}]} |
+ {atom(), term()}].
mod_options(_Host) ->
[{namespaces, []}].
@@ -87,7 +96,7 @@ component_connected(Host) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_connected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec component_disconnected(binary(), binary()) -> ok.
component_disconnected(Host, _Reason) ->
@@ -95,7 +104,7 @@ component_disconnected(Host, _Reason) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_disconnected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec ejabberd_local(iq()) -> iq().
ejabberd_local(IQ) ->
@@ -149,8 +158,7 @@ handle_call(_Request, _From, State) ->
handle_cast({component_connected, Host}, State) ->
ServerHost = State#state.server_host,
To = jid:make(Host),
- NSAttrsAccessList = gen_mod:get_module_opt(
- ServerHost, ?MODULE, namespaces),
+ NSAttrsAccessList = mod_delegation_opt:namespaces(ServerHost),
lists:foreach(
fun({NS, _Attrs, Access}) ->
case acl:match_rule(ServerHost, Access, To) of
diff --git a/src/mod_delegation_opt.erl b/src/mod_delegation_opt.erl
new file mode 100644
index 000000000..90965007e
--- /dev/null
+++ b/src/mod_delegation_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_delegation_opt).
+
+-export([namespaces/1]).
+
+-spec namespaces(gen_mod:opts() | global | binary()) -> [{binary(),[binary()],acl:acl()}].
+namespaces(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(namespaces, Opts);
+namespaces(Host) ->
+ gen_mod:get_module_opt(Host, mod_delegation, namespaces).
+
diff --git a/src/mod_disco.erl b/src/mod_disco.erl
index f0d23a0ca..fd992d49d 100644
--- a/src/mod_disco.erl
+++ b/src/mod_disco.erl
@@ -37,8 +37,7 @@
get_local_features/5, get_local_services/5,
process_sm_iq_items/1, process_sm_iq_info/1,
get_sm_identity/5, get_sm_features/5, get_sm_items/5,
- get_info/5, transform_module_options/1, mod_opt_type/1,
- mod_options/1, depends/2]).
+ get_info/5, mod_opt_type/1, mod_options/1, depends/2]).
-include("logger.hrl").
-include("translate.hrl").
@@ -63,7 +62,7 @@ start(Host, Opts) ->
catch ets:new(disco_extra_domains,
[named_table, ordered_set, public,
{heir, erlang:group_leader(), none}]),
- ExtraDomains = gen_mod:get_opt(extra_domains, Opts),
+ ExtraDomains = mod_disco_opt:extra_domains(Opts),
lists:foreach(fun (Domain) ->
register_extra_domain(Host, Domain)
end,
@@ -112,19 +111,16 @@ stop(Host) ->
ok.
reload(Host, NewOpts, OldOpts) ->
- case gen_mod:is_equal_opt(extra_domains, NewOpts, OldOpts) of
- {false, NewDomains, OldDomains} ->
- lists:foreach(
- fun(Domain) ->
- register_extra_domain(Host, Domain)
- end, NewDomains -- OldDomains),
- lists:foreach(
- fun(Domain) ->
- unregister_extra_domain(Host, Domain)
- end, OldDomains -- NewDomains);
- true ->
- ok
- end.
+ NewDomains = mod_disco_opt:extra_domains(NewOpts),
+ OldDomains = mod_disco_opt:extra_domains(OldOpts),
+ lists:foreach(
+ fun(Domain) ->
+ register_extra_domain(Host, Domain)
+ end, NewDomains -- OldDomains),
+ lists:foreach(
+ fun(Domain) ->
+ unregister_extra_domain(Host, Domain)
+ end, OldDomains -- NewDomains).
-spec register_extra_domain(binary(), binary()) -> true.
register_extra_domain(Host, Domain) ->
@@ -177,7 +173,7 @@ process_local_iq_info(#iq{type = get, lang = Lang,
binary(), binary()) -> [identity()].
get_local_identity(Acc, _From, To, <<"">>, _Lang) ->
Host = To#jid.lserver,
- Name = gen_mod:get_module_opt(Host, ?MODULE, name),
+ Name = mod_disco_opt:name(Host),
Acc ++ [#identity{category = <<"server">>,
type = <<"im">>,
name = Name}];
@@ -238,7 +234,7 @@ get_vh_services(Host) ->
Hosts = lists:sort(fun (H1, H2) ->
byte_size(H1) >= byte_size(H2)
end,
- ejabberd_config:get_myhosts()),
+ ejabberd_option:hosts()),
lists:filter(fun (H) ->
case lists:dropwhile(fun (VH) ->
not
@@ -374,23 +370,6 @@ get_user_resources(User, Server) ->
[#disco_item{jid = jid:make(User, Server, Resource), name = User}
|| Resource <- lists:sort(Rs)].
--spec transform_module_options(gen_mod:opts()) -> gen_mod:opts().
-transform_module_options(Opts) ->
- lists:map(
- fun({server_info, Infos}) ->
- NewInfos = lists:map(
- fun({Modules, Name, URLs}) ->
- [[{modules, Modules},
- {name, Name},
- {urls, URLs}]];
- (Opt) ->
- Opt
- end, Infos),
- {server_info, NewInfos};
- (Opt) ->
- Opt
- end, Opts).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Support for: XEP-0157 Contact Addresses for XMPP Services
@@ -411,7 +390,7 @@ get_info(Acc, _, _, _Node, _) -> Acc.
-spec get_fields(binary(), module()) -> [xdata_field()].
get_fields(Host, Module) ->
- Fields = gen_mod:get_module_opt(Host, ?MODULE, server_info),
+ Fields = mod_disco_opt:server_info(Host),
Fields1 = lists:filter(fun ({Modules, _, _}) ->
case Modules of
all -> true;
@@ -429,19 +408,29 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(extra_domains) ->
- fun (Hs) -> [iolist_to_binary(H) || H <- Hs] end;
-mod_opt_type(name) -> fun iolist_to_binary/1;
+ econf:list(econf:binary());
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(server_info) ->
- fun (L) ->
- lists:map(fun (Opts) ->
- Mods = proplists:get_value(modules, Opts, all),
- Name = proplists:get_value(name, Opts, <<>>),
- URLs = proplists:get_value(urls, Opts, []),
- {Mods, Name, URLs}
- end,
- L)
- end.
-
+ econf:list(
+ econf:and_then(
+ econf:options(
+ #{name => econf:binary(),
+ urls => econf:list(econf:binary()),
+ modules =>
+ econf:either(
+ all,
+ econf:list(econf:beam()))}),
+ fun(Opts) ->
+ Mods = proplists:get_value(modules, Opts, all),
+ Name = proplists:get_value(name, Opts, <<>>),
+ URLs = proplists:get_value(urls, Opts, []),
+ {Mods, Name, URLs}
+ end)).
+
+-spec mod_options(binary()) -> [{server_info,
+ [{all | [module()], binary(), [binary()]}]} |
+ {atom(), any()}].
mod_options(_Host) ->
[{extra_domains, []},
{server_info, []},
diff --git a/src/mod_disco_opt.erl b/src/mod_disco_opt.erl
new file mode 100644
index 000000000..a66c3293a
--- /dev/null
+++ b/src/mod_disco_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_disco_opt).
+
+-export([extra_domains/1]).
+-export([name/1]).
+-export([server_info/1]).
+
+-spec extra_domains(gen_mod:opts() | global | binary()) -> [binary()].
+extra_domains(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(extra_domains, Opts);
+extra_domains(Host) ->
+ gen_mod:get_module_opt(Host, mod_disco, extra_domains).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_disco, name).
+
+-spec server_info(gen_mod:opts() | global | binary()) -> [{'all' | [module()],binary(),[binary()]}].
+server_info(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(server_info, Opts);
+server_info(Host) ->
+ gen_mod:get_module_opt(Host, mod_disco, server_info).
+
diff --git a/src/mod_echo.erl b/src/mod_echo.erl
index eaa630ac7..32b17ef39 100644
--- a/src/mod_echo.erl
+++ b/src/mod_echo.erl
@@ -60,11 +60,13 @@ reload(Host, NewOpts, OldOpts) ->
depends(_Host, _Opts) ->
[].
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1.
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE).
-mod_options(_Host) ->
- [{host, <<"echo.@HOST@">>}, {hosts, []}].
+mod_options(Host) ->
+ [{host, <<"echo.", Host/binary>>}, {hosts, []}].
%%====================================================================
%% gen_server callbacks
@@ -79,7 +81,7 @@ mod_options(_Host) ->
%%--------------------------------------------------------------------
init([Host, Opts]) ->
process_flag(trap_exit, true),
- Hosts = gen_mod:get_opt_hosts(Host, Opts),
+ Hosts = gen_mod:get_opt_hosts(Opts),
lists:foreach(
fun(H) ->
ejabberd_router:register_route(H, Host)
@@ -105,8 +107,8 @@ handle_call(stop, _From, State) ->
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast({reload, Host, NewOpts, OldOpts}, State) ->
- NewMyHosts = gen_mod:get_opt_hosts(Host, NewOpts),
- OldMyHosts = gen_mod:get_opt_hosts(Host, OldOpts),
+ NewMyHosts = gen_mod:get_opt_hosts(NewOpts),
+ OldMyHosts = gen_mod:get_opt_hosts(OldOpts),
lists:foreach(
fun(H) ->
ejabberd_router:unregister_route(H)
@@ -167,12 +169,12 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%% replace the argument 'disabled' with 'enabled' in the call to the
%% function do_client_version.
-%% ejabberd provides a method to receive XMPP packets using Erlang's
-%% message passing mechanism.
+%% ejabberd provides a method to receive XMPP packets using Erlang's
+%% message passing mechanism.
%%
%% The packets received by ejabberd are sent
%% to the local destination process by sending an Erlang message.
-%% This means that you can receive XMPP stanzas in an Erlang process
+%% This means that you can receive XMPP stanzas in an Erlang process
%% using Erlang's Receive, as long as this process is registered in
%% ejabberd as the process which handles the destination JID.
%%
diff --git a/src/mod_echo_opt.erl b/src/mod_echo_opt.erl
new file mode 100644
index 000000000..8c030e6ab
--- /dev/null
+++ b/src/mod_echo_opt.erl
@@ -0,0 +1,20 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_echo_opt).
+
+-export([host/1]).
+-export([hosts/1]).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_echo, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_echo, hosts).
+
diff --git a/src/mod_fail2ban.erl b/src/mod_fail2ban.erl
index 29247a0d1..6ee65ca3e 100644
--- a/src/mod_fail2ban.erl
+++ b/src/mod_fail2ban.erl
@@ -61,10 +61,8 @@ c2s_auth_result(#{ip := {Addr, _}, lserver := LServer} = State, {false, _}, _Use
true ->
State;
false ->
- BanLifetime = gen_mod:get_module_opt(
- LServer, ?MODULE, c2s_auth_ban_lifetime),
- MaxFailures = gen_mod:get_module_opt(
- LServer, ?MODULE, c2s_max_auth_failures),
+ BanLifetime = mod_fail2ban_opt:c2s_auth_ban_lifetime(LServer),
+ MaxFailures = mod_fail2ban_opt:c2s_max_auth_failures(LServer),
UnbanTS = erlang:system_time(second) + BanLifetime,
Attempts = case ets:lookup(failed_auth, Addr) of
[{Addr, N, _, _}] ->
@@ -186,28 +184,27 @@ get_commands_spec() ->
result_desc = "Amount of unbanned entries, or negative in case of error.",
result = {unbanned, integer}}].
--spec unban(string()) -> integer().
+-spec unban(binary()) -> integer().
unban(S) ->
- case acl:parse_ip_netmask(S) of
- {ok, Net, Mask} ->
+ case misc:parse_ip_mask(S) of
+ {ok, {Net, Mask}} ->
unban(Net, Mask);
error ->
?WARNING_MSG("Invalid network address when trying to unban: ~p", [S]),
-1
end.
+-spec unban(inet:ip_address(), 0..128) -> non_neg_integer().
unban(Net, Mask) ->
ets:foldl(
fun({Addr, _, _, _}, Acc) ->
- case acl:ip_matches_mask(Addr, Net, Mask) of
+ case misc:match_ip_mask(Addr, Net, Mask) of
true ->
ets:delete(failed_auth, Addr),
Acc+1;
false -> Acc
end
- end,
- 0,
- failed_auth).
+ end, 0, failed_auth).
%%%===================================================================
%%% Internal functions
@@ -228,7 +225,7 @@ log_and_disconnect(#{ip := {Addr, _}, lang := Lang} = State, Attempts, UnbanTS)
{stop, ejabberd_c2s:send(State, Err)}.
is_whitelisted(Host, Addr) ->
- Access = gen_mod:get_module_opt(Host, ?MODULE, access),
+ Access = mod_fail2ban_opt:access(Host),
acl:match_rule(Host, Access, Addr) == allow.
seconds_to_now(Secs) ->
@@ -239,11 +236,11 @@ format_date({{Year, Month, Day}, {Hour, Minute, Second}}) ->
[Hour, Minute, Second, Day, Month, Year]).
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(c2s_auth_ban_lifetime) ->
- fun (T) when is_integer(T), T > 0 -> T end;
+ econf:pos_int();
mod_opt_type(c2s_max_auth_failures) ->
- fun (I) when is_integer(I), I > 0 -> I end.
+ econf:pos_int().
mod_options(_Host) ->
[{access, none},
diff --git a/src/mod_fail2ban_opt.erl b/src/mod_fail2ban_opt.erl
new file mode 100644
index 000000000..15abbedd0
--- /dev/null
+++ b/src/mod_fail2ban_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_fail2ban_opt).
+
+-export([access/1]).
+-export([c2s_auth_ban_lifetime/1]).
+-export([c2s_max_auth_failures/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_fail2ban, access).
+
+-spec c2s_auth_ban_lifetime(gen_mod:opts() | global | binary()) -> pos_integer().
+c2s_auth_ban_lifetime(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(c2s_auth_ban_lifetime, Opts);
+c2s_auth_ban_lifetime(Host) ->
+ gen_mod:get_module_opt(Host, mod_fail2ban, c2s_auth_ban_lifetime).
+
+-spec c2s_max_auth_failures(gen_mod:opts() | global | binary()) -> pos_integer().
+c2s_max_auth_failures(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(c2s_max_auth_failures, Opts);
+c2s_max_auth_failures(Host) ->
+ gen_mod:get_module_opt(Host, mod_fail2ban, c2s_max_auth_failures).
+
diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl
index e3a738a07..4e2ecdcdd 100644
--- a/src/mod_http_api.erl
+++ b/src/mod_http_api.erl
@@ -74,7 +74,7 @@
-behaviour(gen_mod).
--export([start/2, stop/1, reload/3, process/2, mod_opt_type/1, depends/2,
+-export([start/2, stop/1, reload/3, process/2, depends/2,
mod_options/1]).
-include("xmpp.hrl").
@@ -119,16 +119,13 @@
%% -------------------
start(_Host, _Opts) ->
- ejabberd_access_permissions:register_permission_addon(?MODULE, fun permission_addon/0),
ok.
stop(_Host) ->
- ejabberd_access_permissions:unregister_permission_addon(?MODULE),
ok.
-reload(Host, NewOpts, _OldOpts) ->
- stop(Host),
- start(Host, NewOpts).
+reload(_Host, _NewOpts, _OldOpts) ->
+ ok.
depends(_Host, _Opts) ->
[].
@@ -170,10 +167,7 @@ extract_auth(#request{auth = HTTPAuth, ip = {IP, _}, opts = Opts}) ->
_ ->
?DEBUG("Invalid auth data: ~p", [Info]),
Info
- end;
-extract_auth(#request{ip = IP, opts = Opts}) ->
- Tag = proplists:get_value(tag, Opts, <<>>),
- #{ip => IP, caller_module => ?MODULE, tag => Tag}.
+ end.
%% ------------------
%% command processing
@@ -233,7 +227,6 @@ perform_call(Command, Args, Req, Version) ->
{error, expired} -> invalid_token_response();
{error, not_found} -> invalid_token_response();
{error, invalid_auth} -> unauthorized_response();
- {error, _} -> unauthorized_response();
Auth when is_map(Auth) ->
Result = handle(Call, Auth, Args, Version),
json_format(Result)
@@ -274,50 +267,40 @@ get_api_version([]) ->
% generic ejabberd command handler
handle(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
- case ejabberd_commands:get_command_format(Call, Auth, Version) of
- {ArgsSpec, _} when is_list(ArgsSpec) ->
- Args2 = [{misc:binary_to_atom(Key), Value} || {Key, Value} <- Args],
- try
- handle2(Call, Auth, Args2, Version)
- catch throw:not_found ->
- {404, <<"not_found">>};
- throw:{not_found, Why} when is_atom(Why) ->
- {404, misc:atom_to_binary(Why)};
- throw:{not_found, Msg} ->
- {404, iolist_to_binary(Msg)};
- throw:not_allowed ->
- {401, <<"not_allowed">>};
- throw:{not_allowed, Why} when is_atom(Why) ->
- {401, misc:atom_to_binary(Why)};
- throw:{not_allowed, Msg} ->
- {401, iolist_to_binary(Msg)};
- throw:{error, account_unprivileged} ->
- {403, 31, <<"Command need to be run with admin privilege.">>};
- throw:{error, access_rules_unauthorized} ->
- {403, 32, <<"AccessRules: Account does not have the right to perform the operation.">>};
- throw:{invalid_parameter, Msg} ->
- {400, iolist_to_binary(Msg)};
- throw:{error, Why} when is_atom(Why) ->
- {400, misc:atom_to_binary(Why)};
- throw:{error, Msg} ->
- {400, iolist_to_binary(Msg)};
- throw:Error when is_atom(Error) ->
- {400, misc:atom_to_binary(Error)};
- throw:Msg when is_list(Msg); is_binary(Msg) ->
- {400, iolist_to_binary(Msg)};
- ?EX_RULE(Class, Error, Stack) ->
- ?ERROR_MSG("REST API Error: "
- "~s(~p) -> ~p:~p ~p",
- [Call, hide_sensitive_args(Args),
- Class, Error, ?EX_STACK(Stack)]),
- {500, <<"internal_error">>}
- end;
- {error, Msg} ->
- ?ERROR_MSG("REST API Error: ~p", [Msg]),
- {400, Msg};
- _Error ->
- ?ERROR_MSG("REST API Error: ~p", [_Error]),
- {400, <<"Error">>}
+ Args2 = [{misc:binary_to_atom(Key), Value} || {Key, Value} <- Args],
+ try handle2(Call, Auth, Args2, Version)
+ catch throw:not_found ->
+ {404, <<"not_found">>};
+ throw:{not_found, Why} when is_atom(Why) ->
+ {404, misc:atom_to_binary(Why)};
+ throw:{not_found, Msg} ->
+ {404, iolist_to_binary(Msg)};
+ throw:not_allowed ->
+ {401, <<"not_allowed">>};
+ throw:{not_allowed, Why} when is_atom(Why) ->
+ {401, misc:atom_to_binary(Why)};
+ throw:{not_allowed, Msg} ->
+ {401, iolist_to_binary(Msg)};
+ throw:{error, account_unprivileged} ->
+ {403, 31, <<"Command need to be run with admin privilege.">>};
+ throw:{error, access_rules_unauthorized} ->
+ {403, 32, <<"AccessRules: Account does not have the right to perform the operation.">>};
+ throw:{invalid_parameter, Msg} ->
+ {400, iolist_to_binary(Msg)};
+ throw:{error, Why} when is_atom(Why) ->
+ {400, misc:atom_to_binary(Why)};
+ throw:{error, Msg} ->
+ {400, iolist_to_binary(Msg)};
+ throw:Error when is_atom(Error) ->
+ {400, misc:atom_to_binary(Error)};
+ throw:Msg when is_list(Msg); is_binary(Msg) ->
+ {400, iolist_to_binary(Msg)};
+ ?EX_RULE(Class, Error, Stack) ->
+ ?ERROR_MSG("REST API Error: "
+ "~s(~p) -> ~p:~p ~p",
+ [Call, hide_sensitive_args(Args),
+ Class, Error, ?EX_STACK(Stack)]),
+ {500, <<"internal_error">>}
end.
handle2(Call, Auth, Args, Version) when is_atom(Call), is_list(Args) ->
@@ -566,31 +549,5 @@ hide_sensitive_args(Args=[_H|_T]) ->
hide_sensitive_args(NonListArgs) ->
NonListArgs.
-permission_addon() ->
- Access = gen_mod:get_module_opt(global, ?MODULE, admin_ip_access),
- Rules = acl:resolve_access(Access, global),
- R = case Rules of
- all ->
- [{[{allow, all}], {all, []}}];
- none ->
- [];
- _ ->
- lists:filtermap(
- fun({V, AclRules}) when V == all; V == [all]; V == [allow]; V == allow ->
- {true, {[{allow, AclRules}], {all, []}}};
- ({List, AclRules}) when is_list(List) ->
- {true, {[{allow, AclRules}], {List, []}}};
- (_) ->
- false
- end, Rules)
- end,
- {_, Res} = lists:foldl(
- fun({R2, L2}, {Idx, Acc}) ->
- {Idx+1, [{<<"'mod_http_api admin_ip_access' option compatibility shim ",
- (integer_to_binary(Idx))/binary>>,
- {[?MODULE], [{access, R2}], L2}} | Acc]}
- end, {1, []}, R),
- Res.
-
-mod_opt_type(admin_ip_access) -> fun acl:access_rules_validator/1.
-mod_options(_) -> [{admin_ip_access, none}].
+mod_options(_) ->
+ [].
diff --git a/src/mod_http_api_opt.erl b/src/mod_http_api_opt.erl
new file mode 100644
index 000000000..3d928fc1b
--- /dev/null
+++ b/src/mod_http_api_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_http_api_opt).
+
+-export([admin_ip_access/1]).
+
+-spec admin_ip_access(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+admin_ip_access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(admin_ip_access, Opts);
+admin_ip_access(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_api, admin_ip_access).
+
diff --git a/src/mod_http_fileserver.erl b/src/mod_http_fileserver.erl
index d34d7193e..fbbd1eebe 100644
--- a/src/mod_http_fileserver.erl
+++ b/src/mod_http_fileserver.erl
@@ -121,20 +121,20 @@ init([Host, Opts]) ->
end.
initialize(Host, Opts) ->
- DocRoot = gen_mod:get_opt(docroot, Opts),
- AccessLog = gen_mod:get_opt(accesslog, Opts),
+ DocRoot = mod_http_fileserver_opt:docroot(Opts),
+ AccessLog = mod_http_fileserver_opt:accesslog(Opts),
AccessLogFD = try_open_log(AccessLog, Host),
- DirectoryIndices = gen_mod:get_opt(directory_indices, Opts),
- CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
- DefaultContentType = gen_mod:get_opt(default_content_type, Opts),
- UserAccess0 = gen_mod:get_opt(must_authenticate_with, Opts),
+ DirectoryIndices = mod_http_fileserver_opt:directory_indices(Opts),
+ CustomHeaders = mod_http_fileserver_opt:custom_headers(Opts),
+ DefaultContentType = mod_http_fileserver_opt:default_content_type(Opts),
+ UserAccess0 = mod_http_fileserver_opt:must_authenticate_with(Opts),
UserAccess = case UserAccess0 of
[] -> none;
_ ->
dict:from_list(UserAccess0)
end,
ContentTypes = build_list_content_types(
- gen_mod:get_opt(content_types, Opts),
+ mod_http_fileserver_opt:content_types(Opts),
?DEFAULT_CONTENT_TYPES),
?DEBUG("known content types: ~s",
[str:join([[$*, K, " -> ", V] || {K, V} <- ContentTypes],
@@ -320,9 +320,7 @@ serve(LocalPath, Auth, DocRoot, DirectoryIndices, CustomHeaders, DefaultContentT
DefaultContentType,
ContentTypes)
end
- end;
- _ ->
- ?HTTP_ERR_FORBIDDEN
+ end
end.
%% Troll through the directory indices attempting to find one which
@@ -382,7 +380,7 @@ reopen_log() ->
lists:foreach(
fun(Host) ->
gen_server:cast(get_proc_name(Host), reopen_log)
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
add_to_log(FileSize, Code, Request) ->
gen_server:cast(get_proc_name(Request#request.host),
@@ -464,43 +462,27 @@ ip_to_string(Address) when size(Address) == 8 ->
string:to_lower(lists:flatten(join(Parts, ":"))).
mod_opt_type(accesslog) ->
- fun(undefined) -> undefined;
- (File) -> iolist_to_binary(File)
- end;
+ econf:file(write);
mod_opt_type(content_types) ->
- fun(L) when is_list(L) ->
- lists:map(
- fun({K, V}) ->
- {iolist_to_binary(K),
- iolist_to_binary(V)}
- end, L)
- end;
+ econf:map(econf:binary(), econf:binary());
mod_opt_type(custom_headers) ->
- fun (L) when is_list(L) -> L end;
+ econf:map(econf:binary(), econf:binary());
mod_opt_type(default_content_type) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(directory_indices) ->
- fun (L) when is_list(L) -> L end;
+ econf:list(econf:binary());
mod_opt_type(docroot) ->
- fun(S) ->
- Path = iolist_to_binary(S),
- case filelib:ensure_dir(filename:join(Path, "foo")) of
- ok ->
- Path;
- {error, Why} ->
- ?ERROR_MSG("Failed to create directory ~s: ~s",
- [Path, file:format_error(Why)]),
- erlang:error(badarg)
- end
- end;
+ econf:directory(write);
mod_opt_type(must_authenticate_with) ->
- fun (L) when is_list(L) ->
- lists:map(fun(UP) when is_binary(UP) ->
- [K, V] = binary:split(UP, <<":">>),
- {K, V}
- end, L)
- end.
-
+ econf:list(
+ econf:and_then(
+ econf:and_then(
+ econf:binary("^[^:]+:[^:]+$"),
+ econf:binary_sep(":")),
+ fun([K, V]) -> {K, V} end)).
+
+-spec mod_options(binary()) -> [{must_authenticate_with, [{binary(), binary()}]} |
+ {atom(), any()}].
mod_options(_) ->
[{accesslog, undefined},
{content_types, []},
diff --git a/src/mod_http_fileserver_opt.erl b/src/mod_http_fileserver_opt.erl
new file mode 100644
index 000000000..442ce1d90
--- /dev/null
+++ b/src/mod_http_fileserver_opt.erl
@@ -0,0 +1,55 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_http_fileserver_opt).
+
+-export([accesslog/1]).
+-export([content_types/1]).
+-export([custom_headers/1]).
+-export([default_content_type/1]).
+-export([directory_indices/1]).
+-export([docroot/1]).
+-export([must_authenticate_with/1]).
+
+-spec accesslog(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
+accesslog(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(accesslog, Opts);
+accesslog(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, accesslog).
+
+-spec content_types(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+content_types(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(content_types, Opts);
+content_types(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, content_types).
+
+-spec custom_headers(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+custom_headers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(custom_headers, Opts);
+custom_headers(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, custom_headers).
+
+-spec default_content_type(gen_mod:opts() | global | binary()) -> binary().
+default_content_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(default_content_type, Opts);
+default_content_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, default_content_type).
+
+-spec directory_indices(gen_mod:opts() | global | binary()) -> [binary()].
+directory_indices(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(directory_indices, Opts);
+directory_indices(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, directory_indices).
+
+-spec docroot(gen_mod:opts() | global | binary()) -> binary().
+docroot(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(docroot, Opts);
+docroot(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, docroot).
+
+-spec must_authenticate_with(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+must_authenticate_with(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(must_authenticate_with, Opts);
+must_authenticate_with(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_fileserver, must_authenticate_with).
+
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 364b3a019..dc02bb11d 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -25,7 +25,8 @@
-module(mod_http_upload).
-author('holger@zedat.fu-berlin.de').
-
+-behaviour(gen_server).
+-behaviour(gen_mod).
-protocol({xep, 363, '0.1'}).
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
@@ -57,9 +58,6 @@
{<<".xz">>, <<"application/x-xz">>},
{<<".zip">>, <<"application/zip">>}]).
--behaviour(gen_server).
--behaviour(gen_mod).
-
%% gen_mod/supervisor callbacks.
-export([start/2,
stop/1,
@@ -125,7 +123,7 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> {ok, pid()} | {error, already_started}.
start(ServerHost, Opts) ->
- case gen_mod:get_opt(rm_on_unregister, Opts) of
+ case mod_http_upload_opt:rm_on_unregister(Opts) of
true ->
ejabberd_hooks:add(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -144,7 +142,7 @@ start(ServerHost, Opts) ->
-spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
- case gen_mod:get_module_opt(ServerHost, ?MODULE, rm_on_unregister) of
+ case mod_http_upload_opt:rm_on_unregister(ServerHost) of
true ->
ejabberd_hooks:delete(remove_user, ServerHost, ?MODULE,
remove_user, 50);
@@ -154,76 +152,62 @@ stop(ServerHost) ->
Proc = get_proc_name(ServerHost, ?MODULE),
gen_mod:stop_child(Proc).
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
-mod_opt_type(host) ->
- fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) ->
- fun ejabberd_config:v_hosts/1;
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(name) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(max_size) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
mod_opt_type(secret_length) ->
- fun(I) when is_integer(I), I >= 8 -> I end;
+ econf:int(8, 1000);
mod_opt_type(jid_in_url) ->
- fun(sha1) -> sha1;
- (node) -> node
- end;
+ econf:enum([sha1, node]);
mod_opt_type(file_mode) ->
- fun(undefined) -> undefined;
- (Mode) -> binary_to_integer(iolist_to_binary(Mode), 8)
- end;
+ econf:octal();
mod_opt_type(dir_mode) ->
- fun(undefined) -> undefined;
- (Mode) -> binary_to_integer(iolist_to_binary(Mode), 8)
- end;
+ econf:octal();
mod_opt_type(docroot) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(put_url) ->
- fun misc:try_url/1;
+ econf:url();
mod_opt_type(get_url) ->
- fun(undefined) -> undefined;
- (URL) -> misc:try_url(URL)
- end;
+ econf:url();
mod_opt_type(service_url) ->
- fun(undefined) -> undefined;
- (URL) ->
- ?WARNING_MSG("option 'service_url' is deprecated, consider unsing "
- "the 'external_secret' interface instead", []),
- misc:try_url(URL)
- end;
+ econf:and_then(
+ econf:url(),
+ fun(URL) ->
+ ?WARNING_MSG("Option 'service_url' is deprecated, consider using "
+ "the 'external_secret' interface instead", []),
+ URL
+ end);
mod_opt_type(custom_headers) ->
- fun(Headers) ->
- lists:map(fun({K, V}) ->
- {iolist_to_binary(K), iolist_to_binary(V)}
- end, Headers)
- end;
+ econf:map(econf:binary(), econf:binary());
mod_opt_type(rm_on_unregister) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(thumbnail) ->
- fun(true) ->
- case eimp:supported_formats() of
- [] ->
- ?WARNING_MSG("ejabberd is built without image converter "
- "support, option '~s' is ignored",
- [thumbnail]),
- erlang:error(badarg);
- _ ->
- true
- end;
- (false) ->
- false
- end;
+ econf:and_then(
+ econf:bool(),
+ fun(true) ->
+ case eimp:supported_formats() of
+ [] -> econf:fail(eimp_error);
+ [_|_] -> true
+ end;
+ (false) ->
+ false
+ end);
mod_opt_type(external_secret) ->
- fun iolist_to_binary/1.
+ econf:binary();
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE).
--spec mod_options(binary()) -> [{atom(), any()}].
-mod_options(_Host) ->
- [{host, <<"upload.@HOST@">>},
+-spec mod_options(binary()) -> [{service_url, binary()} |
+ {thumbnail, boolean()} |
+ {atom(), any()}].
+mod_options(Host) ->
+ [{host, <<"upload.", Host/binary>>},
{hosts, []},
{name, ?T("HTTP File Upload")},
{access, local},
@@ -233,7 +217,7 @@ mod_options(_Host) ->
{file_mode, undefined},
{dir_mode, undefined},
{docroot, <<"@HOME@/upload">>},
- {put_url, <<"https://@HOST@:5443/upload">>},
+ {put_url, <<"https://", Host/binary, ":5443/upload">>},
{get_url, undefined},
{service_url, undefined},
{external_secret, <<"">>},
@@ -251,24 +235,24 @@ depends(_Host, _Opts) ->
-spec init(list()) -> {ok, state()}.
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
- Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
- Name = gen_mod:get_opt(name, Opts),
- Access = gen_mod:get_opt(access, Opts),
- MaxSize = gen_mod:get_opt(max_size, Opts),
- SecretLength = gen_mod:get_opt(secret_length, Opts),
- JIDinURL = gen_mod:get_opt(jid_in_url, Opts),
- DocRoot = gen_mod:get_opt(docroot, Opts),
- FileMode = gen_mod:get_opt(file_mode, Opts),
- DirMode = gen_mod:get_opt(dir_mode, Opts),
- PutURL = gen_mod:get_opt(put_url, Opts),
- GetURL = case gen_mod:get_opt(get_url, Opts) of
+ Hosts = gen_mod:get_opt_hosts(Opts),
+ Name = mod_http_upload_opt:name(Opts),
+ Access = mod_http_upload_opt:access(Opts),
+ MaxSize = mod_http_upload_opt:max_size(Opts),
+ SecretLength = mod_http_upload_opt:secret_length(Opts),
+ JIDinURL = mod_http_upload_opt:jid_in_url(Opts),
+ DocRoot = mod_http_upload_opt:docroot(Opts),
+ FileMode = mod_http_upload_opt:file_mode(Opts),
+ DirMode = mod_http_upload_opt:dir_mode(Opts),
+ PutURL = mod_http_upload_opt:put_url(Opts),
+ GetURL = case mod_http_upload_opt:get_url(Opts) of
undefined -> PutURL;
URL -> URL
end,
- ServiceURL = gen_mod:get_opt(service_url, Opts),
- Thumbnail = gen_mod:get_opt(thumbnail, Opts),
- ExternalSecret = gen_mod:get_opt(external_secret, Opts),
- CustomHeaders = gen_mod:get_opt(custom_headers, Opts),
+ ServiceURL = mod_http_upload_opt:service_url(Opts),
+ Thumbnail = mod_http_upload_opt:thumbnail(Opts),
+ ExternalSecret = mod_http_upload_opt:external_secret(Opts),
+ CustomHeaders = mod_http_upload_opt:custom_headers(Opts),
DocRoot1 = expand_home(str:strip(DocRoot, right, $/)),
DocRoot2 = expand_host(DocRoot1, ServerHost),
case DirMode of
@@ -507,7 +491,7 @@ process(_LocalPath, #request{method = Method, host = Host, ip = IP}) ->
%%--------------------------------------------------------------------
-spec get_proc_name(binary(), atom()) -> atom().
get_proc_name(ServerHost, ModuleName) ->
- PutURL = gen_mod:get_module_opt(ServerHost, ?MODULE, put_url),
+ PutURL = mod_http_upload_opt:put_url(ServerHost),
%% Once we depend on OTP >= 20.0, we can use binaries with http_uri.
{ok, {_Scheme, _UserInfo, Host0, _Port, Path0, _Query}} =
http_uri:parse(binary_to_list(expand_host(PutURL, ServerHost))),
@@ -741,7 +725,7 @@ encode_addr(IP) ->
-spec iq_disco_info(binary(), binary(), binary(), [xdata()]) -> disco_info().
iq_disco_info(Host, Lang, Name, AddInfo) ->
- Form = case gen_mod:get_module_opt(Host, ?MODULE, max_size) of
+ Form = case mod_http_upload_opt:max_size(Host) of
infinity ->
AddInfo;
MaxSize ->
@@ -853,9 +837,9 @@ http_response(Code, ExtraHeaders) ->
Message = <<(code_to_message(Code))/binary, $\n>>,
http_response(Code, ExtraHeaders, Message).
--type http_body() :: binary() | {file, file:filename()}.
+-type http_body() :: binary() | {file, file:filename_all()}.
-spec http_response(100..599, [{binary(), binary()}], http_body())
- -> {pos_integer(), [{binary(), binary()}], binary()}.
+ -> {pos_integer(), [{binary(), binary()}], http_body()}.
http_response(Code, ExtraHeaders, Body) ->
Headers = case proplists:is_defined(<<"Content-Type">>, ExtraHeaders) of
true ->
@@ -914,13 +898,13 @@ read_image(Path) ->
pass
end.
--spec convert(binary(), media_info()) -> {ok, binary(), media_info()} | pass.
+-spec convert(binary(), media_info()) -> {ok, media_info()} | pass.
convert(InData, #media_info{path = Path, type = T, width = W, height = H} = Info) ->
if W * H >= 25000000 ->
?DEBUG("The image ~s is more than 25 Mpix", [Path]),
pass;
W =< 300, H =< 300 ->
- {ok, Path, Info};
+ {ok, Info};
true ->
Dir = filename:dirname(Path),
Ext = atom_to_binary(T, latin1),
@@ -961,8 +945,8 @@ thumb_el(#media_info{type = T, height = H, width = W}, URI) ->
-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
ServerHost = jid:nameprep(Server),
- DocRoot = gen_mod:get_module_opt(ServerHost, ?MODULE, docroot),
- JIDinURL = gen_mod:get_module_opt(ServerHost, ?MODULE, jid_in_url),
+ DocRoot = mod_http_upload_opt:docroot(ServerHost),
+ JIDinURL = mod_http_upload_opt:jid_in_url(ServerHost),
DocRoot1 = expand_host(expand_home(DocRoot), ServerHost),
UserStr = make_user_string(jid:make(User, Server), JIDinURL),
UserDir = str:join([DocRoot1, UserStr], <<$/>>),
diff --git a/src/mod_http_upload_opt.erl b/src/mod_http_upload_opt.erl
new file mode 100644
index 000000000..a985c229e
--- /dev/null
+++ b/src/mod_http_upload_opt.erl
@@ -0,0 +1,125 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_http_upload_opt).
+
+-export([access/1]).
+-export([custom_headers/1]).
+-export([dir_mode/1]).
+-export([docroot/1]).
+-export([external_secret/1]).
+-export([file_mode/1]).
+-export([get_url/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([jid_in_url/1]).
+-export([max_size/1]).
+-export([name/1]).
+-export([put_url/1]).
+-export([rm_on_unregister/1]).
+-export([secret_length/1]).
+-export([service_url/1]).
+-export([thumbnail/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'local' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, access).
+
+-spec custom_headers(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+custom_headers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(custom_headers, Opts);
+custom_headers(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, custom_headers).
+
+-spec dir_mode(gen_mod:opts() | global | binary()) -> 'undefined' | non_neg_integer().
+dir_mode(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(dir_mode, Opts);
+dir_mode(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, dir_mode).
+
+-spec docroot(gen_mod:opts() | global | binary()) -> binary().
+docroot(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(docroot, Opts);
+docroot(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, docroot).
+
+-spec external_secret(gen_mod:opts() | global | binary()) -> binary().
+external_secret(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(external_secret, Opts);
+external_secret(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, external_secret).
+
+-spec file_mode(gen_mod:opts() | global | binary()) -> 'undefined' | non_neg_integer().
+file_mode(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(file_mode, Opts);
+file_mode(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, file_mode).
+
+-spec get_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
+get_url(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(get_url, Opts);
+get_url(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, get_url).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, hosts).
+
+-spec jid_in_url(gen_mod:opts() | global | binary()) -> 'node' | 'sha1'.
+jid_in_url(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(jid_in_url, Opts);
+jid_in_url(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, jid_in_url).
+
+-spec max_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_size, Opts);
+max_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, max_size).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, name).
+
+-spec put_url(gen_mod:opts() | global | binary()) -> binary().
+put_url(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(put_url, Opts);
+put_url(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, put_url).
+
+-spec rm_on_unregister(gen_mod:opts() | global | binary()) -> boolean().
+rm_on_unregister(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(rm_on_unregister, Opts);
+rm_on_unregister(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, rm_on_unregister).
+
+-spec secret_length(gen_mod:opts() | global | binary()) -> 1..1114111.
+secret_length(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(secret_length, Opts);
+secret_length(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, secret_length).
+
+-spec service_url(gen_mod:opts() | global | binary()) -> binary().
+service_url(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(service_url, Opts);
+service_url(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, service_url).
+
+-spec thumbnail(gen_mod:opts() | global | binary()) -> boolean().
+thumbnail(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(thumbnail, Opts);
+thumbnail(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload, thumbnail).
+
diff --git a/src/mod_http_upload_quota.erl b/src/mod_http_upload_quota.erl
index 10f7831bd..4f0d49c0b 100644
--- a/src/mod_http_upload_quota.erl
+++ b/src/mod_http_upload_quota.erl
@@ -69,25 +69,21 @@
%%--------------------------------------------------------------------
%% gen_mod/supervisor callbacks.
%%--------------------------------------------------------------------
--spec start(binary(), gen_mod:opts()) -> {ok, pid()}.
start(ServerHost, Opts) ->
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
gen_mod:start_child(?MODULE, ServerHost, Opts, Proc).
--spec stop(binary()) -> ok | {error, any()}.
stop(ServerHost) ->
Proc = mod_http_upload:get_proc_name(ServerHost, ?MODULE),
gen_mod:stop_child(Proc).
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(access_soft_quota) ->
- fun acl:shaper_rules_validator/1;
+ econf:shaper();
mod_opt_type(access_hard_quota) ->
- fun acl:shaper_rules_validator/1;
+ econf:shaper();
mod_opt_type(max_days) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end.
+ econf:pos_int(infinity).
-spec mod_options(binary()) -> [{atom(), any()}].
mod_options(_) ->
@@ -105,10 +101,10 @@ depends(_Host, _Opts) ->
-spec init(list()) -> {ok, state()}.
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
- AccessSoftQuota = gen_mod:get_opt(access_soft_quota, Opts),
- AccessHardQuota = gen_mod:get_opt(access_hard_quota, Opts),
- MaxDays = gen_mod:get_opt(max_days, Opts),
- DocRoot1 = gen_mod:get_module_opt(ServerHost, mod_http_upload, docroot),
+ AccessSoftQuota = mod_http_upload_quota_opt:access_soft_quota(Opts),
+ AccessHardQuota = mod_http_upload_quota_opt:access_hard_quota(Opts),
+ MaxDays = mod_http_upload_quota_opt:max_days(Opts),
+ DocRoot1 = mod_http_upload_opt:docroot(ServerHost),
DocRoot2 = mod_http_upload:expand_home(str:strip(DocRoot1, right, $/)),
DocRoot3 = mod_http_upload:expand_host(DocRoot2, ServerHost),
Timers = if MaxDays == infinity -> [];
@@ -137,13 +133,13 @@ handle_cast({handle_slot_request, #jid{user = U, server = S} = JID, Path, Size},
access_soft_quota = AccessSoftQuota,
access_hard_quota = AccessHardQuota,
disk_usage = DiskUsage} = State) ->
- HardQuota = case acl:match_rule(ServerHost, AccessHardQuota, JID) of
+ HardQuota = case ejabberd_shaper:match(ServerHost, AccessHardQuota, JID) of
Hard when is_integer(Hard), Hard > 0 ->
Hard * 1024 * 1024;
_ ->
0
end,
- SoftQuota = case acl:match_rule(ServerHost, AccessSoftQuota, JID) of
+ SoftQuota = case ejabberd_shaper:match(ServerHost, AccessSoftQuota, JID) of
Soft when is_integer(Soft), Soft > 0 ->
Soft * 1024 * 1024;
_ ->
diff --git a/src/mod_http_upload_quota_opt.erl b/src/mod_http_upload_quota_opt.erl
new file mode 100644
index 000000000..acf739fab
--- /dev/null
+++ b/src/mod_http_upload_quota_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_http_upload_quota_opt).
+
+-export([access_hard_quota/1]).
+-export([access_soft_quota/1]).
+-export([max_days/1]).
+
+-spec access_hard_quota(gen_mod:opts() | global | binary()) -> atom() | [ejabberd_shaper:shaper_rule()].
+access_hard_quota(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_hard_quota, Opts);
+access_hard_quota(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload_quota, access_hard_quota).
+
+-spec access_soft_quota(gen_mod:opts() | global | binary()) -> atom() | [ejabberd_shaper:shaper_rule()].
+access_soft_quota(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_soft_quota, Opts);
+access_soft_quota(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload_quota, access_soft_quota).
+
+-spec max_days(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_days(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_days, Opts);
+max_days(Host) ->
+ gen_mod:get_module_opt(Host, mod_http_upload_quota, max_days).
+
diff --git a/src/mod_last.erl b/src/mod_last.erl
index 1cb747060..17db7fabb 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -58,7 +58,7 @@
-optional_callbacks([use_cache/1, cache_nodes/1]).
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
@@ -89,8 +89,8 @@ stop(Host) ->
?NS_LAST).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -113,12 +113,8 @@ process_local_iq(#iq{type = get} = IQ) ->
%% @doc Get the uptime of the ejabberd node, expressed in seconds.
%% When ejabberd is starting, ejabberd_config:start/0 stores the datetime.
get_node_uptime() ->
- case ejabberd_config:get_option(node_start) of
- undefined ->
- trunc(element(1, erlang:statistics(wall_clock)) / 1000);
- Now ->
- erlang:system_time(second) - Now
- end.
+ NodeStart = ejabberd_config:get_node_start(),
+ erlang:monotonic_time(second) - NodeStart.
%%%
%%% Serve queries about user last online
@@ -187,9 +183,7 @@ get_last(LUser, LServer) ->
?LAST_CACHE, {LUser, LServer},
fun() -> Mod:get_last(LUser, LServer) end);
false ->
- Mod:get_last(LUser, LServer);
- undefined ->
- error
+ Mod:get_last(LUser, LServer)
end,
case Res of
{ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status};
@@ -274,9 +268,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_last_opt:cache_size(Opts),
+ CacheMissed = mod_last_opt:cache_missed(Opts),
+ LifeTime = case mod_last_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -286,7 +280,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_last_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -321,17 +315,20 @@ export(LServer) ->
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_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_last_mnesia.erl b/src/mod_last_mnesia.erl
index d8d5296f3..7e4411443 100644
--- a/src/mod_last_mnesia.erl
+++ b/src/mod_last_mnesia.erl
@@ -45,7 +45,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(last_activity, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_last, use_cache);
+ mod_last_opt:use_cache(Host);
_ ->
false
end.
@@ -71,7 +71,7 @@ remove_user(LUser, LServer) ->
import(_LServer, #last_activity{} = LA) ->
mnesia:dirty_write(LA).
-need_transform(#last_activity{us = {U, S}, status = Status})
+need_transform({last_activity, {U, S}, _, Status})
when is_list(U) orelse is_list(S) orelse is_list(Status) ->
?INFO_MSG("Mnesia table 'last_activity' will be converted to binary", []),
true;
diff --git a/src/mod_last_opt.erl b/src/mod_last_opt.erl
new file mode 100644
index 000000000..470ffce5e
--- /dev/null
+++ b/src/mod_last_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_last_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_last, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_last, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_last, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_last, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_last, use_cache).
+
diff --git a/src/mod_last_sql.erl b/src/mod_last_sql.erl
index 85f3e3895..46079717c 100644
--- a/src/mod_last_sql.erl
+++ b/src/mod_last_sql.erl
@@ -26,7 +26,6 @@
-behaviour(mod_last).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, get_last/2, store_last_info/4, remove_user/2,
diff --git a/src/mod_legacy_auth.erl b/src/mod_legacy_auth.erl
index 9848f5457..064d7eb3e 100644
--- a/src/mod_legacy_auth.erl
+++ b/src/mod_legacy_auth.erl
@@ -119,9 +119,8 @@ authenticate(#{stream_id := StreamID, server := Server,
DGen = fun (PW) -> str:sha(<<StreamID/binary, PW/binary>>) end,
JID = jid:make(U, Server, R),
case JID /= error andalso
- acl:access_matches(Access,
- #{usr => jid:split(JID), ip => IP},
- JID#jid.lserver) == allow of
+ acl:match_rule(JID#jid.lserver, Access,
+ #{usr => jid:split(JID), ip => IP}) == allow of
true ->
case ejabberd_auth:check_password_with_authmodule(
U, U, JID#jid.lserver, P, D, DGen) of
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index f050128cd..ba8a9bcfc 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -92,7 +92,7 @@
%%% API
%%%===================================================================
start(Host, Opts) ->
- case gen_mod:get_opt(db_type, Opts) of
+ case mod_mam_opt:db_type(Opts) of
mnesia ->
?WARNING_MSG("Mnesia backend for ~s is not recommended: "
"it's limited to 2GB and often gets corrupted "
@@ -103,7 +103,7 @@ start(Host, Opts) ->
_ ->
ok
end,
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
case Mod:init(Host, Opts) of
ok ->
init_cache(Mod, Host, Opts),
@@ -132,14 +132,14 @@ start(Host, Opts) ->
set_room_option, 50),
ejabberd_hooks:add(store_mam_message, Host, ?MODULE,
store_mam_message, 100),
- case gen_mod:get_opt(assume_mam_usage, Opts) of
+ case mod_mam_opt:assume_mam_usage(Opts) of
true ->
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
false ->
ok
end,
- case gen_mod:get_opt(clear_archive_on_room_destroy, Opts) of
+ case mod_mam_opt:clear_archive_on_room_destroy(Opts) of
true ->
ejabberd_hooks:add(remove_room, Host, ?MODULE,
remove_room, 50);
@@ -156,7 +156,7 @@ start(Host, Opts) ->
use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 2) of
true -> Mod:use_cache(Host);
- false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
+ false -> mod_mam_opt:use_cache(Host)
end.
cache_nodes(Mod, Host) ->
@@ -174,9 +174,9 @@ init_cache(Mod, Host, Opts) ->
end.
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
+ MaxSize = mod_mam_opt:cache_size(Opts),
+ CacheMissed = mod_mam_opt:cache_missed(Opts),
+ LifeTime = case mod_mam_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -208,14 +208,14 @@ stop(Host) ->
set_room_option, 50),
ejabberd_hooks:delete(store_mam_message, Host, ?MODULE,
store_mam_message, 100),
- case gen_mod:get_module_opt(Host, ?MODULE, assume_mam_usage) of
+ case mod_mam_opt:assume_mam_usage(Host) of
true ->
ejabberd_hooks:delete(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
false ->
ok
end,
- case gen_mod:get_module_opt(Host, ?MODULE, clear_archive_on_room_destroy) of
+ case mod_mam_opt:clear_archive_on_room_destroy(Host) of
true ->
ejabberd_hooks:delete(remove_room, Host, ?MODULE,
remove_room, 50);
@@ -231,22 +231,23 @@ stop(Host) ->
end.
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
ok
end,
init_cache(NewMod, Host, NewOpts),
- case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
- {false, true, _} ->
+ case {mod_mam_opt:assume_mam_usage(NewOpts),
+ mod_mam_opt:assume_mam_usage(OldOpts)} of
+ {true, false} ->
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
- {false, false, _} ->
+ {false, true} ->
ejabberd_hooks:delete(message_is_archived, Host, ?MODULE,
message_is_archived, 50);
- true ->
+ _ ->
ok
end.
@@ -447,7 +448,7 @@ muc_filter_message(#message{from = From} = Pkt,
muc_filter_message(Acc, _MUCState, _FromNick) ->
Acc.
--spec make_id() -> binary().
+-spec make_id() -> integer().
make_id() ->
erlang:system_time(microsecond).
@@ -465,7 +466,7 @@ init_stanza_id(Pkt, LServer) ->
Pkt1 = strip_my_stanza_id(Pkt, LServer),
xmpp:put_meta(Pkt1, stanza_id, ID).
--spec set_stanza_id(stanza(), jid(), integer()) -> stanza().
+-spec set_stanza_id(stanza(), jid(), binary()) -> stanza().
set_stanza_id(Pkt, JID, ID) ->
BareJID = jid:remove_resource(JID),
Archived = #mam_archived{by = BareJID, id = ID},
@@ -555,7 +556,7 @@ disco_sm_features(Acc, _From, _To, _Node, _Lang) ->
message_is_archived(true, _C2SState, _Pkt) ->
true;
message_is_archived(false, #{lserver := LServer}, Pkt) ->
- case gen_mod:get_module_opt(LServer, ?MODULE, assume_mam_usage) of
+ case mod_mam_opt:assume_mam_usage(LServer) of
true ->
is_archived(Pkt, LServer);
false ->
@@ -572,11 +573,11 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
DBTypes = lists:usort(
lists:map(
fun(Host) ->
- case gen_mod:get_module_opt(Host, ?MODULE, db_type) of
+ case mod_mam_opt:db_type(Host) of
sql -> {sql, Host};
Other -> {Other, global}
end
- end, ejabberd_config:get_myhosts())),
+ end, ejabberd_option:hosts())),
Results = lists:map(
fun({DBType, ServerHost}) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
@@ -640,7 +641,7 @@ process_iq(#iq{from = #jid{luser = LUser, lserver = LServer},
default = Default,
always = Always0,
never = Never0}]} = IQ) ->
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access_preferences),
+ Access = mod_mam_opt:access_preferences(LServer),
case acl:match_rule(LServer, Access, jid:make(LUser, LServer)) of
allow ->
Always = lists:usort(get_jids(Always0)),
@@ -895,7 +896,7 @@ may_enter_room(From, MUCState) ->
store_msg(Pkt, LUser, LServer, Peer, Dir) ->
case get_prefs(LUser, LServer) of
{ok, Prefs} ->
- UseMucArchive = gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive),
+ UseMucArchive = mod_mam_opt:user_mucsub_from_muc_archive(LServer),
StoredInMucMam = UseMucArchive andalso xmpp:get_meta(Pkt, in_muc_mam, false),
case {should_archive_peer(LUser, LServer, Prefs, Peer), Pkt, StoredInMucMam} of
{true, #message{meta = #{sm_copy := true}}, _} ->
@@ -974,15 +975,12 @@ get_prefs(LUser, LServer) ->
{error, _} ->
{error, db_failure};
error ->
- ActivateOpt = gen_mod:get_module_opt(
- LServer, ?MODULE,
- request_activates_archiving),
+ ActivateOpt = mod_mam_opt:request_activates_archiving(LServer),
case ActivateOpt of
true ->
{ok, #archive_prefs{us = {LUser, LServer}, default = never}};
false ->
- Default = gen_mod:get_module_opt(
- LServer, ?MODULE, default),
+ Default = mod_mam_opt:default(LServer),
{ok, #archive_prefs{us = {LUser, LServer}, default = Default}}
end
end.
@@ -994,8 +992,7 @@ prefs_el(Default, Always, Never, NS) ->
xmlns = NS}.
maybe_activate_mam(LUser, LServer) ->
- ActivateOpt = gen_mod:get_module_opt(
- LServer, ?MODULE, request_activates_archiving),
+ ActivateOpt = mod_mam_opt:request_activates_archiving(LServer),
case ActivateOpt of
true ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
@@ -1015,8 +1012,7 @@ maybe_activate_mam(LUser, LServer) ->
{error, _} ->
{error, db_failure};
error ->
- Default = gen_mod:get_module_opt(
- LServer, ?MODULE, default),
+ Default = mod_mam_opt:default(LServer),
write_prefs(LUser, LServer, LServer, Default, [], [])
end;
false ->
@@ -1089,7 +1085,7 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType, Flags) ->
true ->
{[], true, 0};
false ->
- case {MsgType, gen_mod:get_module_opt(LServer, ?MODULE, user_mucsub_from_muc_archive)} of
+ case {MsgType, mod_mam_opt:user_mucsub_from_muc_archive(LServer)} of
{chat, true} ->
select_with_mucsub(LServer, JidRequestor, JidArchive, Query, RSM, Flags);
_ ->
@@ -1187,7 +1183,7 @@ wrap_as_mucsub(Message, Requester, ReqServer) ->
case Message of
#forwarded{delay = #delay{stamp = Stamp, desc = Desc},
sub_els = [#message{from = From, sub_els = SubEls, subject = Subject} = Msg]} ->
- {L1, SubEls2} = case lists:keytake(mam_archived, 1, xmpp:decode(SubEls)) of
+ {L1, SubEls2} = case lists:keytake(mam_archived, 1, SubEls) of
{value, Arch, Rest} ->
{[Arch#mam_archived{by = Requester}], Rest};
_ ->
@@ -1225,7 +1221,8 @@ wrap_as_mucsub(Message, Requester, ReqServer) ->
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
peer = Peer, id = ID},
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
- CodecOpts = ejabberd_config:codec_options(LServer),
+ CodecOpts = ejabberd_config:codec_options(
+ ejabberd_router:host_of_route(LServer)),
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
Pkt1 ->
Pkt2 = case MsgType of
@@ -1392,28 +1389,30 @@ get_commands_spec() ->
result_example = {ok, <<"MAM archive removed">>}}
].
+mod_opt_type(compress_xml) ->
+ econf:bool();
mod_opt_type(assume_mam_usage) ->
- fun (B) when is_boolean(B) -> B 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; 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;
+ econf:bool();
mod_opt_type(default) ->
- fun (always) -> always;
- (never) -> never;
- (roster) -> roster
- end;
+ econf:enum([always, never, roster]);
mod_opt_type(request_activates_archiving) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(clear_archive_on_room_destroy) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(user_mucsub_from_muc_archive) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(access_preferences) ->
- fun acl:access_rules_validator/1.
+ econf:acl();
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
[{assume_mam_usage, false},
@@ -1424,7 +1423,7 @@ mod_options(Host) ->
{access_preferences, all},
{user_mucsub_from_muc_archive, false},
{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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_mam_mnesia.erl b/src/mod_mam_mnesia.erl
index dff10ef96..b964c977c 100644
--- a/src/mod_mam_mnesia.erl
+++ b/src/mod_mam_mnesia.erl
@@ -85,7 +85,12 @@ remove_from_archive(LUser, LServer, WithJid) ->
US = {LUser, LServer},
Peer = jid:remove_resource(jid:split(WithJid)),
F = fun () ->
- Msgs = mnesia:match_object(#archive_msg{us = US, bare_peer = Peer, _ = '_'}),
+ Msgs = mnesia:select(
+ archive_msg,
+ ets:fun2ms(
+ fun(#archive_msg{us = US1, bare_peer = Peer1} = Msg)
+ when US1 == US, Peer1 == Peer -> Msg
+ end)),
lists:foreach(fun mnesia:delete_object/1, Msgs)
end,
case mnesia:transaction(F) of
diff --git a/src/mod_mam_opt.erl b/src/mod_mam_opt.erl
new file mode 100644
index 000000000..d8d970a13
--- /dev/null
+++ b/src/mod_mam_opt.erl
@@ -0,0 +1,90 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_mam_opt).
+
+-export([access_preferences/1]).
+-export([assume_mam_usage/1]).
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([clear_archive_on_room_destroy/1]).
+-export([compress_xml/1]).
+-export([db_type/1]).
+-export([default/1]).
+-export([request_activates_archiving/1]).
+-export([use_cache/1]).
+-export([user_mucsub_from_muc_archive/1]).
+
+-spec access_preferences(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_preferences(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_preferences, Opts);
+access_preferences(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, access_preferences).
+
+-spec assume_mam_usage(gen_mod:opts() | global | binary()) -> boolean().
+assume_mam_usage(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(assume_mam_usage, Opts);
+assume_mam_usage(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, assume_mam_usage).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, cache_size).
+
+-spec clear_archive_on_room_destroy(gen_mod:opts() | global | binary()) -> boolean().
+clear_archive_on_room_destroy(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(clear_archive_on_room_destroy, Opts);
+clear_archive_on_room_destroy(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, clear_archive_on_room_destroy).
+
+-spec compress_xml(gen_mod:opts() | global | binary()) -> boolean().
+compress_xml(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(compress_xml, Opts);
+compress_xml(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, compress_xml).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, db_type).
+
+-spec default(gen_mod:opts() | global | binary()) -> 'always' | 'never' | 'roster'.
+default(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(default, Opts);
+default(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, default).
+
+-spec request_activates_archiving(gen_mod:opts() | global | binary()) -> boolean().
+request_activates_archiving(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(request_activates_archiving, Opts);
+request_activates_archiving(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, request_activates_archiving).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, use_cache).
+
+-spec user_mucsub_from_muc_archive(gen_mod:opts() | global | binary()) -> boolean().
+user_mucsub_from_muc_archive(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(user_mucsub_from_muc_archive, Opts);
+user_mucsub_from_muc_archive(Host) ->
+ gen_mod:get_module_opt(Host, mod_mam, user_mucsub_from_muc_archive).
+
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index be87e64da..94e50e038 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -24,7 +24,6 @@
-module(mod_mam_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_mam).
@@ -106,7 +105,7 @@ store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, TS) ->
jid:tolower(Peer)),
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
+ XML = case mod_mam_opt:compress_xml(LServer) of
true ->
J1 = case Type of
chat -> jid:encode({LUser, LHost, <<>>});
@@ -184,7 +183,7 @@ select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
-spec select_with_mucsub(binary(), jid(), jid(), mam_query:result(),
#rsm_set{} | undefined, all | only_count | only_messages) ->
- {[{binary(), non_neg_integer(), xmlel()}], boolean(), integer()} |
+ {[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()} |
{error, db_failure}.
select_with_mucsub(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
MAMQuery, RSM, Flags) ->
@@ -354,7 +353,7 @@ make_sql_query(User, LServer, MAMQuery, RSM, ExtraUsernames) ->
With = proplists:get_value(with, MAMQuery),
WithText = proplists:get_value(withtext, MAMQuery),
{Max, Direction, ID} = get_max_direction_id(RSM),
- ODBCType = ejabberd_config:get_option({sql_type, LServer}),
+ ODBCType = ejabberd_option:sql_type(LServer),
Escape =
case ODBCType of
mssql -> fun ejabberd_sql:standard_escape/1;
diff --git a/src/mod_metrics.erl b/src/mod_metrics.erl
index 070f927e2..8c33aa89c 100644
--- a/src/mod_metrics.erl
+++ b/src/mod_metrics.erl
@@ -32,7 +32,7 @@
-include("xmpp.hrl").
-export([start/2, stop/1, mod_opt_type/1, mod_options/1, depends/2, reload/3]).
-
+-export([push/2]).
-export([offline_message_hook/1,
sm_register_connection_hook/3, sm_remove_connection_hook/3,
user_send_packet/1, user_receive_packet/1,
@@ -42,6 +42,8 @@
-define(SOCKET_NAME, mod_metrics_udp_socket).
-define(SOCKET_REGISTER_RETRIES, 10).
+-type probe() :: atom() | {atom(), integer()}.
+
%%====================================================================
%% API
%%====================================================================
@@ -124,12 +126,14 @@ register_user(_User, Server) ->
%%====================================================================
%% metrics push handler
%%====================================================================
-
+-spec push(binary(), probe()) -> ok | {error, not_owner | inet:posix()}.
push(Host, Probe) ->
- IP = gen_mod:get_module_opt(Host, ?MODULE, ip),
- Port = gen_mod:get_module_opt(Host, ?MODULE, port),
+ IP = mod_metrics_opt:ip(Host),
+ Port = mod_metrics_opt:port(Host),
send_metrics(Host, Probe, IP, Port).
+-spec send_metrics(binary(), probe(), inet:ip4_address(), inet:port_number()) ->
+ ok | {error, not_owner | inet:posix()}.
send_metrics(Host, Probe, Peer, Port) ->
% our default metrics handler is https://github.com/processone/grapherl
% grapherl metrics are named first with service domain, then nodename
@@ -156,6 +160,7 @@ send_metrics(Host, Probe, Peer, Port) ->
Err
end.
+-spec get_socket(integer()) -> {ok, gen_udp:socket()} | {error, inet:posix()}.
get_socket(N) ->
case whereis(?SOCKET_NAME) of
undefined ->
@@ -177,13 +182,9 @@ get_socket(N) ->
end.
mod_opt_type(ip) ->
- fun(S) ->
- {ok, IP} = inet:parse_ipv4_address(
- binary_to_list(iolist_to_binary(S))),
- IP
- end;
+ econf:ipv4();
mod_opt_type(port) ->
- fun(I) when is_integer(I), I>0, I<65536 -> I end.
+ econf:port().
mod_options(_) ->
- [{ip, <<"127.0.0.1">>}, {port, 11111}].
+ [{ip, {127,0,0,1}}, {port, 11111}].
diff --git a/src/mod_metrics_opt.erl b/src/mod_metrics_opt.erl
new file mode 100644
index 000000000..22b656775
--- /dev/null
+++ b/src/mod_metrics_opt.erl
@@ -0,0 +1,20 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_metrics_opt).
+
+-export([ip/1]).
+-export([port/1]).
+
+-spec ip(gen_mod:opts() | global | binary()) -> {127,0,0,1} | inet:ip4_address().
+ip(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ip, Opts);
+ip(Host) ->
+ gen_mod:get_module_opt(Host, mod_metrics, ip).
+
+-spec port(gen_mod:opts() | global | binary()) -> 1..1114111.
+port(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(port, Opts);
+port(Host) ->
+ gen_mod:get_module_opt(Host, mod_metrics, port).
+
diff --git a/src/mod_mix.erl b/src/mod_mix.erl
index 5625beac1..01446314b 100644
--- a/src/mod_mix.erl
+++ b/src/mod_mix.erl
@@ -46,7 +46,7 @@
-callback init(binary(), gen_mod:opts()) -> ok | {error, db_failure}.
-callback set_channel(binary(), binary(), binary(),
- binary(), boolean(), binary()) ->
+ jid:jid(), boolean(), binary()) ->
ok | {error, db_failure}.
-callback get_channels(binary(), binary()) ->
{ok, [binary()]} | {error, db_failure}.
@@ -77,15 +77,20 @@ reload(Host, NewOpts, OldOpts) ->
depends(_Host, _Opts) ->
[{mod_mam, hard}].
-mod_opt_type(access_create) -> fun acl:access_rules_validator/1;
-mod_opt_type(name) -> fun iolist_to_binary/1;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end.
+mod_opt_type(access_create) ->
+ econf:acl();
+mod_opt_type(name) ->
+ econf:binary();
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE);
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE).
mod_options(Host) ->
[{access_create, all},
- {host, <<"mix.@HOST@">>},
+ {host, <<"mix.", Host/binary>>},
{hosts, []},
{name, ?T("Channels")},
{db_type, ejabberd_config:default_db(Host, ?MODULE)}].
@@ -116,7 +121,7 @@ process_disco_info(#iq{type = get, to = #jid{luser = <<>>} = To,
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
[ServerHost, ?MODULE, <<"">>, Lang]),
- Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
+ Name = mod_mix_opt:name(ServerHost),
Identity = #identity{category = <<"conference">>,
type = <<"text">>,
name = translate:translate(Lang, Name)},
@@ -247,9 +252,9 @@ process_mam_query(IQ) ->
%%%===================================================================
init([Host, Opts]) ->
process_flag(trap_exit, true),
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
- MyHosts = gen_mod:get_opt_hosts(Host, Opts),
- case Mod:init(Host, [{hosts, MyHosts}|Opts]) of
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
+ MyHosts = gen_mod:get_opt_hosts(Opts),
+ case Mod:init(Host, gen_mod:set_opt(hosts, MyHosts, Opts)) of
ok ->
lists:foreach(
fun(MyHost) ->
@@ -530,7 +535,7 @@ known_nodes() ->
[?NS_MIX_NODES_MESSAGES,
?NS_MIX_NODES_PARTICIPANTS].
--spec filter_nodes(binary()) -> [binary()].
+-spec filter_nodes([binary()]) -> [binary()].
filter_nodes(Nodes) ->
lists:filter(
fun(Node) ->
diff --git a/src/mod_mix_mnesia.erl b/src/mod_mix_mnesia.erl
index 38c03d761..2ffd32bee 100644
--- a/src/mod_mix_mnesia.erl
+++ b/src/mod_mix_mnesia.erl
@@ -21,7 +21,6 @@
%%%----------------------------------------------------------------------
-module(mod_mix_mnesia).
-behaviour(mod_mix).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2]).
diff --git a/src/mod_mix_opt.erl b/src/mod_mix_opt.erl
new file mode 100644
index 000000000..b8225b19e
--- /dev/null
+++ b/src/mod_mix_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_mix_opt).
+
+-export([access_create/1]).
+-export([db_type/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([name/1]).
+
+-spec access_create(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_create(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_create, Opts);
+access_create(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix, access_create).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix, db_type).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix, hosts).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix, name).
+
diff --git a/src/mod_mix_pam.erl b/src/mod_mix_pam.erl
index 9bcbfbf21..b9eba337e 100644
--- a/src/mod_mix_pam.erl
+++ b/src/mod_mix_pam.erl
@@ -52,7 +52,7 @@
%%% API
%%%===================================================================
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
case Mod:init(Host, Opts) of
ok ->
init_cache(Mod, Host, Opts),
@@ -72,8 +72,8 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MIX_PAM_0).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -84,20 +84,23 @@ reload(Host, NewOpts, OldOpts) ->
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_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
-spec bounce_sm_packet({term(), stanza()}) -> {term(), stanza()}.
bounce_sm_packet({_, #message{to = #jid{lresource = <<>>} = To,
@@ -329,9 +332,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_mix_pam_opt:cache_size(Opts),
+ CacheMissed = mod_mix_pam_opt:cache_missed(Opts),
+ LifeTime = case mod_mix_pam_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -341,7 +344,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_mix_pam_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
diff --git a/src/mod_mix_pam_mnesia.erl b/src/mod_mix_pam_mnesia.erl
index 568c4b9fa..7d14579eb 100644
--- a/src/mod_mix_pam_mnesia.erl
+++ b/src/mod_mix_pam_mnesia.erl
@@ -47,7 +47,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(mix_pam, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_mix_pam, use_cache);
+ mod_mix_pam_opt:use_cache(Host);
_ ->
false
end.
@@ -69,7 +69,7 @@ get_channel(User, Channel) ->
get_channels(User) ->
{LUser, LServer, _} = jid:tolower(User),
- Ret = mnesia:dirty_index_read(mix_pam, #mix_pam.user, {LUser, LServer}),
+ Ret = mnesia:dirty_index_read(mix_pam, {LUser, LServer}, #mix_pam.user),
{ok, lists:map(
fun(#mix_pam{user_channel = {_, _, Chan, Service},
id = ID}) ->
@@ -83,7 +83,7 @@ del_channel(User, Channel) ->
del_channels(User) ->
{LUser, LServer, _} = jid:tolower(User),
- Ret = mnesia:dirty_index_read(mix_pam, #mix_pam.user, {LUser, LServer}),
+ Ret = mnesia:dirty_index_read(mix_pam, {LUser, LServer}, #mix_pam.user),
lists:foreach(fun mnesia:dirty_delete_object/1, Ret).
%%%===================================================================
diff --git a/src/mod_mix_pam_opt.erl b/src/mod_mix_pam_opt.erl
new file mode 100644
index 000000000..103e6039c
--- /dev/null
+++ b/src/mod_mix_pam_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_mix_pam_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix_pam, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix_pam, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix_pam, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix_pam, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_mix_pam, use_cache).
+
diff --git a/src/mod_mix_pam_sql.erl b/src/mod_mix_pam_sql.erl
index eda5966f1..c23046154 100644
--- a/src/mod_mix_pam_sql.erl
+++ b/src/mod_mix_pam_sql.erl
@@ -22,7 +22,6 @@
%%%----------------------------------------------------------------------
-module(mod_mix_pam_sql).
-behaviour(mod_mix_pam).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, add_channel/3, get_channel/2,
@@ -109,6 +108,7 @@ del_channels(User) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
--spec report_corrupted(iolist()) -> ok.
+-spec report_corrupted(#sql_query{}) -> ok.
report_corrupted(SQL) ->
- ?ERROR_MSG("Corrupted values returned by SQL request: ~s", [SQL]).
+ ?ERROR_MSG("Corrupted values returned by SQL request: ~s",
+ [SQL#sql_query.hash]).
diff --git a/src/mod_mix_sql.erl b/src/mod_mix_sql.erl
index 16f7c0d17..e67848608 100644
--- a/src/mod_mix_sql.erl
+++ b/src/mod_mix_sql.erl
@@ -21,7 +21,6 @@
%%%----------------------------------------------------------------------
-module(mod_mix_sql).
-behaviour(mod_mix).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2]).
@@ -230,7 +229,7 @@ unsubscribe(LServer, Channel, Service, JID, Nodes) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
--spec report_corrupted(atom(), iolist()) -> ok.
+-spec report_corrupted(atom(), #sql_query{}) -> ok.
report_corrupted(Column, SQL) ->
?ERROR_MSG("Corrupted value of '~s' column returned by "
- "SQL request: ~s", [Column, SQL]).
+ "SQL request: ~s", [Column, SQL#sql_query.hash]).
diff --git a/src/mod_mqtt.erl b/src/mod_mqtt.erl
index 566804f36..196b6efbe 100644
--- a/src/mod_mqtt.erl
+++ b/src/mod_mqtt.erl
@@ -19,6 +19,7 @@
-behaviour(p1_server).
-behaviour(gen_mod).
-behaviour(ejabberd_listener).
+-dialyzer({no_improper_lists, join_filter/1}).
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_options/1, mod_opt_type/1]).
@@ -135,7 +136,7 @@ publish({_, S, _} = USR, Pkt, ExpiryTime) ->
ok | {error, db_failure | subscribe_forbidden}.
subscribe({_, S, _} = USR, TopicFilter, SubOpts, ID) ->
Mod = gen_mod:ram_db_mod(S, ?MODULE),
- Limit = gen_mod:get_module_opt(S, ?MODULE, max_topic_depth),
+ Limit = mod_mqtt_opt:max_topic_depth(S),
case check_topic_depth(TopicFilter, Limit) of
allow ->
case check_subscribe_access(TopicFilter, USR) of
@@ -157,15 +158,15 @@ unsubscribe({U, S, R}, Topic) ->
[{publish(), seconds()}].
select_retained({_, S, _} = USR, TopicFilter, QoS, SubID) ->
Mod = gen_mod:db_mod(S, ?MODULE),
- Limit = gen_mod:get_module_opt(S, ?MODULE, match_retained_limit),
+ Limit = mod_mqtt_opt:match_retained_limit(S),
select_retained(Mod, USR, TopicFilter, QoS, SubID, Limit).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([Host, Opts]) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
- RMod = gen_mod:ram_db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
+ RMod = gen_mod:ram_db_mod(Opts, ?MODULE),
try
ok = Mod:init(Host, Opts),
ok = RMod:init(),
@@ -194,6 +195,9 @@ code_change(_OldVsn, State, _Extra) ->
%%%===================================================================
%%% Options
%%%===================================================================
+-spec mod_options(binary()) -> [{access_publish, [{[binary()], acl:acl()}]} |
+ {access_subscribe, [{[binary()], acl:acl()}]} |
+ {atom(), any()}].
mod_options(Host) ->
[{match_retained_limit, 1000},
{max_topic_depth, 8},
@@ -204,55 +208,45 @@ mod_options(Host) ->
{access_publish, []},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
- {queue_type, ejabberd_config:default_queue_type(Host)},
- {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)}].
+ {queue_type, ejabberd_option:queue_type(Host)},
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
mod_opt_type(max_queue) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> unlimited;
- (unlimited) -> unlimited
- end;
+ econf:pos_int(unlimited);
mod_opt_type(session_expiry) ->
- fun(I) when is_integer(I), I>= 0 -> I end;
+ econf:non_neg_int();
mod_opt_type(match_retained_limit) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
mod_opt_type(max_topic_depth) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
mod_opt_type(max_topic_aliases) ->
- fun(I) when is_integer(I), I>=0, I<65536 -> I end;
+ econf:int(0, 65535);
mod_opt_type(access_subscribe) ->
- fun validate_topic_access/1;
+ topic_access_validator();
mod_opt_type(access_publish) ->
- fun validate_topic_access/1;
+ topic_access_validator();
+mod_opt_type(queue_type) ->
+ econf:well_known(queue_type, ?MODULE);
mod_opt_type(db_type) ->
- fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+ econf:well_known(db_type, ?MODULE);
mod_opt_type(ram_db_type) ->
- fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(queue_type) ->
- fun(ram) -> ram; (file) -> file 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.
+ econf:well_known(ram_db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
listen_opt_type(tls_verify) ->
- fun(B) when is_boolean(B) -> B end;
+ econf:bool();
listen_opt_type(max_payload_size) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end.
+ econf:pos_int(infinity).
listen_options() ->
[{max_fsm_queue, 5000},
@@ -436,30 +430,31 @@ split_path(Path) ->
%%%===================================================================
%%% Validators
%%%===================================================================
-validate_topic_access(FilterRules) ->
- lists:map(
- fun({TopicFilter, Access}) ->
- Rule = acl:access_rules_validator(Access),
- try
- mqtt_codec:topic_filter(TopicFilter),
- {split_path(TopicFilter), Rule}
- catch _:_ ->
- ?ERROR_MSG("Invalid topic filter: ~s", [TopicFilter]),
- erlang:error(badarg)
- end
- end, lists:reverse(lists:keysort(1, FilterRules))).
+-spec topic_access_validator() -> econf:validator().
+topic_access_validator() ->
+ econf:and_then(
+ econf:map(
+ fun(TF) ->
+ try split_path(mqtt_codec:topic_filter(TF))
+ catch _:{mqtt_codec, _} = Reason ->
+ econf:fail(Reason)
+ end
+ end,
+ econf:acl(),
+ [{return, orddict}]),
+ fun lists:reverse/1).
%%%===================================================================
%%% ACL checks
%%%===================================================================
check_subscribe_access(Topic, {_, S, _} = USR) ->
- Rules = gen_mod:get_module_opt(S, mod_mqtt, access_subscribe),
+ Rules = mod_mqtt_opt:access_subscribe(S),
check_access(Topic, USR, Rules).
check_publish_access(<<$$, _/binary>>, _) ->
deny;
check_publish_access(Topic, {_, S, _} = USR) ->
- Rules = gen_mod:get_module_opt(S, mod_mqtt, access_publish),
+ Rules = mod_mqtt_opt:access_publish(S),
check_access(Topic, USR, Rules).
check_access(_, _, []) ->
@@ -544,9 +539,9 @@ init_payload_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_mqtt_opt:cache_size(Opts),
+ CacheMissed = mod_mqtt_opt:cache_missed(Opts),
+ LifeTime = case mod_mqtt_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -556,7 +551,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_mqtt_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
diff --git a/src/mod_mqtt_mnesia.erl b/src/mod_mqtt_mnesia.erl
index b1b8b9dbd..6f29a1522 100644
--- a/src/mod_mqtt_mnesia.erl
+++ b/src/mod_mqtt_mnesia.erl
@@ -67,7 +67,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(mqtt_pub, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_mqtt, use_cache);
+ mod_mqtt_opt:use_cache(Host);
_ ->
false
end.
@@ -217,7 +217,7 @@ subscribe({U, S, R} = USR, TopicFilter, SubOpts, ID) ->
end,
case mnesia:transaction(F) of
{atomic, _} -> ok;
- {abored, Reason} ->
+ {aborted, Reason} ->
db_fail("Failed to subscribe ~s to ~s",
Reason, [jid:encode(USR), TopicFilter])
end.
diff --git a/src/mod_mqtt_opt.erl b/src/mod_mqtt_opt.erl
new file mode 100644
index 000000000..5459f39e8
--- /dev/null
+++ b/src/mod_mqtt_opt.erl
@@ -0,0 +1,104 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_mqtt_opt).
+
+-export([access_publish/1]).
+-export([access_subscribe/1]).
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([match_retained_limit/1]).
+-export([max_queue/1]).
+-export([max_topic_aliases/1]).
+-export([max_topic_depth/1]).
+-export([queue_type/1]).
+-export([ram_db_type/1]).
+-export([session_expiry/1]).
+-export([use_cache/1]).
+
+-spec access_publish(gen_mod:opts() | global | binary()) -> [{[binary()],acl:acl()}].
+access_publish(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_publish, Opts);
+access_publish(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, access_publish).
+
+-spec access_subscribe(gen_mod:opts() | global | binary()) -> [{[binary()],acl:acl()}].
+access_subscribe(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_subscribe, Opts);
+access_subscribe(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, access_subscribe).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, db_type).
+
+-spec match_retained_limit(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+match_retained_limit(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(match_retained_limit, Opts);
+match_retained_limit(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, match_retained_limit).
+
+-spec max_queue(gen_mod:opts() | global | binary()) -> 'unlimited' | pos_integer().
+max_queue(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_queue, Opts);
+max_queue(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, max_queue).
+
+-spec max_topic_aliases(gen_mod:opts() | global | binary()) -> char().
+max_topic_aliases(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_topic_aliases, Opts);
+max_topic_aliases(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, max_topic_aliases).
+
+-spec max_topic_depth(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_topic_depth(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_topic_depth, Opts);
+max_topic_depth(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, max_topic_depth).
+
+-spec queue_type(gen_mod:opts() | global | binary()) -> 'file' | 'ram'.
+queue_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_type, Opts);
+queue_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, queue_type).
+
+-spec ram_db_type(gen_mod:opts() | global | binary()) -> atom().
+ram_db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ram_db_type, Opts);
+ram_db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, ram_db_type).
+
+-spec session_expiry(gen_mod:opts() | global | binary()) -> non_neg_integer().
+session_expiry(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(session_expiry, Opts);
+session_expiry(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, session_expiry).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_mqtt, use_cache).
+
diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl
index bbcf9258a..c0777d266 100644
--- a/src/mod_mqtt_session.erl
+++ b/src/mod_mqtt_session.erl
@@ -33,7 +33,7 @@
-record(state, {vsn = ?VSN :: integer(),
version :: undefined | mqtt_version(),
socket :: undefined | socket(),
- peername :: peername(),
+ peername :: undefined | peername(),
timeout = infinity :: timer(),
jid :: undefined | jid:jid(),
session_expiry = 0 :: seconds(),
@@ -1050,19 +1050,19 @@ connack_reason_code(_) -> 'unspecified-error'.
%%%===================================================================
-spec queue_type(binary()) -> ram | file.
queue_type(Host) ->
- gen_mod:get_module_opt(Host, mod_mqtt, queue_type).
+ mod_mqtt_opt:queue_type(Host).
-spec queue_limit(binary()) -> non_neg_integer() | unlimited.
queue_limit(Host) ->
- gen_mod:get_module_opt(Host, mod_mqtt, max_queue).
+ mod_mqtt_opt:max_queue(Host).
-spec session_expiry(binary()) -> seconds().
session_expiry(Host) ->
- gen_mod:get_module_opt(Host, mod_mqtt, session_expiry).
+ mod_mqtt_opt:session_expiry(Host).
-spec topic_alias_maximum(binary()) -> non_neg_integer().
topic_alias_maximum(Host) ->
- gen_mod:get_module_opt(Host, mod_mqtt, max_topic_aliases).
+ mod_mqtt_opt:max_topic_aliases(Host).
%%%===================================================================
%%% Timings
diff --git a/src/mod_mqtt_sql.erl b/src/mod_mqtt_sql.erl
index a11f8e04c..560d995fe 100644
--- a/src/mod_mqtt_sql.erl
+++ b/src/mod_mqtt_sql.erl
@@ -17,7 +17,6 @@
%%%-------------------------------------------------------------------
-module(mod_mqtt_sql).
-behaviour(mod_mqtt).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, publish/6, delete_published/2, lookup_published/2]).
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index e546a94a9..fb4595c94 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -80,7 +80,7 @@
-record(state,
{hosts = [] :: [binary()],
server_host = <<"">> :: binary(),
- access = {none, none, none, none} :: {atom(), atom(), atom(), atom()},
+ access = {none, none, none, none} :: {atom(), atom(), atom(), atom(), atom()},
history_size = 20 :: non_neg_integer(),
max_rooms_discoitems = 100 :: non_neg_integer(),
queue_type = ram :: ram | file,
@@ -130,18 +130,18 @@ reload(Host, NewOpts, OldOpts) ->
depends(_Host, _Opts) ->
[{mod_mam, soft}].
-shutdown_rooms(Host) ->
- RMod = gen_mod:ram_db_mod(Host, ?MODULE),
- MyHost = gen_mod:get_module_opt_host(Host, mod_muc,
- <<"conference.@HOST@">>),
- Rooms = RMod:get_online_rooms(Host, MyHost, undefined),
+shutdown_rooms(ServerHost) ->
+ RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
+ Hosts = gen_mod:get_module_opt_hosts(ServerHost, mod_muc),
+ Rooms = [RMod:get_online_rooms(ServerHost, Host, undefined)
+ || Host <- Hosts],
lists:flatmap(
fun({_, _, Pid}) when node(Pid) == node() ->
Pid ! shutdown,
[Pid];
(_) ->
[]
- end, Rooms).
+ end, lists:flatten(Rooms)).
%% This function is called by a room in three situations:
%% A) The owner of the room destroyed it
@@ -238,10 +238,10 @@ init([Host, Opts]) ->
#state{access = Access, hosts = MyHosts,
history_size = HistorySize, queue_type = QueueType,
room_shaper = RoomShaper} = State = init_state(Host, Opts),
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
- RMod = gen_mod:ram_db_mod(Host, Opts, ?MODULE),
- Mod:init(Host, [{hosts, MyHosts}|Opts]),
- RMod:init(Host, [{hosts, MyHosts}|Opts]),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
+ RMod = gen_mod:ram_db_mod(Opts, ?MODULE),
+ Mod:init(Host, gen_mod:set_opt(hosts, MyHosts, Opts)),
+ RMod:init(Host, gen_mod:set_opt(hosts, MyHosts, Opts)),
lists:foreach(
fun(MyHost) ->
register_iq_handlers(MyHost),
@@ -274,18 +274,18 @@ handle_call({create, Room, Host, From, Nick, Opts}, _From,
{reply, ok, State}.
handle_cast({reload, ServerHost, NewOpts, OldOpts}, #state{hosts = OldHosts}) ->
- NewMod = gen_mod:db_mod(ServerHost, NewOpts, ?MODULE),
- NewRMod = gen_mod:ram_db_mod(ServerHost, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(ServerHost, OldOpts, ?MODULE),
- OldRMod = gen_mod:ram_db_mod(ServerHost, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ NewRMod = gen_mod:ram_db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
+ OldRMod = gen_mod:ram_db_mod(OldOpts, ?MODULE),
#state{hosts = NewHosts} = NewState = init_state(ServerHost, NewOpts),
if NewMod /= OldMod ->
- NewMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]);
+ NewMod:init(ServerHost, gen_mod:set_opt(hosts, NewHosts, NewOpts));
true ->
ok
end,
if NewRMod /= OldRMod ->
- NewRMod:init(ServerHost, [{hosts, NewHosts}|NewOpts]);
+ NewRMod:init(ServerHost, gen_mod:set_opt(hosts, NewHosts, NewOpts));
true ->
ok
end,
@@ -353,17 +353,17 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
init_state(Host, Opts) ->
- MyHosts = gen_mod:get_opt_hosts(Host, Opts),
- Access = gen_mod:get_opt(access, Opts),
- AccessCreate = gen_mod:get_opt(access_create, Opts),
- AccessAdmin = gen_mod:get_opt(access_admin, Opts),
- AccessPersistent = gen_mod:get_opt(access_persistent, Opts),
- AccessMam = gen_mod:get_opt(access_mam, Opts),
- HistorySize = gen_mod:get_opt(history_size, Opts),
- MaxRoomsDiscoItems = gen_mod:get_opt(max_rooms_discoitems, Opts),
- DefRoomOpts = gen_mod:get_opt(default_room_options, Opts),
- QueueType = gen_mod:get_opt(queue_type, Opts),
- RoomShaper = gen_mod:get_opt(room_shaper, Opts),
+ MyHosts = gen_mod:get_opt_hosts(Opts),
+ Access = mod_muc_opt:access(Opts),
+ AccessCreate = mod_muc_opt:access_create(Opts),
+ AccessAdmin = mod_muc_opt:access_admin(Opts),
+ AccessPersistent = mod_muc_opt:access_persistent(Opts),
+ AccessMam = mod_muc_opt:access_mam(Opts),
+ HistorySize = mod_muc_opt:history_size(Opts),
+ MaxRoomsDiscoItems = mod_muc_opt:max_rooms_discoitems(Opts),
+ DefRoomOpts = mod_muc_opt:default_room_options(Opts),
+ QueueType = mod_muc_opt:queue_type(Opts),
+ RoomShaper = mod_muc_opt:room_shaper(Opts),
#state{hosts = MyHosts,
server_host = Host,
access = {Access, AccessCreate, AccessAdmin, AccessPersistent, AccessMam},
@@ -490,7 +490,7 @@ process_register(#iq{type = Type, from = From, to = To, lang = Lang,
sub_els = [El = #register{}]} = IQ) ->
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
- AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
+ AccessRegister = mod_muc_opt:access_register(ServerHost),
case acl:match_rule(ServerHost, AccessRegister, From) of
allow ->
case Type of
@@ -519,7 +519,7 @@ process_disco_info(#iq{type = get, from = From, to = To, lang = Lang,
sub_els = [#disco_info{node = <<"">>}]} = IQ) ->
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE),
- AccessRegister = gen_mod:get_module_opt(ServerHost, ?MODULE, access_register),
+ AccessRegister = mod_muc_opt:access_register(ServerHost),
X = ejabberd_hooks:run_fold(disco_info, ServerHost, [],
[ServerHost, ?MODULE, <<"">>, Lang]),
MAMFeatures = case gen_mod:is_loaded(ServerHost, mod_mam) of
@@ -537,7 +537,7 @@ process_disco_info(#iq{type = get, from = From, to = To, lang = Lang,
Features = [?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
?NS_MUC, ?NS_VCARD, ?NS_MUCSUB, ?NS_MUC_UNIQUE
| RegisterFeatures ++ RSMFeatures ++ MAMFeatures],
- Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
+ Name = mod_muc_opt:name(ServerHost),
Identity = #identity{category = <<"conference">>,
type = <<"text">>,
name = translate:translate(Lang, Name)},
@@ -560,8 +560,7 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
sub_els = [#disco_items{node = Node, rsm = RSM}]} = IQ) ->
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
- MaxRoomsDiscoItems = gen_mod:get_module_opt(
- ServerHost, ?MODULE, max_rooms_discoitems),
+ MaxRoomsDiscoItems = mod_muc_opt:max_rooms_discoitems(ServerHost),
case iq_disco_items(ServerHost, Host, From, Lang,
MaxRoomsDiscoItems, Node, RSM) of
{error, Err} ->
@@ -620,10 +619,9 @@ check_create_room(ServerHost, Host, Room, From, Access) ->
_AccessPersistent, _AccessMam} = Access,
case acl:match_rule(ServerHost, AccessCreate, From) of
allow ->
- case gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id) of
+ case mod_muc_opt:max_room_id(ServerHost) of
Max when byte_size(Room) =< Max ->
- Regexp = gen_mod:get_module_opt(
- ServerHost, ?MODULE, regexp_room_id),
+ Regexp = mod_muc_opt:regexp_room_id(ServerHost),
case re:run(Room, Regexp, [unicode, {capture, none}]) of
match ->
case acl:match_rule(
@@ -945,117 +943,88 @@ import(LServer, {sql, _}, DBType, Tab, L) ->
Mod:import(LServer, Tab, L).
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(access_admin) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(access_create) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(access_persistent) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(access_mam) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(access_register) ->
- fun acl:access_rules_validator/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(ram_db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+ econf:acl();
mod_opt_type(history_size) ->
- fun (I) when is_integer(I), I >= 0 -> I end;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(name) -> fun iolist_to_binary/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
+ econf:non_neg_int();
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(max_room_desc) ->
- fun (infinity) -> infinity;
- (I) when is_integer(I), I > 0 -> I
- end;
+ econf:pos_int(infinity);
mod_opt_type(max_room_id) ->
- fun (infinity) -> infinity;
- (I) when is_integer(I), I > 0 -> I
- end;
+ econf:pos_int(infinity);
mod_opt_type(max_rooms_discoitems) ->
- fun (I) when is_integer(I), I >= 0 -> I end;
+ econf:non_neg_int();
mod_opt_type(regexp_room_id) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(max_room_name) ->
- fun (infinity) -> infinity;
- (I) when is_integer(I), I > 0 -> I
- end;
+ econf:pos_int(infinity);
mod_opt_type(max_user_conferences) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(max_users) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(max_users_admin_threshold) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(max_users_presence) ->
- fun (MUP) when is_integer(MUP) -> MUP end;
+ econf:int();
mod_opt_type(min_message_interval) ->
- fun (MMI) when is_number(MMI), MMI >= 0 -> MMI end;
+ econf:number(0);
mod_opt_type(min_presence_interval) ->
- fun (I) when is_number(I), I >= 0 -> I end;
+ econf:number(0);
mod_opt_type(room_shaper) ->
- fun (A) when is_atom(A) -> A end;
+ econf:atom();
mod_opt_type(user_message_shaper) ->
- fun (A) when is_atom(A) -> A end;
+ econf:atom();
mod_opt_type(user_presence_shaper) ->
- fun (A) when is_atom(A) -> A end;
+ econf:atom();
+mod_opt_type(default_room_options) ->
+ econf:options(
+ #{allow_change_subj => econf:bool(),
+ allow_private_messages => econf:bool(),
+ allow_private_messages_from_visitors =>
+ econf:enum([anyone, moderators, nobody]),
+ allow_query_users => econf:bool(),
+ allow_subscription => econf:bool(),
+ allow_user_invites => econf:bool(),
+ allow_visitor_nickchange => econf:bool(),
+ allow_visitor_status => econf:bool(),
+ anonymous => econf:bool(),
+ captcha_protected => econf:bool(),
+ lang => econf:lang(),
+ logging => econf:bool(),
+ mam => econf:bool(),
+ max_users => econf:pos_int(),
+ members_by_default => econf:bool(),
+ members_only => econf:bool(),
+ moderated => econf:bool(),
+ password => econf:binary(),
+ password_protected => econf:bool(),
+ persistent => econf:bool(),
+ presence_broadcast =>
+ econf:list(
+ econf:enum([moderator, participant, visitor])),
+ public => econf:bool(),
+ public_list => econf:bool(),
+ title => econf:binary()});
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(ram_db_type) ->
+ econf:well_known(ram_db_type, ?MODULE);
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE);
mod_opt_type(queue_type) ->
- fun(ram) -> ram; (file) -> file end;
-mod_opt_type({default_room_options, allow_change_subj}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_private_messages}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_query_users}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_user_invites}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_visitor_nickchange}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_visitor_status}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, anonymous}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, captcha_protected}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, logging}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, members_by_default}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, members_only}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, moderated}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, password_protected}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, persistent}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, public}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, public_list}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, mam}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, allow_subscription}) ->
- fun(B) when is_boolean(B) -> B end;
-mod_opt_type({default_room_options, password}) ->
- fun iolist_to_binary/1;
-mod_opt_type({default_room_options, title}) ->
- fun iolist_to_binary/1;
-mod_opt_type({default_room_options, allow_private_messages_from_visitors}) ->
- fun(anyone) -> anyone;
- (moderators) -> moderators;
- (nobody) -> nobody
- end;
-mod_opt_type({default_room_options, max_users}) ->
- fun(I) when is_integer(I), I > 0 -> I end;
-mod_opt_type({default_room_options, presence_broadcast}) ->
- fun(L) ->
- lists:map(
- fun(moderator) -> moderator;
- (participant) -> participant;
- (visitor) -> visitor
- end, L)
- end;
-mod_opt_type({default_room_options, lang}) ->
- fun xmpp_lang:check/1.
+ econf:well_known(queue_type, ?MODULE).
mod_options(Host) ->
[{access, all},
@@ -1067,7 +1036,7 @@ mod_options(Host) ->
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
{history_size, 20},
- {host, <<"conference.@HOST@">>},
+ {host, <<"conference.", Host/binary>>},
{hosts, []},
{name, ?T("Chatrooms")},
{max_room_desc, infinity},
@@ -1080,7 +1049,7 @@ mod_options(Host) ->
{max_users_presence, 1000},
{min_message_interval, 0},
{min_presence_interval, 0},
- {queue_type, ejabberd_config:default_queue_type(Host)},
+ {queue_type, ejabberd_option:queue_type(Host)},
{regexp_room_id, <<"">>},
{room_shaper, none},
{user_message_shaper, none},
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index 3672c2b9c..499987e1e 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -411,7 +411,7 @@ get_user_rooms(User, Server) ->
false ->
[]
end
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
%%----------------------------
%% Ad-hoc commands
@@ -619,8 +619,7 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
true = (error /= (Host = jid:nodeprep(Host1))),
%% Get the default room options from the muc configuration
- DefRoomOpts = gen_mod:get_module_opt(ServerHost, mod_muc,
- default_room_options),
+ DefRoomOpts = mod_muc_opt:default_room_options(ServerHost),
%% Change default room options as required
FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts],
RoomOpts = lists:ukeymerge(1,
@@ -631,14 +630,14 @@ create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
mod_muc:store_room(ServerHost, Host, Name, RoomOpts),
%% Get all remaining mod_muc parameters that might be utilized
- Access = gen_mod:get_module_opt(ServerHost, mod_muc, access),
- AcCreate = gen_mod:get_module_opt(ServerHost, mod_muc, access_create),
- AcAdmin = gen_mod:get_module_opt(ServerHost, mod_muc, access_admin),
- AcPer = gen_mod:get_module_opt(ServerHost, mod_muc, access_persistent),
- AcMam = gen_mod:get_module_opt(ServerHost, mod_muc, access_mam),
- HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size),
- RoomShaper = gen_mod:get_module_opt(ServerHost, mod_muc, room_shaper),
- QueueType = gen_mod:get_module_opt(ServerHost, mod_muc, queue_type),
+ Access = mod_muc_opt:access(ServerHost),
+ AcCreate = mod_muc_opt:access_create(ServerHost),
+ AcAdmin = mod_muc_opt:access_admin(ServerHost),
+ AcPer = mod_muc_opt:access_persistent(ServerHost),
+ AcMam = mod_muc_opt:access_mam(ServerHost),
+ HistorySize = mod_muc_opt:history_size(ServerHost),
+ RoomShaper = mod_muc_opt:room_shaper(ServerHost),
+ QueueType = mod_muc_opt:queue_type(ServerHost),
%% If the room does not exist yet in the muc_online_room
case mod_muc:find_online_room(Name, Host) of
@@ -739,8 +738,7 @@ create_rooms_file(Filename) ->
Rooms = read_rooms(F, RJID, []),
file:close(F),
%% Read the default room options defined for the first virtual host
- DefRoomOpts = gen_mod:get_module_opt(ejabberd_config:get_myname(), mod_muc,
- default_room_options),
+ DefRoomOpts = mod_muc_opt:default_room_options(ejabberd_config:get_myname()),
[muc_create_room(ejabberd_config:get_myname(), A, DefRoomOpts) || A <- Rooms],
ok.
@@ -820,7 +818,7 @@ decide_room(unused, {_Room_name, _Host, Room_pid}, ServerHost, Last_allowed) ->
History = (S#state.history)#lqueue.queue,
Ts_now = calendar:universal_time(),
- HistorySize = gen_mod:get_module_opt(ServerHost, mod_muc, history_size),
+ HistorySize = mod_muc_opt:history_size(ServerHost),
{Has_hist, Last} = case p1_queue:is_empty(History) of
true when (HistorySize == 0) or (Just_created == true) ->
{false, 0};
@@ -865,7 +863,7 @@ seconds_to_days(S) ->
%% Act
act_on_rooms(Method, Action, Rooms, ServerHost) ->
- ServerHosts = [ {A, find_host(A)} || A <- ejabberd_config:get_myhosts() ],
+ ServerHosts = [ {A, find_host(A)} || A <- ejabberd_option:hosts() ],
Delete = fun({_N, H, _Pid} = Room) ->
SH = case ServerHost of
global -> find_serverhost(H, ServerHosts);
@@ -1279,7 +1277,7 @@ find_host(<<"global">>) ->
find_host(ServerHost) when is_list(ServerHost) ->
find_host(list_to_binary(ServerHost));
find_host(ServerHost) ->
- gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>).
+ hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)).
find_hosts(Global) when Global == global;
Global == "global";
@@ -1292,7 +1290,7 @@ find_hosts(Global) when Global == global;
false ->
[]
end
- end, ejabberd_config:get_myhosts());
+ end, ejabberd_option:hosts());
find_hosts(ServerHost) when is_list(ServerHost) ->
find_hosts(list_to_binary(ServerHost));
find_hosts(ServerHost) ->
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index 3e4f67247..0e2ff6984 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -34,7 +34,7 @@
-behaviour(gen_mod).
%% API
--export([start/2, stop/1, reload/3, transform_module_options/1,
+-export([start/2, stop/1, reload/3,
check_access_log/2, add_to_log/5]).
-export([init/1, handle_call/3, handle_cast/2,
@@ -91,14 +91,6 @@ check_access_log(Host, From) ->
Res -> Res
end.
-transform_module_options(Opts) ->
- lists:map(
- fun({top_link, {S1, S2}}) ->
- {top_link, [{S1, S2}]};
- (Opt) ->
- Opt
- end, Opts).
-
depends(_Host, _Opts) ->
[{mod_muc, hard}].
@@ -137,17 +129,17 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%% Internal functions
%%--------------------------------------------------------------------
init_state(Host, Opts) ->
- OutDir = gen_mod:get_opt(outdir, Opts),
- DirType = gen_mod:get_opt(dirtype, Opts),
- DirName = gen_mod:get_opt(dirname, Opts),
- FileFormat = gen_mod:get_opt(file_format, Opts),
- FilePermissions = gen_mod:get_opt(file_permissions, Opts),
- CSSFile = gen_mod:get_opt(cssfile, Opts),
- AccessLog = gen_mod:get_opt(access_log, Opts),
- Timezone = gen_mod:get_opt(timezone, Opts),
- Top_link = gen_mod:get_opt(top_link, Opts),
- NoFollow = gen_mod:get_opt(spam_prevention, Opts),
- Lang = ejabberd_config:get_lang(Host),
+ OutDir = mod_muc_log_opt:outdir(Opts),
+ DirType = mod_muc_log_opt:dirtype(Opts),
+ DirName = mod_muc_log_opt:dirname(Opts),
+ FileFormat = mod_muc_log_opt:file_format(Opts),
+ FilePermissions = mod_muc_log_opt:file_permissions(Opts),
+ CSSFile = mod_muc_log_opt:cssfile(Opts),
+ AccessLog = mod_muc_log_opt:access_log(Opts),
+ Timezone = mod_muc_log_opt:timezone(Opts),
+ Top_link = mod_muc_log_opt:top_link(Opts),
+ NoFollow = mod_muc_log_opt:spam_prevention(Opts),
+ Lang = ejabberd_option:language(Host),
#logstate{host = Host, out_dir = OutDir,
dir_type = DirType, dir_name = DirName,
file_format = FileFormat, css_file = CSSFile,
@@ -885,26 +877,30 @@ get_room_occupants(RoomJIDString) ->
RoomJID = jid:decode(RoomJIDString),
RoomName = RoomJID#jid.luser,
MucService = RoomJID#jid.lserver,
- StateData = get_room_state(RoomName, MucService),
- [{U#user.jid, U#user.nick, U#user.role}
- || U <- maps:values(StateData#state.users)].
+ case get_room_state(RoomName, MucService) of
+ {ok, StateData} ->
+ [{U#user.jid, U#user.nick, U#user.role}
+ || U <- maps:values(StateData#state.users)];
+ error ->
+ []
+ end.
--spec get_room_state(binary(), binary()) -> mod_muc_room:state().
+-spec get_room_state(binary(), binary()) -> {ok, mod_muc_room:state()} | error.
get_room_state(RoomName, MucService) ->
case mod_muc:find_online_room(RoomName, MucService) of
{ok, RoomPid} ->
- get_room_state(RoomPid);
+ get_room_state(RoomPid);
error ->
- #state{}
+ error
end.
--spec get_room_state(pid()) -> mod_muc_room:state().
+-spec get_room_state(pid()) -> {ok, mod_muc_room:state()} | error.
get_room_state(RoomPid) ->
- {ok, R} = p1_fsm:sync_send_all_state_event(RoomPid,
- get_state),
- R.
+ try p1_fsm:sync_send_all_state_event(RoomPid, get_state)
+ catch _:_ -> error
+ end.
get_proc_name(Host) ->
gen_mod:get_module_proc(Host, ?MODULE).
@@ -929,58 +925,48 @@ has_no_permanent_store_hint(Packet) ->
xmpp:has_subtag(Packet, #hint{type = 'no-permanent-storage'}).
mod_opt_type(access_log) ->
- fun acl:access_rules_validator/1;
+ econf:acl();
mod_opt_type(cssfile) ->
- fun(S) ->
- case str:to_lower(S) of
- <<"http:/", _/binary>> -> {url, misc:try_url(S)};
- <<"https:/", _/binary>> -> {url, misc:try_url(S)};
- _ -> {file, misc:try_read_file(S)}
- end
- end;
+ econf:url_or_file();
mod_opt_type(dirname) ->
- fun (room_jid) -> room_jid;
- (room_name) -> room_name
- end;
+ econf:enum([room_jid, room_name]);
mod_opt_type(dirtype) ->
- fun (subdirs) -> subdirs;
- (plain) -> plain
- end;
+ econf:enum([subdirs, plain]);
mod_opt_type(file_format) ->
- fun (html) -> html;
- (plaintext) -> plaintext
- end;
+ econf:enum([html, plaintext]);
mod_opt_type(file_permissions) ->
- fun (SubOpts) ->
- {proplists:get_value(mode, SubOpts, 644),
- proplists:get_value(group, SubOpts, 33)}
- end;
-mod_opt_type({file_permissions, mode}) ->
- fun(I) when is_integer(I), I>=0 -> I end;
-mod_opt_type({file_permissions, group}) ->
- fun(I) when is_integer(I), I>=0 -> I end;
-mod_opt_type(outdir) -> fun iolist_to_binary/1;
+ econf:and_then(
+ econf:options(
+ #{mode => econf:non_neg_int(),
+ group => econf:non_neg_int()}),
+ fun(Opts) ->
+ {proplists:get_value(mode, Opts, 644),
+ proplists:get_value(group, Opts, 33)}
+ end);
+mod_opt_type(outdir) ->
+ econf:directory(write);
mod_opt_type(spam_prevention) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(timezone) ->
- fun (local) -> local;
- (universal) -> universal
- end;
+ econf:enum([local, universal]);
mod_opt_type(top_link) ->
- fun ([{S1, S2}]) ->
- {iolist_to_binary(S1), iolist_to_binary(S2)}
- end.
-
+ econf:and_then(
+ econf:non_empty(
+ econf:map(econf:binary(), econf:binary())),
+ fun hd/1).
+
+-spec mod_options(binary()) -> [{top_link, {binary(), binary()}} |
+ {file_permissions,
+ {non_neg_integer(), non_neg_integer()}} |
+ {atom(), any()}].
mod_options(_) ->
[{access_log, muc_admin},
- {cssfile, filename:join(misc:css_dir(), "muc.css")},
+ {cssfile, filename:join(misc:css_dir(), <<"muc.css">>)},
{dirname, room_jid},
{dirtype, subdirs},
{file_format, html},
- {file_permissions,
- [{mode, 644},
- {group, 33}]},
+ {file_permissions, {644, 33}},
{outdir, <<"www/muc">>},
{spam_prevention, true},
{timezone, local},
- {top_link, [{<<"/">>, <<"Home">>}]}].
+ {top_link, {<<"/">>, <<"Home">>}}].
diff --git a/src/mod_muc_log_opt.erl b/src/mod_muc_log_opt.erl
new file mode 100644
index 000000000..1fbad70c1
--- /dev/null
+++ b/src/mod_muc_log_opt.erl
@@ -0,0 +1,76 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_muc_log_opt).
+
+-export([access_log/1]).
+-export([cssfile/1]).
+-export([dirname/1]).
+-export([dirtype/1]).
+-export([file_format/1]).
+-export([file_permissions/1]).
+-export([outdir/1]).
+-export([spam_prevention/1]).
+-export([timezone/1]).
+-export([top_link/1]).
+
+-spec access_log(gen_mod:opts() | global | binary()) -> 'muc_admin' | acl:acl().
+access_log(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_log, Opts);
+access_log(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, access_log).
+
+-spec cssfile(gen_mod:opts() | global | binary()) -> {'file',binary()} | {'url',binary()}.
+cssfile(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cssfile, Opts);
+cssfile(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, cssfile).
+
+-spec dirname(gen_mod:opts() | global | binary()) -> 'room_jid' | 'room_name'.
+dirname(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(dirname, Opts);
+dirname(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, dirname).
+
+-spec dirtype(gen_mod:opts() | global | binary()) -> 'plain' | 'subdirs'.
+dirtype(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(dirtype, Opts);
+dirtype(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, dirtype).
+
+-spec file_format(gen_mod:opts() | global | binary()) -> 'html' | 'plaintext'.
+file_format(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(file_format, Opts);
+file_format(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, file_format).
+
+-spec file_permissions(gen_mod:opts() | global | binary()) -> {non_neg_integer(),non_neg_integer()}.
+file_permissions(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(file_permissions, Opts);
+file_permissions(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, file_permissions).
+
+-spec outdir(gen_mod:opts() | global | binary()) -> binary().
+outdir(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(outdir, Opts);
+outdir(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, outdir).
+
+-spec spam_prevention(gen_mod:opts() | global | binary()) -> boolean().
+spam_prevention(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(spam_prevention, Opts);
+spam_prevention(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, spam_prevention).
+
+-spec timezone(gen_mod:opts() | global | binary()) -> 'local' | 'universal'.
+timezone(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(timezone, Opts);
+timezone(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, timezone).
+
+-spec top_link(gen_mod:opts() | global | binary()) -> {binary(),binary()}.
+top_link(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(top_link, Opts);
+top_link(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc_log, top_link).
+
diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl
index fdd109a85..1bc81e97d 100644
--- a/src/mod_muc_mnesia.erl
+++ b/src/mod_muc_mnesia.erl
@@ -263,7 +263,7 @@ unregister_online_user(_ServerHost, {U, S, R}, Room, Host) ->
room = Room, host = Host}).
count_online_rooms_by_user(ServerHost, U, S) ->
- MucHost = gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>),
+ MucHost = hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)),
ets:select_count(
muc_online_users,
ets:fun2ms(
@@ -272,7 +272,7 @@ count_online_rooms_by_user(ServerHost, U, S) ->
end)).
get_online_rooms_by_user(ServerHost, U, S) ->
- MucHost = gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>),
+ MucHost = hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)),
ets:select(
muc_online_users,
ets:fun2ms(
@@ -296,9 +296,9 @@ import(_LServer, <<"muc_registered">>,
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
-init([Host, Opts]) ->
- MyHosts = proplists:get_value(hosts, Opts),
- case gen_mod:db_mod(Host, Opts, mod_muc) of
+init([_Host, Opts]) ->
+ MyHosts = mod_muc_opt:hosts(Opts),
+ case gen_mod:db_mod(Opts, mod_muc) of
?MODULE ->
ejabberd_mnesia:create(?MODULE, muc_room,
[{disc_copies, [node()]},
@@ -312,7 +312,7 @@ init([Host, Opts]) ->
_ ->
ok
end,
- case gen_mod:ram_db_mod(Host, Opts, mod_muc) of
+ case gen_mod:ram_db_mod(Opts, mod_muc) of
?MODULE ->
ejabberd_mnesia:create(?MODULE, muc_online_room,
[{ram_copies, [node()]},
@@ -382,11 +382,11 @@ clean_table_from_bad_node(Node, Host) ->
end,
mnesia:async_dirty(F).
-need_transform(#muc_room{name_host = {N, H}})
+need_transform({muc_room, {N, H}, _})
when is_list(N) orelse is_list(H) ->
?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []),
true;
-need_transform(#muc_registered{us_host = {{U, S}, H}, nick = Nick})
+need_transform({muc_registered, {{U, S}, H}, Nick})
when is_list(U) orelse is_list(S) orelse is_list(H) orelse is_list(Nick) ->
?INFO_MSG("Mnesia table 'muc_registered' will be converted to binary", []),
true;
diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl
new file mode 100644
index 000000000..67c42e98f
--- /dev/null
+++ b/src/mod_muc_opt.erl
@@ -0,0 +1,202 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_muc_opt).
+
+-export([access/1]).
+-export([access_admin/1]).
+-export([access_create/1]).
+-export([access_mam/1]).
+-export([access_persistent/1]).
+-export([access_register/1]).
+-export([db_type/1]).
+-export([default_room_options/1]).
+-export([history_size/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([max_room_desc/1]).
+-export([max_room_id/1]).
+-export([max_room_name/1]).
+-export([max_rooms_discoitems/1]).
+-export([max_user_conferences/1]).
+-export([max_users/1]).
+-export([max_users_admin_threshold/1]).
+-export([max_users_presence/1]).
+-export([min_message_interval/1]).
+-export([min_presence_interval/1]).
+-export([name/1]).
+-export([queue_type/1]).
+-export([ram_db_type/1]).
+-export([regexp_room_id/1]).
+-export([room_shaper/1]).
+-export([user_message_shaper/1]).
+-export([user_presence_shaper/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access).
+
+-spec access_admin(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+access_admin(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_admin, Opts);
+access_admin(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access_admin).
+
+-spec access_create(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_create(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_create, Opts);
+access_create(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access_create).
+
+-spec access_mam(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_mam(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_mam, Opts);
+access_mam(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access_mam).
+
+-spec access_persistent(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_persistent(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_persistent, Opts);
+access_persistent(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access_persistent).
+
+-spec access_register(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_register(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_register, Opts);
+access_register(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, access_register).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, db_type).
+
+-spec default_room_options(gen_mod:opts() | global | binary()) -> [{atom(),'anyone' | 'false' | 'moderators' | 'nobody' | 'true' | binary() | ['moderator' | 'participant' | 'visitor'] | pos_integer()}].
+default_room_options(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(default_room_options, Opts);
+default_room_options(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, default_room_options).
+
+-spec history_size(gen_mod:opts() | global | binary()) -> non_neg_integer().
+history_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(history_size, Opts);
+history_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, history_size).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, hosts).
+
+-spec max_room_desc(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_room_desc(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_room_desc, Opts);
+max_room_desc(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_room_desc).
+
+-spec max_room_id(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_room_id(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_room_id, Opts);
+max_room_id(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_room_id).
+
+-spec max_room_name(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_room_name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_room_name, Opts);
+max_room_name(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_room_name).
+
+-spec max_rooms_discoitems(gen_mod:opts() | global | binary()) -> non_neg_integer().
+max_rooms_discoitems(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_rooms_discoitems, Opts);
+max_rooms_discoitems(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_rooms_discoitems).
+
+-spec max_user_conferences(gen_mod:opts() | global | binary()) -> pos_integer().
+max_user_conferences(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_user_conferences, Opts);
+max_user_conferences(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_user_conferences).
+
+-spec max_users(gen_mod:opts() | global | binary()) -> pos_integer().
+max_users(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_users, Opts);
+max_users(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_users).
+
+-spec max_users_admin_threshold(gen_mod:opts() | global | binary()) -> pos_integer().
+max_users_admin_threshold(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_users_admin_threshold, Opts);
+max_users_admin_threshold(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_users_admin_threshold).
+
+-spec max_users_presence(gen_mod:opts() | global | binary()) -> integer().
+max_users_presence(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_users_presence, Opts);
+max_users_presence(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, max_users_presence).
+
+-spec min_message_interval(gen_mod:opts() | global | binary()) -> number().
+min_message_interval(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(min_message_interval, Opts);
+min_message_interval(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, min_message_interval).
+
+-spec min_presence_interval(gen_mod:opts() | global | binary()) -> number().
+min_presence_interval(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(min_presence_interval, Opts);
+min_presence_interval(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, min_presence_interval).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, name).
+
+-spec queue_type(gen_mod:opts() | global | binary()) -> 'file' | 'ram'.
+queue_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_type, Opts);
+queue_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, queue_type).
+
+-spec ram_db_type(gen_mod:opts() | global | binary()) -> atom().
+ram_db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ram_db_type, Opts);
+ram_db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, ram_db_type).
+
+-spec regexp_room_id(gen_mod:opts() | global | binary()) -> binary().
+regexp_room_id(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(regexp_room_id, Opts);
+regexp_room_id(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, regexp_room_id).
+
+-spec room_shaper(gen_mod:opts() | global | binary()) -> atom().
+room_shaper(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(room_shaper, Opts);
+room_shaper(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, room_shaper).
+
+-spec user_message_shaper(gen_mod:opts() | global | binary()) -> atom().
+user_message_shaper(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(user_message_shaper, Opts);
+user_message_shaper(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, user_message_shaper).
+
+-spec user_presence_shaper(gen_mod:opts() | global | binary()) -> atom().
+user_presence_shaper(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(user_presence_shaper, Opts);
+user_presence_shaper(Host) ->
+ gen_mod:get_module_opt(Host, mod_muc, user_presence_shaper).
+
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 67642a4de..e41b737f5 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -163,10 +163,7 @@ normal_state({route, <<"">>,
true when Type == groupchat ->
Activity = get_user_activity(From, StateData),
Now = erlang:system_time(microsecond),
- MinMessageInterval = trunc(gen_mod:get_module_opt(
- StateData#state.server_host,
- mod_muc, min_message_interval)
- * 1000000),
+ MinMessageInterval = trunc(mod_muc_opt:min_message_interval(StateData#state.server_host) * 1000000),
Size = element_size(Packet),
{MessageShaper, MessageShaperInterval} =
ejabberd_shaper:update(Activity#activity.message_shaper, Size),
@@ -347,9 +344,7 @@ normal_state({route, Nick, #presence{from = From} = Packet}, StateData) ->
Activity = get_user_activity(From, StateData),
Now = erlang:system_time(microsecond),
MinPresenceInterval =
- trunc(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, min_presence_interval)
- * 1000000),
+ trunc(mod_muc_opt:min_presence_interval(StateData#state.server_host) * 1000000),
if (Now >= Activity#activity.presence_time + MinPresenceInterval)
and (Activity#activity.presence == undefined) ->
NewActivity = Activity#activity{presence_time = Now},
@@ -415,8 +410,10 @@ normal_state({route, ToNick,
PrivMsg = xmpp:set_from(
xmpp:set_subtag(Packet, X),
FromNickJID),
- [ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID))
- || ToJID <- ToJIDs];
+ lists:foreach(
+ fun(ToJID) ->
+ ejabberd_router:route(xmpp:set_to(PrivMsg, ToJID))
+ end, ToJIDs);
true ->
ErrText = <<"It is not allowed to send private messages">>,
Err = xmpp:err_forbidden(ErrText, Lang),
@@ -493,9 +490,7 @@ handle_event({service_message, Msg}, _StateName,
{next_state, normal_state, NSD};
handle_event({destroy, Reason}, _StateName,
StateData) ->
- {result, undefined, stop} =
- destroy_room(#muc_destroy{xmlns = ?NS_MUC_OWNER, reason = Reason},
- StateData),
+ _ = destroy_room(#muc_destroy{xmlns = ?NS_MUC_OWNER, reason = Reason}, StateData),
?INFO_MSG("Destroyed MUC room ~s with reason: ~p",
[jid:encode(StateData#state.jid), Reason]),
add_to_log(room_existence, destroyed, StateData),
@@ -693,8 +688,7 @@ handle_info({iq_reply, timeout, IQ}, StateName, StateData) ->
ejabberd_router:route_error(IQ, Err),
{next_state, StateName, StateData};
handle_info(config_reloaded, StateName, StateData) ->
- Max = gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, history_size),
+ Max = mod_muc_opt:history_size(StateData#state.server_host),
History1 = StateData#state.history,
Q1 = History1#lqueue.queue,
Q2 = case p1_queue:len(Q1) of
@@ -1377,7 +1371,7 @@ get_affiliation(#jid{} = JID, StateData) ->
get_affiliation(LJID, StateData) ->
get_affiliation(jid:make(LJID), StateData).
--spec do_get_affiliation(jid(), state()) -> affiliation().
+-spec do_get_affiliation(jid(), state()) -> affiliation() | {affiliation(), binary()}.
do_get_affiliation(JID, #state{config = #config{persistent = false}} = StateData) ->
do_get_affiliation_fallback(JID, StateData);
do_get_affiliation(JID, StateData) ->
@@ -1394,7 +1388,7 @@ do_get_affiliation(JID, StateData) ->
Affiliation
end.
--spec do_get_affiliation_fallback(jid(), state()) -> affiliation().
+-spec do_get_affiliation_fallback(jid(), state()) -> affiliation() | {affiliation(), binary()}.
do_get_affiliation_fallback(JID, StateData) ->
LJID = jid:tolower(JID),
try maps:get(LJID, StateData#state.affiliations)
@@ -1534,29 +1528,19 @@ get_max_users(StateData) ->
-spec get_service_max_users(state()) -> pos_integer().
get_service_max_users(StateData) ->
- gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, max_users).
+ mod_muc_opt:max_users(StateData#state.server_host).
-spec get_max_users_admin_threshold(state()) -> pos_integer().
get_max_users_admin_threshold(StateData) ->
- gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, max_users_admin_threshold).
+ mod_muc_opt:max_users_admin_threshold(StateData#state.server_host).
-spec room_queue_new(binary(), ejabberd_shaper:shaper(), _) -> p1_queue:queue().
room_queue_new(ServerHost, Shaper, QueueType) ->
HaveRoomShaper = Shaper /= none,
- HaveMessageShaper = gen_mod:get_module_opt(
- ServerHost, mod_muc,
- user_message_shaper) /= none,
- HavePresenceShaper = gen_mod:get_module_opt(
- ServerHost, mod_muc,
- user_presence_shaper) /= none,
- HaveMinMessageInterval = gen_mod:get_module_opt(
- ServerHost, mod_muc,
- min_message_interval) /= 0,
- HaveMinPresenceInterval = gen_mod:get_module_opt(
- ServerHost, mod_muc,
- min_presence_interval) /= 0,
+ HaveMessageShaper = mod_muc_opt:user_message_shaper(ServerHost) /= none,
+ HavePresenceShaper = mod_muc_opt:user_presence_shaper(ServerHost) /= none,
+ HaveMinMessageInterval = mod_muc_opt:min_message_interval(ServerHost) /= 0,
+ HaveMinPresenceInterval = mod_muc_opt:min_presence_interval(ServerHost) /= 0,
if HaveRoomShaper or HaveMessageShaper or HavePresenceShaper
or HaveMinMessageInterval or HaveMinPresenceInterval ->
p1_queue:new(QueueType);
@@ -1572,11 +1556,9 @@ get_user_activity(JID, StateData) ->
{ok, _P, A} -> A;
error ->
MessageShaper =
- ejabberd_shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, user_message_shaper)),
+ ejabberd_shaper:new(mod_muc_opt:user_message_shaper(StateData#state.server_host)),
PresenceShaper =
- ejabberd_shaper:new(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, user_presence_shaper)),
+ ejabberd_shaper:new(mod_muc_opt:user_presence_shaper(StateData#state.server_host)),
#activity{message_shaper = MessageShaper,
presence_shaper = PresenceShaper}
end.
@@ -1584,13 +1566,9 @@ get_user_activity(JID, StateData) ->
-spec store_user_activity(jid(), #activity{}, state()) -> state().
store_user_activity(JID, UserActivity, StateData) ->
MinMessageInterval =
- trunc(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, min_message_interval)
- * 1000),
+ trunc(mod_muc_opt:min_message_interval(StateData#state.server_host) * 1000),
MinPresenceInterval =
- trunc(gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, min_presence_interval)
- * 1000),
+ trunc(mod_muc_opt:min_presence_interval(StateData#state.server_host) * 1000),
Key = jid:tolower(JID),
Now = erlang:system_time(microsecond),
Activity1 = clean_treap(StateData#state.activity,
@@ -1710,12 +1688,7 @@ update_online_user(JID, #user{nick = Nick} = User, StateData) ->
set_subscriber(JID, Nick, Nodes,
#state{room = Room, host = Host, server_host = ServerHost} = StateData) ->
- BareJID = case JID of
- #jid{} -> jid:remove_resource(JID);
- _ ->
- ?ERROR_MSG("Invalid subscriber JID in set_subscriber ~p", [JID]),
- jid:remove_resource(jid:make(JID))
- end,
+ BareJID = jid:remove_resource(JID),
LBareJID = jid:tolower(BareJID),
Subscribers = maps:put(LBareJID,
#subscriber{jid = BareJID,
@@ -1893,8 +1866,7 @@ add_new_user(From, Nick, Packet, StateData) ->
StateData),
NConferences = tab_count_user(From, StateData),
MaxConferences =
- gen_mod:get_module_opt(StateData#state.server_host,
- mod_muc, max_user_conferences),
+ mod_muc_opt:max_user_conferences(StateData#state.server_host),
Collision = nick_collision(From, Nick, StateData),
IsSubscribeRequest = not is_record(Packet, presence),
case {(ServiceAffiliation == owner orelse
@@ -2132,7 +2104,7 @@ extract_password(#iq{} = IQ) ->
false
end.
--spec get_history(binary(), stanza(), state()) -> lqueue().
+-spec get_history(binary(), stanza(), state()) -> [lqueue_elem()].
get_history(Nick, Packet, #state{history = History}) ->
case xmpp:get_subtag(Packet, #muc{}) of
#muc{history = #muc_history{} = MUCHistory} ->
@@ -2144,7 +2116,7 @@ get_history(Nick, Packet, #state{history = History}) ->
end.
-spec filter_history(p1_queue:queue(), erlang:timestamp(),
- binary(), muc_history()) -> list().
+ binary(), muc_history()) -> [lqueue_elem()].
filter_history(Queue, Now, Nick,
#muc_history{since = Since,
seconds = Seconds,
@@ -2169,9 +2141,7 @@ filter_history(Queue, Now, Nick,
-spec is_room_overcrowded(state()) -> boolean().
is_room_overcrowded(StateData) ->
- MaxUsersPresence = gen_mod:get_module_opt(
- StateData#state.server_host,
- mod_muc, max_users_presence),
+ MaxUsersPresence = mod_muc_opt:max_users_presence(StateData#state.server_host),
maps:size(StateData#state.users) > MaxUsersPresence.
-spec presence_broadcast_allowed(jid(), state()) -> boolean().
@@ -2527,7 +2497,7 @@ status_codes(_IsInitialPresence, _IsSelfPresence = false, _StateData) -> [].
lqueue_new(Max, Type) ->
#lqueue{queue = p1_queue:new(Type), max = Max}.
--spec lqueue_in(term(), lqueue()) -> lqueue().
+-spec lqueue_in(lqueue_elem(), lqueue()) -> lqueue().
%% If the message queue limit is set to 0, do not store messages.
lqueue_in(_Item, LQ = #lqueue{max = 0}) -> LQ;
%% Otherwise, rotate messages in the queue store.
@@ -2575,7 +2545,7 @@ add_message_to_history(FromNick, FromJID, Packet, StateData) ->
StateData
end.
--spec send_history(jid(), list(), state()) -> ok.
+-spec send_history(jid(), [lqueue_elem()], state()) -> ok.
send_history(JID, History, StateData) ->
lists:foreach(
fun({Nick, Packet, _HaveSubject, _TimeStamp, _Size}) ->
@@ -3155,6 +3125,7 @@ get_actor_nick(MJID, StateData) ->
catch _:{badkey, _} -> <<"">>
end.
+-spec convert_legacy_fields([xdata_field()]) -> [xdata_field()].
convert_legacy_fields(Fs) ->
lists:map(
fun(#xdata_field{var = Var} = F) ->
@@ -3300,12 +3271,8 @@ is_allowed_mam_change(Options, StateData, From) ->
is_allowed_room_name_desc_limits(Options, StateData) ->
RoomName = proplists:get_value(roomname, Options, <<"">>),
RoomDesc = proplists:get_value(roomdesc, Options, <<"">>),
- MaxRoomName = gen_mod:get_module_opt(
- StateData#state.server_host,
- mod_muc, max_room_name),
- MaxRoomDesc = gen_mod:get_module_opt(
- StateData#state.server_host,
- mod_muc, max_room_desc),
+ MaxRoomName = mod_muc_opt:max_room_name(StateData#state.server_host),
+ MaxRoomDesc = mod_muc_opt:max_room_desc(StateData#state.server_host),
(byte_size(RoomName) =< MaxRoomName)
andalso (byte_size(RoomDesc) =< MaxRoomDesc).
@@ -3329,8 +3296,7 @@ is_password_settings_correct(Options, StateData) ->
-spec get_default_room_maxusers(state()) -> non_neg_integer().
get_default_room_maxusers(RoomState) ->
DefRoomOpts =
- gen_mod:get_module_opt(RoomState#state.server_host,
- mod_muc, default_room_options),
+ mod_muc_opt:default_room_options(RoomState#state.server_host),
RoomState2 = set_opts(DefRoomOpts, RoomState),
(RoomState2#state.config)#config.max_users.
@@ -3428,6 +3394,7 @@ set_config(Options, StateData, Lang) ->
Err
end.
+-spec get_config_opt_name(pos_integer()) -> atom().
get_config_opt_name(Pos) ->
Fs = [config|record_info(fields, config)],
lists:nth(Pos, Fs).
@@ -3736,6 +3703,7 @@ set_opts([{Opt, Val} | Opts], StateData) ->
end,
set_opts(Opts, NSD).
+-spec set_vcard_xupdate(state()) -> state().
set_vcard_xupdate(#state{config =
#config{vcard = VCardRaw,
vcard_xupdate = undefined} = Config} = State)
@@ -4470,7 +4438,7 @@ send_wrapped(From, To, Packet, Node, State) ->
case lists:member(Node, Nodes) of
true ->
MamEnabled = (State#state.config)#config.mam,
- Id = case xmpp:get_subtag(Packet, #stanza_id{}) of
+ Id = case xmpp:get_subtag(Packet, #stanza_id{by = #jid{}}) of
#stanza_id{id = Id2} ->
Id2;
_ ->
diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl
index f041257f8..54e0315df 100644
--- a/src/mod_muc_sql.erl
+++ b/src/mod_muc_sql.erl
@@ -24,7 +24,6 @@
-module(mod_muc_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_muc).
-behaviour(mod_muc_room).
@@ -50,7 +49,7 @@
%%% API
%%%===================================================================
init(Host, Opts) ->
- case gen_mod:ram_db_mod(Host, Opts, mod_muc) of
+ case gen_mod:ram_db_mod(Opts, mod_muc) of
?MODULE ->
clean_tables(Host);
_ ->
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index 509fe8893..e8513bac2 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -52,8 +52,8 @@
ts :: integer()}).
-record(dest, {jid_string :: binary() | none,
- jid_jid :: jid(),
- type :: to | cc | bcc,
+ jid_jid :: jid() | undefined,
+ type :: bcc | cc | noreply | ofrom | replyroom | replyto | to,
address :: address()}).
-type limit_value() :: {default | custom, integer()}.
@@ -67,9 +67,9 @@
-record(group, {server :: binary(),
dests :: [#dest{}],
- multicast :: routing(),
- others :: [#address{}],
- addresses :: [#address{}]}).
+ multicast :: routing() | undefined,
+ others :: [address()],
+ addresses :: [address()]}).
-record(state, {lserver :: binary(),
lservice :: binary(),
@@ -153,9 +153,9 @@ user_send_packet(Acc) ->
-spec init(list()) -> {ok, state()}.
init([LServerS, Opts]) ->
process_flag(trap_exit, true),
- [LServiceS|_] = gen_mod:get_opt_hosts(LServerS, Opts),
- Access = gen_mod:get_opt(access, Opts),
- SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts)),
+ [LServiceS|_] = gen_mod:get_opt_hosts(Opts),
+ Access = mod_multicast_opt:access(Opts),
+ SLimits = build_service_limit_record(mod_multicast_opt:limits(Opts)),
create_cache(),
try_start_loop(),
ejabberd_router_multicast:register_route(LServerS),
@@ -171,9 +171,9 @@ handle_call(stop, _From, State) ->
handle_cast({reload, NewOpts, NewOpts},
#state{lserver = LServerS, lservice = OldLServiceS} = State) ->
- Access = gen_mod:get_opt(access, NewOpts),
- SLimits = build_service_limit_record(gen_mod:get_opt(limits, NewOpts)),
- [NewLServiceS|_] = gen_mod:get_opt_hosts(LServerS, NewOpts),
+ Access = mod_multicast_opt:access(NewOpts),
+ SLimits = build_service_limit_record(mod_multicast_opt:limits(NewOpts)),
+ [NewLServiceS|_] = gen_mod:get_opt_hosts(NewOpts),
if NewLServiceS /= OldLServiceS ->
ejabberd_router:register_route(NewLServiceS, LServerS),
ejabberd_router:unregister_route(OldLServiceS);
@@ -283,7 +283,7 @@ process_iq(_, _) ->
-define(FEATURE(Feat), Feat).
iq_disco_info(From, Lang, State) ->
- Name = gen_mod:get_module_opt(State#state.lserver, ?MODULE, name),
+ Name = mod_multicast_opt:name(State#state.lserver),
#disco_info{
identities = [#identity{category = <<"service">>,
type = <<"multicast">>,
@@ -459,8 +459,9 @@ check_limit_dests(SLimits, FromJID, Packet,
-spec convert_dest_record([address()]) -> [#dest{}].
convert_dest_record(Addrs) ->
lists:map(
- fun(#address{jid = undefined} = Addr) ->
- #dest{jid_string = none, address = Addr};
+ fun(#address{jid = undefined, type = Type} = Addr) ->
+ #dest{jid_string = none,
+ type = Type, address = Addr};
(#address{jid = JID, type = Type} = Addr) ->
#dest{jid_string = jid:encode(JID), jid_jid = JID,
type = Type, address = Addr}
@@ -502,7 +503,8 @@ group_dests(Dests) ->
end,
dict:new(), Dests),
Keys = dict:fetch_keys(D),
- [#group{server = Key, dests = dict:fetch(Key, D)}
+ [#group{server = Key, dests = dict:fetch(Key, D),
+ addresses = [], others = []}
|| Key <- Keys].
%%%-------------------------
@@ -1074,7 +1076,7 @@ iq_disco_info_extras(From, State) ->
end.
sender_type(From) ->
- Local_hosts = ejabberd_config:get_myhosts(),
+ Local_hosts = ejabberd_option:hosts(),
case lists:member(From#jid.lserver, Local_hosts) of
true -> local;
false -> remote
@@ -1124,23 +1126,27 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
-mod_opt_type(name) -> fun iolist_to_binary/1;
-mod_opt_type({limits, Type}) when (Type == local) or (Type == remote) ->
- fun(L) ->
- lists:map(
- fun ({message, infinite} = O) -> O;
- ({presence, infinite} = O) -> O;
- ({message, I} = O) when is_integer(I) -> O;
- ({presence, I} = O) when is_integer(I) -> O
- end, L)
- end.
-
-mod_options(_Host) ->
+ econf:acl();
+mod_opt_type(name) ->
+ econf:binary();
+mod_opt_type(limits) ->
+ econf:options(
+ #{local =>
+ econf:options(
+ #{message => econf:non_neg_int(infinite),
+ presence => econf:non_neg_int(infinite)}),
+ remote =>
+ econf:options(
+ #{message => econf:non_neg_int(infinite),
+ presence => econf:non_neg_int(infinite)})});
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE).
+
+mod_options(Host) ->
[{access, all},
- {host, <<"multicast.@HOST@">>},
+ {host, <<"multicast.", Host/binary>>},
{hosts, []},
{limits, [{local, []}, {remote, []}]},
{name, ?T("Multicast")}].
diff --git a/src/mod_multicast_opt.erl b/src/mod_multicast_opt.erl
new file mode 100644
index 000000000..f149d1ddc
--- /dev/null
+++ b/src/mod_multicast_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_multicast_opt).
+
+-export([access/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([limits/1]).
+-export([name/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, access).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, hosts).
+
+-spec limits(gen_mod:opts() | global | binary()) -> [{'local',[{'message','infinite' | non_neg_integer()} | {'presence','infinite' | non_neg_integer()}]} | {'remote',[{'message','infinite' | non_neg_integer()} | {'presence','infinite' | non_neg_integer()}]}].
+limits(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(limits, Opts);
+limits(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, limits).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_multicast, name).
+
diff --git a/src/mod_offline.erl b/src/mod_offline.erl
index 76682d06c..1c1b22663 100644
--- a/src/mod_offline.erl
+++ b/src/mod_offline.erl
@@ -109,7 +109,7 @@ depends(_Host, _Opts) ->
[].
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Opts),
ejabberd_hooks:add(offline_message_hook, Host, ?MODULE,
@@ -159,8 +159,8 @@ stop(Host) ->
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_FLEX_OFFLINE).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
init_cache(NewOpts),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
@@ -169,10 +169,10 @@ reload(Host, NewOpts, OldOpts) ->
end.
init_cache(Opts) ->
- case gen_mod:get_opt(use_mam_for_storage, Opts) of
+ case mod_offline_opt:use_mam_for_storage(Opts) of
true ->
- MaxSize = gen_mod:get_opt(cache_size, Opts),
- LifeTime = case gen_mod:get_opt(cache_life_time, Opts) of
+ MaxSize = mod_offline_opt:cache_size(Opts),
+ LifeTime = case mod_offline_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -218,8 +218,8 @@ store_offline_msg(#offline_msg{us = {User, Server}, packet = Pkt} = Msg) ->
end.
get_max_user_messages(User, Server) ->
- Access = gen_mod:get_module_opt(Server, ?MODULE, access_max_user_messages),
- case acl:match_rule(Server, Access, jid:make(User, Server)) of
+ Access = mod_offline_opt:access_max_user_messages(Server),
+ case ejabberd_shaper:match(Server, Access, jid:make(User, Server)) of
Max when is_integer(Max) -> Max;
infinity -> infinity;
_ -> ?MAX_USER_MESSAGES
@@ -432,15 +432,13 @@ need_to_store(LServer, #message{type = Type} = Packet) ->
none ->
Store = case Type of
groupchat ->
- gen_mod:get_module_opt(
- LServer, ?MODULE, store_groupchat);
+ mod_offline_opt:store_groupchat(LServer);
headline ->
false;
_ ->
true
end,
- case {Store, gen_mod:get_module_opt(
- LServer, ?MODULE, store_empty_body)} of
+ case {Store, mod_offline_opt:store_empty_body(LServer)} of
{false, _} ->
false;
{_, true} ->
@@ -648,11 +646,11 @@ remove_user(User, Server) ->
check_if_message_should_be_bounced(Packet) ->
case Packet of
#message{type = groupchat, to = #jid{lserver = LServer}} ->
- gen_mod:get_module_opt(LServer, ?MODULE, bounce_groupchat);
+ mod_offline_opt:bounce_groupchat(LServer);
#message{to = #jid{lserver = LServer}} ->
case misc:is_mucsub_message(Packet) of
true ->
- gen_mod:get_module_opt(LServer, ?MODULE, bounce_groupchat);
+ mod_offline_opt:bounce_groupchat(LServer);
_ ->
true
end;
@@ -745,7 +743,7 @@ parse_marker_messages(LServer, ReadMsgs) ->
{Timestamp, ExtraMsgs} = lists:foldl(
fun({_Node, #message{id = <<"ActivityMarker">>,
body = [], type = error} = Msg}, {T, E}) ->
- case xmpp:get_subtag(Msg, #delay{}) of
+ case xmpp:get_subtag(Msg, #delay{stamp = {0,0,0}}) of
#delay{stamp = Time} ->
if T == none orelse T > Time ->
{Time, E};
@@ -760,7 +758,7 @@ parse_marker_messages(LServer, ReadMsgs) ->
body = [], type = error} = Msg ->
TS2 = case TS of
undefined ->
- case xmpp:get_subtag(Msg, #delay{}) of
+ case xmpp:get_subtag(Msg, #delay{stamp = {0,0,0}}) of
#delay{stamp = TS0} ->
TS0;
_ ->
@@ -785,7 +783,7 @@ parse_marker_messages(LServer, ReadMsgs) ->
end, {none, []}, ReadMsgs),
Start = case {Timestamp, ExtraMsgs} of
{none, [First|_]} ->
- case xmpp:get_subtag(First, #delay{}) of
+ case xmpp:get_subtag(First, #delay{stamp = {0,0,0}}) of
#delay{stamp = {Mega, Sec, Micro}} ->
{Mega, Sec, Micro+1};
_ ->
@@ -807,9 +805,10 @@ read_mam_messages(LUser, LServer, ReadMsgs) ->
ExtraMsgs;
_ ->
MaxOfflineMsgs = case get_max_user_messages(LUser, LServer) of
- Number when is_integer(Number) -> Number - length(ExtraMsgs);
- infinity -> undefined;
- _ -> 100 - length(ExtraMsgs)
+ Number when is_integer(Number) ->
+ max(0, Number - length(ExtraMsgs));
+ infinity ->
+ undefined
end,
JID = jid:make(LUser, LServer, <<>>),
{MamMsgs, _, _} = mod_mam:select(LServer, JID, JID,
@@ -826,20 +825,20 @@ read_mam_messages(LUser, LServer, ReadMsgs) ->
end,
AllMsgs2 = lists:sort(
fun(A, B) ->
- DA = case xmpp:get_subtag(A, #stanza_id{}) of
+ DA = case xmpp:get_subtag(A, #stanza_id{by = #jid{}}) of
#stanza_id{id = IDA} ->
IDA;
- _ -> case xmpp:get_subtag(A, #delay{}) of
+ _ -> case xmpp:get_subtag(A, #delay{stamp = {0,0,0}}) of
#delay{stamp = STA} ->
integer_to_binary(misc:now_to_usec(STA));
_ ->
<<"unknown">>
end
end,
- DB = case xmpp:get_subtag(B, #stanza_id{}) of
+ DB = case xmpp:get_subtag(B, #stanza_id{by = #jid{}}) of
#stanza_id{id = IDB} ->
IDB;
- _ -> case xmpp:get_subtag(B, #delay{}) of
+ _ -> case xmpp:get_subtag(B, #delay{stamp = {0,0,0}}) of
#delay{stamp = STB} ->
integer_to_binary(misc:now_to_usec(STB));
_ ->
@@ -864,8 +863,7 @@ count_mam_messages(LUser, LServer, ReadMsgs) ->
_ ->
MaxOfflineMsgs = case get_max_user_messages(LUser, LServer) of
Number when is_integer(Number) -> Number - length(ExtraMsgs);
- infinity -> undefined;
- _ -> 100 - length(ExtraMsgs)
+ infinity -> undefined
end,
JID = jid:make(LUser, LServer, <<>>),
{_, _, Count} = mod_mam:select(LServer, JID, JID,
@@ -915,19 +913,14 @@ user_queue(User, Server, Query, Lang) ->
LServer = jid:nameprep(Server),
US = {LUser, LServer},
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Res = user_queue_parse_query(LUser, LServer, Query),
+ user_queue_parse_query(LUser, LServer, Query),
HdrsAll = Mod:read_message_headers(LUser, LServer),
Hdrs = get_messages_subset(User, Server, HdrsAll),
FMsgs = format_user_queue(Hdrs),
[?XC(<<"h1">>,
(str:format(?T(<<"~s's Offline Messages Queue">>),
[us_to_list(US)])))]
- ++
- case Res of
- ok -> [?XREST(<<"Submitted">>)];
- nothing -> []
- end
- ++
+ ++ [?XREST(<<"Submitted">>)] ++
[?XAE(<<"form">>,
[{<<"action">>, <<"">>}, {<<"method">>, <<"post">>}],
[?XE(<<"table">>,
@@ -954,7 +947,7 @@ user_queue_parse_query(LUser, LServer, Query) ->
{value, _} ->
user_queue_parse_query(LUser, LServer, Query, Mod);
_ ->
- nothing
+ ok
end.
user_queue_parse_query(LUser, LServer, Query, Mod) ->
@@ -964,11 +957,11 @@ user_queue_parse_query(LUser, LServer, Query, Mod) ->
I when is_integer(I), I>=0 ->
Mod:remove_message(LUser, LServer, I);
_ ->
- nothing
+ ok
end,
user_queue_parse_query(LUser, LServer, Query2, Mod);
false ->
- nothing
+ ok
end.
us_to_list({User, Server}) ->
@@ -1099,26 +1092,26 @@ import(LServer, {sql, _}, DBType, <<"spool">>,
Mod:import(OffMsg).
use_mam_for_user(_User, Server) ->
- gen_mod:get_module_opt(Server, ?MODULE, use_mam_for_storage).
+ mod_offline_opt:use_mam_for_storage(Server).
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;
+ econf:shaper();
mod_opt_type(store_groupchat) ->
- fun(V) when is_boolean(V) -> V end;
+ econf:bool();
mod_opt_type(bounce_groupchat) ->
- fun(V) when is_boolean(V) -> V end;
+ econf:bool();
mod_opt_type(use_mam_for_storage) ->
- fun(V) when is_boolean(V) -> V end;
+ econf:bool();
mod_opt_type(store_empty_body) ->
- fun (V) when is_boolean(V) -> V;
- (unless_chat_state) -> unless_chat_state
- 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.
-
+ econf:either(
+ unless_chat_state,
+ econf:bool());
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)},
@@ -1127,5 +1120,5 @@ mod_options(Host) ->
{use_mam_for_storage, false},
{bounce_groupchat, false},
{store_groupchat, false},
- {cache_size, ejabberd_config:cache_size(Host)},
- {cache_life_time, ejabberd_config:cache_life_time(Host)}].
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_offline_mnesia.erl b/src/mod_offline_mnesia.erl
index 2356bbf03..d82735dee 100644
--- a/src/mod_offline_mnesia.erl
+++ b/src/mod_offline_mnesia.erl
@@ -164,7 +164,8 @@ count_messages(LUser, LServer) ->
import(#offline_msg{} = Msg) ->
mnesia:dirty_write(Msg).
-need_transform(#offline_msg{us = {U, S}}) when is_list(U) orelse is_list(S) ->
+need_transform({offline_msg, {U, S}, _, _, _, _, _})
+ when is_list(U) orelse is_list(S) ->
?INFO_MSG("Mnesia table 'offline_msg' will be converted to binary", []),
true;
need_transform(_) ->
diff --git a/src/mod_offline_opt.erl b/src/mod_offline_opt.erl
new file mode 100644
index 000000000..bb5eac6d9
--- /dev/null
+++ b/src/mod_offline_opt.erl
@@ -0,0 +1,62 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_offline_opt).
+
+-export([access_max_user_messages/1]).
+-export([bounce_groupchat/1]).
+-export([cache_life_time/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([store_empty_body/1]).
+-export([store_groupchat/1]).
+-export([use_mam_for_storage/1]).
+
+-spec access_max_user_messages(gen_mod:opts() | global | binary()) -> atom() | [ejabberd_shaper:shaper_rule()].
+access_max_user_messages(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_max_user_messages, Opts);
+access_max_user_messages(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, access_max_user_messages).
+
+-spec bounce_groupchat(gen_mod:opts() | global | binary()) -> boolean().
+bounce_groupchat(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(bounce_groupchat, Opts);
+bounce_groupchat(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, bounce_groupchat).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, cache_life_time).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, db_type).
+
+-spec store_empty_body(gen_mod:opts() | global | binary()) -> 'false' | 'true' | 'unless_chat_state'.
+store_empty_body(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(store_empty_body, Opts);
+store_empty_body(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, store_empty_body).
+
+-spec store_groupchat(gen_mod:opts() | global | binary()) -> boolean().
+store_groupchat(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(store_groupchat, Opts);
+store_groupchat(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, store_groupchat).
+
+-spec use_mam_for_storage(gen_mod:opts() | global | binary()) -> boolean().
+use_mam_for_storage(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_mam_for_storage, Opts);
+use_mam_for_storage(Host) ->
+ gen_mod:get_module_opt(Host, mod_offline, use_mam_for_storage).
+
diff --git a/src/mod_offline_sql.erl b/src/mod_offline_sql.erl
index 972316954..fe2ae42a4 100644
--- a/src/mod_offline_sql.erl
+++ b/src/mod_offline_sql.erl
@@ -24,7 +24,6 @@
-module(mod_offline_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_offline).
diff --git a/src/mod_ping.erl b/src/mod_ping.erl
index fd3a08909..aeb3aedbd 100644
--- a/src/mod_ping.erl
+++ b/src/mod_ping.erl
@@ -189,10 +189,10 @@ user_send({Packet, #{jid := JID} = C2SState}) ->
%% Internal functions
%%====================================================================
init_state(Host, Opts) ->
- SendPings = gen_mod:get_opt(send_pings, Opts),
- PingInterval = gen_mod:get_opt(ping_interval, Opts),
- PingAckTimeout = gen_mod:get_opt(ping_ack_timeout, Opts),
- TimeoutAction = gen_mod:get_opt(timeout_action, Opts),
+ SendPings = mod_ping_opt:send_pings(Opts),
+ PingInterval = mod_ping_opt:ping_interval(Opts),
+ PingAckTimeout = mod_ping_opt:ping_ack_timeout(Opts),
+ TimeoutAction = mod_ping_opt:timeout_action(Opts),
#state{host = Host,
send_pings = SendPings,
ping_interval = PingInterval,
@@ -253,17 +253,13 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(ping_interval) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(ping_ack_timeout) ->
- fun(undefined) -> undefined;
- (I) when is_integer(I), I>0 -> timer:seconds(I)
- end;
+ econf:timeout(second);
mod_opt_type(send_pings) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(timeout_action) ->
- fun (none) -> none;
- (kill) -> kill
- end.
+ econf:enum([none, kill]).
mod_options(_Host) ->
[{ping_interval, 60},
diff --git a/src/mod_ping_opt.erl b/src/mod_ping_opt.erl
new file mode 100644
index 000000000..fd0052130
--- /dev/null
+++ b/src/mod_ping_opt.erl
@@ -0,0 +1,34 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_ping_opt).
+
+-export([ping_ack_timeout/1]).
+-export([ping_interval/1]).
+-export([send_pings/1]).
+-export([timeout_action/1]).
+
+-spec ping_ack_timeout(gen_mod:opts() | global | binary()) -> 'undefined' | pos_integer().
+ping_ack_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ping_ack_timeout, Opts);
+ping_ack_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_ping, ping_ack_timeout).
+
+-spec ping_interval(gen_mod:opts() | global | binary()) -> pos_integer().
+ping_interval(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ping_interval, Opts);
+ping_interval(Host) ->
+ gen_mod:get_module_opt(Host, mod_ping, ping_interval).
+
+-spec send_pings(gen_mod:opts() | global | binary()) -> boolean().
+send_pings(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(send_pings, Opts);
+send_pings(Host) ->
+ gen_mod:get_module_opt(Host, mod_ping, send_pings).
+
+-spec timeout_action(gen_mod:opts() | global | binary()) -> 'kill' | 'none'.
+timeout_action(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(timeout_action, Opts);
+timeout_action(Host) ->
+ gen_mod:get_module_opt(Host, mod_ping, timeout_action).
+
diff --git a/src/mod_pres_counter.erl b/src/mod_pres_counter.erl
index 6c1290469..9c50eb104 100644
--- a/src/mod_pres_counter.erl
+++ b/src/mod_pres_counter.erl
@@ -78,8 +78,8 @@ check_packet(Acc, _, _, _) ->
Acc.
update(Server, JID, Dir) ->
- StormCount = gen_mod:get_module_opt(Server, ?MODULE, count),
- TimeInterval = gen_mod:get_module_opt(Server, ?MODULE, interval),
+ StormCount = mod_pres_counter_opt:count(Server),
+ TimeInterval = mod_pres_counter_opt:interval(Server),
TimeStamp = erlang:system_time(second),
case read(Dir) of
undefined ->
@@ -123,9 +123,9 @@ read(K) -> get({pres_counter, K}).
write(K, V) -> put({pres_counter, K}, V).
mod_opt_type(count) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(interval) ->
- fun (I) when is_integer(I), I > 0 -> I end.
+ econf:pos_int().
mod_options(_) ->
[{count, 5}, {interval, 60}].
diff --git a/src/mod_pres_counter_opt.erl b/src/mod_pres_counter_opt.erl
new file mode 100644
index 000000000..7964fe368
--- /dev/null
+++ b/src/mod_pres_counter_opt.erl
@@ -0,0 +1,20 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_pres_counter_opt).
+
+-export([count/1]).
+-export([interval/1]).
+
+-spec count(gen_mod:opts() | global | binary()) -> pos_integer().
+count(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(count, Opts);
+count(Host) ->
+ gen_mod:get_module_opt(Host, mod_pres_counter, count).
+
+-spec interval(gen_mod:opts() | global | binary()) -> pos_integer().
+interval(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(interval, Opts);
+interval(Host) ->
+ gen_mod:get_module_opt(Host, mod_pres_counter, interval).
+
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index a25d30515..988333f4f 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -57,7 +57,7 @@
ok | {error, notfound | conflict | any()}.
-callback remove_lists(binary(), binary()) -> ok | {error, any()}.
-callback set_lists(#privacy{}) -> ok | {error, any()}.
--callback set_list(binary(), binary(), binary(), listitem()) ->
+-callback set_list(binary(), binary(), binary(), [listitem()]) ->
ok | {error, any()}.
-callback get_list(binary(), binary(), binary() | default) ->
{ok, {binary(), [listitem()]}} | error | {error, any()}.
@@ -69,7 +69,7 @@
-optional_callbacks([use_cache/1, cache_nodes/1]).
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
@@ -100,8 +100,8 @@ stop(Host) ->
?NS_PRIVACY).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -704,9 +704,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_privacy_opt:cache_size(Opts),
+ CacheMissed = mod_privacy_opt:cache_missed(Opts),
+ LifeTime = case mod_privacy_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -716,7 +716,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_privacy_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -848,17 +848,20 @@ export(LServer) ->
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_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl
index 1be9912fb..be50894e0 100644
--- a/src/mod_privacy_mnesia.erl
+++ b/src/mod_privacy_mnesia.erl
@@ -47,7 +47,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(privacy, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_privacy, use_cache);
+ mod_privacy_opt:use_cache(Host);
_ ->
false
end.
@@ -143,7 +143,7 @@ remove_lists(LUser, LServer) ->
import(#privacy{} = P) ->
mnesia:dirty_write(P).
-need_transform(#privacy{us = {U, S}}) when is_list(U) orelse is_list(S) ->
+need_transform({privacy, {U, S}, _, _}) when is_list(U) orelse is_list(S) ->
?INFO_MSG("Mnesia table 'privacy' will be converted to binary", []),
true;
need_transform(_) ->
diff --git a/src/mod_privacy_opt.erl b/src/mod_privacy_opt.erl
new file mode 100644
index 000000000..acc0f2ac9
--- /dev/null
+++ b/src/mod_privacy_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_privacy_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_privacy, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_privacy, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_privacy, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_privacy, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_privacy, use_cache).
+
diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl
index 92c9f6c60..ea3a86919 100644
--- a/src/mod_privacy_sql.erl
+++ b/src/mod_privacy_sql.erl
@@ -24,7 +24,6 @@
-module(mod_privacy_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_privacy).
diff --git a/src/mod_private.erl b/src/mod_private.erl
index f69f2cc96..350817eea 100644
--- a/src/mod_private.erl
+++ b/src/mod_private.erl
@@ -58,7 +58,7 @@
-optional_callbacks([use_cache/1, cache_nodes/1]).
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(remove_user, Host, ?MODULE, remove_user, 50),
@@ -80,8 +80,8 @@ stop(Host) ->
end.
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -92,20 +92,23 @@ reload(Host, NewOpts, OldOpts) ->
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_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
jid(), jid(), binary(), binary()) ->
@@ -348,9 +351,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_private_opt:cache_size(Opts),
+ CacheMissed = mod_private_opt:cache_missed(Opts),
+ LifeTime = case mod_private_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -360,7 +363,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_private_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
diff --git a/src/mod_private_mnesia.erl b/src/mod_private_mnesia.erl
index 03d98b1ca..bf0ce26e8 100644
--- a/src/mod_private_mnesia.erl
+++ b/src/mod_private_mnesia.erl
@@ -46,7 +46,7 @@ init(_Host, _Opts) ->
use_cache(Host) ->
case mnesia:table_info(private_storage, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_private, use_cache);
+ mod_private_opt:use_cache(Host);
_ ->
false
end.
@@ -107,7 +107,7 @@ import(LServer, <<"private_storage">>,
PS = #private_storage{usns = {LUser, LServer, XMLNS}, xml = El},
mnesia:dirty_write(PS).
-need_transform(#private_storage{usns = {U, S, NS}})
+need_transform({private_storage, {U, S, NS}, _})
when is_list(U) orelse is_list(S) orelse is_list(NS) ->
?INFO_MSG("Mnesia table 'private_storage' will be converted to binary", []),
true;
diff --git a/src/mod_private_opt.erl b/src/mod_private_opt.erl
new file mode 100644
index 000000000..71257217d
--- /dev/null
+++ b/src/mod_private_opt.erl
@@ -0,0 +1,41 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_private_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_private, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_private, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_private, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_private, db_type).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_private, use_cache).
+
diff --git a/src/mod_private_sql.erl b/src/mod_private_sql.erl
index 1fa91a9d4..e92c3e6ca 100644
--- a/src/mod_private_sql.erl
+++ b/src/mod_private_sql.erl
@@ -23,7 +23,6 @@
%%%----------------------------------------------------------------------
-module(mod_private_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_private).
%% API
diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl
index abb38456a..5eb4a15e4 100644
--- a/src/mod_privilege.erl
+++ b/src/mod_privilege.erl
@@ -57,9 +57,15 @@ stop(Host) ->
reload(_Host, _NewOpts, _OldOpts) ->
ok.
-mod_opt_type({roster, _}) -> fun acl:access_rules_validator/1;
-mod_opt_type({message, _}) -> fun acl:access_rules_validator/1;
-mod_opt_type({presence, _}) -> fun acl:access_rules_validator/1.
+mod_opt_type(roster) ->
+ econf:options(
+ #{both => econf:acl(), get => econf:acl(), set => econf:acl()});
+mod_opt_type(message) ->
+ econf:options(
+ #{outgoing => econf:acl()});
+mod_opt_type(presence) ->
+ econf:options(
+ #{managed_entity => econf:acl(), roster => econf:acl()}).
mod_options(_) ->
[{roster, [{both, none}, {get, none}, {set, none}]},
@@ -75,7 +81,7 @@ component_connected(Host) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_connected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec component_disconnected(binary(), binary()) -> ok.
component_disconnected(Host, _Reason) ->
@@ -83,7 +89,7 @@ component_disconnected(Host, _Reason) ->
fun(ServerHost) ->
Proc = gen_mod:get_module_proc(ServerHost, ?MODULE),
gen_server:cast(Proc, {component_disconnected, Host})
- end, ejabberd_config:get_myhosts()).
+ end, ejabberd_option:hosts()).
-spec process_message(stanza()) -> stop | ok.
process_message(#message{from = #jid{luser = <<"">>, lresource = <<"">>} = From,
@@ -309,7 +315,7 @@ forward_message(#message{to = To} = Msg) ->
end.
get_roster_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, roster),
+ Perms = mod_privilege_opt:roster(ServerHost),
case match_rule(ServerHost, Host, Perms, both) of
allow ->
both;
@@ -324,14 +330,14 @@ get_roster_permission(ServerHost, Host) ->
end.
get_message_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, message),
+ Perms = mod_privilege_opt:message(ServerHost),
case match_rule(ServerHost, Host, Perms, outgoing) of
allow -> outgoing;
deny -> none
end.
get_presence_permission(ServerHost, Host) ->
- Perms = gen_mod:get_module_opt(ServerHost, ?MODULE, presence),
+ Perms = mod_privilege_opt:presence(ServerHost),
case match_rule(ServerHost, Host, Perms, roster) of
allow ->
roster;
diff --git a/src/mod_privilege_opt.erl b/src/mod_privilege_opt.erl
new file mode 100644
index 000000000..64198b387
--- /dev/null
+++ b/src/mod_privilege_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_privilege_opt).
+
+-export([message/1]).
+-export([presence/1]).
+-export([roster/1]).
+
+-spec message(gen_mod:opts() | global | binary()) -> [{'outgoing','none' | acl:acl()}].
+message(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(message, Opts);
+message(Host) ->
+ gen_mod:get_module_opt(Host, mod_privilege, message).
+
+-spec presence(gen_mod:opts() | global | binary()) -> [{'managed_entity','none' | acl:acl()} | {'roster','none' | acl:acl()}].
+presence(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(presence, Opts);
+presence(Host) ->
+ gen_mod:get_module_opt(Host, mod_privilege, presence).
+
+-spec roster(gen_mod:opts() | global | binary()) -> [{'both','none' | acl:acl()} | {'get','none' | acl:acl()} | {'set','none' | acl:acl()}].
+roster(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(roster, Opts);
+roster(Host) ->
+ gen_mod:get_module_opt(Host, mod_privilege, roster).
+
diff --git a/src/mod_proxy65.erl b/src/mod_proxy65.erl
index 0fca1cdcb..4d8d06b43 100644
--- a/src/mod_proxy65.erl
+++ b/src/mod_proxy65.erl
@@ -34,7 +34,7 @@
-behaviour(supervisor).
%% gen_mod callbacks.
--export([start/2, stop/1, reload/3, transform_module_options/1]).
+-export([start/2, stop/1, reload/3]).
%% supervisor callbacks.
-export([init/1]).
@@ -52,21 +52,14 @@
ok | {error, limit | conflict | notfound | term()}.
start(Host, Opts) ->
- {ListenOpts, ModOpts} = lists:partition(
- fun({auth_type, _}) -> true;
- ({recbuf, _}) -> true;
- ({sndbuf, _}) -> true;
- ({shaper, _}) -> true;
- (_) -> false
- end, Opts),
- case mod_proxy65_service:add_listener(Host, ListenOpts) of
+ case mod_proxy65_service:add_listener(Host, Opts) of
{error, _} = Err ->
Err;
_ ->
Mod = gen_mod:ram_db_mod(global, ?MODULE),
Mod:init(),
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
- ChildSpec = {Proc, {?MODULE, start_link, [Host, ModOpts]},
+ ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]},
transient, infinity, supervisor, [?MODULE]},
supervisor:start_child(ejabberd_gen_mod_sup, ChildSpec)
end.
@@ -92,9 +85,6 @@ start_link(Host, Opts) ->
supervisor:start_link({local, Proc}, ?MODULE,
[Host, Opts]).
-transform_module_options(Opts) ->
- mod_proxy65_service:transform_module_options(Opts).
-
init([Host, Opts]) ->
Service = {mod_proxy65_service,
{mod_proxy65_service, start_link, [Host, Opts]},
@@ -104,41 +94,46 @@ init([Host, Opts]) ->
depends(_Host, _Opts) ->
[].
-mod_opt_type(access) -> fun acl:access_rules_validator/1;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
+mod_opt_type(access) ->
+ econf:acl();
mod_opt_type(hostname) ->
- fun(undefined) -> undefined;
- (H) -> iolist_to_binary(H)
- end;
+ econf:well_known(host, ?MODULE);
mod_opt_type(ip) ->
- fun(undefined) ->
- undefined;
- (S) ->
- {ok, Addr} =
- inet_parse:address(binary_to_list(iolist_to_binary(S))),
- Addr
- end;
-mod_opt_type(name) -> fun iolist_to_binary/1;
+ econf:ip();
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(port) ->
- fun (P) when is_integer(P), P > 0, P < 65536 -> P end;
+ econf:port();
mod_opt_type(max_connections) ->
- fun (I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE);
mod_opt_type(ram_db_type) ->
- fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(Opt) ->
- mod_proxy65_stream:listen_opt_type(Opt).
+ econf:well_known(ram_db_type, ?MODULE);
+mod_opt_type(server_host) ->
+ econf:binary();
+mod_opt_type(auth_type) ->
+ econf:enum([plain, anonymous]);
+mod_opt_type(recbuf) ->
+ econf:pos_int();
+mod_opt_type(shaper) ->
+ econf:shaper();
+mod_opt_type(sndbuf) ->
+ econf:pos_int().
mod_options(Host) ->
[{ram_db_type, ejabberd_config:default_ram_db(Host, ?MODULE)},
{access, all},
- {host, <<"proxy.@HOST@">>},
+ {host, <<"proxy.", Host/binary>>},
{hosts, []},
{hostname, undefined},
{ip, undefined},
{port, 7777},
{name, ?T("SOCKS5 Bytestreams")},
- {max_connections, infinity}] ++
- mod_proxy65_stream:listen_options().
+ {max_connections, infinity},
+ {auth_type, anonymous},
+ {recbuf, 65536},
+ {sndbuf, 65536},
+ {shaper, none}].
diff --git a/src/mod_proxy65_opt.erl b/src/mod_proxy65_opt.erl
new file mode 100644
index 000000000..d65e74d16
--- /dev/null
+++ b/src/mod_proxy65_opt.erl
@@ -0,0 +1,104 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_proxy65_opt).
+
+-export([access/1]).
+-export([auth_type/1]).
+-export([host/1]).
+-export([hostname/1]).
+-export([hosts/1]).
+-export([ip/1]).
+-export([max_connections/1]).
+-export([name/1]).
+-export([port/1]).
+-export([ram_db_type/1]).
+-export([recbuf/1]).
+-export([server_host/1]).
+-export([shaper/1]).
+-export([sndbuf/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, access).
+
+-spec auth_type(gen_mod:opts() | global | binary()) -> 'anonymous' | 'plain'.
+auth_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(auth_type, Opts);
+auth_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, auth_type).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, host).
+
+-spec hostname(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
+hostname(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hostname, Opts);
+hostname(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, hostname).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, hosts).
+
+-spec ip(gen_mod:opts() | global | binary()) -> 'undefined' | inet:ip_address().
+ip(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ip, Opts);
+ip(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, ip).
+
+-spec max_connections(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_connections(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_connections, Opts);
+max_connections(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, max_connections).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, name).
+
+-spec port(gen_mod:opts() | global | binary()) -> 1..1114111.
+port(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(port, Opts);
+port(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, port).
+
+-spec ram_db_type(gen_mod:opts() | global | binary()) -> atom().
+ram_db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ram_db_type, Opts);
+ram_db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, ram_db_type).
+
+-spec recbuf(gen_mod:opts() | global | binary()) -> pos_integer().
+recbuf(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(recbuf, Opts);
+recbuf(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, recbuf).
+
+-spec server_host(gen_mod:opts() | global | binary()) -> binary().
+server_host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(server_host, Opts);
+server_host(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, server_host).
+
+-spec shaper(gen_mod:opts() | global | binary()) -> atom() | [ejabberd_shaper:shaper_rule()].
+shaper(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(shaper, Opts);
+shaper(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, shaper).
+
+-spec sndbuf(gen_mod:opts() | global | binary()) -> pos_integer().
+sndbuf(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(sndbuf, Opts);
+sndbuf(Host) ->
+ gen_mod:get_module_opt(Host, mod_proxy65, sndbuf).
+
diff --git a/src/mod_proxy65_riak.erl b/src/mod_proxy65_riak.erl
index ec1015772..a537a1b01 100644
--- a/src/mod_proxy65_riak.erl
+++ b/src/mod_proxy65_riak.erl
@@ -90,7 +90,7 @@ activate_stream(SID, IJID, MaxConnections, _Node) ->
%%% Internal functions
%%%===================================================================
proxy65_schema() ->
- {record_info(fields, proxy65), #proxy65{}}.
+ {record_info(fields, proxy65), #proxy65{sid = <<>>, pid_t = self()}}.
clean_table() ->
?DEBUG("Cleaning Riak 'proxy65' table...", []),
diff --git a/src/mod_proxy65_service.erl b/src/mod_proxy65_service.erl
index 433371240..35205fe38 100644
--- a/src/mod_proxy65_service.erl
+++ b/src/mod_proxy65_service.erl
@@ -35,7 +35,7 @@
-export([start_link/2, reload/3, add_listener/2, process_disco_info/1,
process_disco_items/1, process_vcard/1, process_bytestreams/1,
- transform_module_options/1, delete_listener/1]).
+ delete_listener/1]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -60,7 +60,7 @@ reload(Host, NewOpts, OldOpts) ->
init([Host, Opts]) ->
process_flag(trap_exit, true),
- MyHosts = gen_mod:get_opt_hosts(Host, Opts),
+ MyHosts = gen_mod:get_opt_hosts(Opts),
lists:foreach(
fun(MyHost) ->
gen_iq_handler:add_iq_handler(ejabberd_local, MyHost, ?NS_DISCO_INFO,
@@ -91,8 +91,8 @@ handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast({reload, ServerHost, NewOpts, OldOpts}, State) ->
- NewHosts = gen_mod:get_opt_hosts(ServerHost, NewOpts),
- OldHosts = gen_mod:get_opt_hosts(ServerHost, OldOpts),
+ NewHosts = gen_mod:get_opt_hosts(NewOpts),
+ OldHosts = gen_mod:get_opt_hosts(OldOpts),
lists:foreach(
fun(NewHost) ->
ejabberd_router:register_route(NewHost, ServerHost),
@@ -116,8 +116,8 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}.
add_listener(Host, Opts) ->
{_, IP, _} = EndPoint = get_endpoint(Host),
- Opts1 = [{server_host, Host} | Opts],
- Opts2 = lists:keystore(ip, 1, Opts1, {ip, IP}),
+ Opts1 = gen_mod:set_opt(server_host, Host, Opts),
+ Opts2 = gen_mod:set_opt(ip, IP, Opts1),
ejabberd_listener:add_listener(EndPoint, mod_proxy65_stream, Opts2).
delete_listener(Host) ->
@@ -132,7 +132,7 @@ process_disco_info(#iq{type = set, lang = Lang} = IQ) ->
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
process_disco_info(#iq{type = get, to = To, lang = Lang} = IQ) ->
Host = ejabberd_router:host_of_route(To#jid.lserver),
- Name = gen_mod:get_module_opt(Host, mod_proxy65, name),
+ Name = mod_proxy65_opt:name(Host),
Info = ejabberd_hooks:run_fold(disco_info, Host,
[], [Host, ?MODULE, <<"">>, <<"">>]),
xmpp:make_iq_result(
@@ -164,7 +164,7 @@ process_vcard(#iq{type = get, lang = Lang} = IQ) ->
process_bytestreams(#iq{type = get, from = JID, to = To, lang = Lang} = IQ) ->
Host = To#jid.lserver,
ServerHost = ejabberd_router:host_of_route(Host),
- ACL = gen_mod:get_module_opt(ServerHost, mod_proxy65, access),
+ ACL = mod_proxy65_opt:access(ServerHost),
case acl:match_rule(ServerHost, ACL, JID) of
allow ->
StreamHost = get_streamhost(Host, ServerHost),
@@ -178,7 +178,7 @@ process_bytestreams(#iq{type = set, lang = Lang,
Why = {bad_attr_value, <<"sid">>, <<"query">>, ?NS_BYTESTREAMS},
Txt = xmpp:io_format_error(Why),
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
-process_bytestreams(#iq{type = set, lang = Lang,
+process_bytestreams(#iq{type = set, lang = Lang,
sub_els = [#bytestreams{activate = undefined}]} = IQ) ->
Why = {missing_cdata, <<"">>, <<"activate">>, ?NS_BYTESTREAMS},
Txt = xmpp:io_format_error(Why),
@@ -187,7 +187,7 @@ process_bytestreams(#iq{type = set, lang = Lang, from = InitiatorJID, to = To,
sub_els = [#bytestreams{activate = TargetJID,
sid = SID}]} = IQ) ->
ServerHost = ejabberd_router:host_of_route(To#jid.lserver),
- ACL = gen_mod:get_module_opt(ServerHost, mod_proxy65, access),
+ ACL = mod_proxy65_opt:access(ServerHost),
case acl:match_rule(ServerHost, ACL, InitiatorJID) of
allow ->
Node = ejabberd_cluster:get_node_by_id(To#jid.lresource),
@@ -226,24 +226,13 @@ process_bytestreams(#iq{type = set, lang = Lang, from = InitiatorJID, to = To,
%%%-------------------------
%%% Auxiliary functions.
%%%-------------------------
-transform_module_options(Opts) ->
- lists:map(
- fun({ip, IP}) when is_tuple(IP) ->
- {ip, misc:ip_to_list(IP)};
- ({hostname, IP}) when is_tuple(IP) ->
- {hostname, misc:ip_to_list(IP)};
- (Opt) ->
- Opt
- end, Opts).
-
-spec get_streamhost(binary(), binary()) -> streamhost().
get_streamhost(Host, ServerHost) ->
{Port, IP, _} = get_endpoint(ServerHost),
- HostName0 = case gen_mod:get_module_opt(ServerHost, mod_proxy65, hostname) of
- undefined -> misc:ip_to_list(IP);
- Val -> Val
- end,
- HostName = misc:expand_keyword(<<"@HOST@">>, HostName0, ServerHost),
+ HostName = case mod_proxy65_opt:hostname(ServerHost) of
+ undefined -> misc:ip_to_list(IP);
+ Val -> Val
+ end,
Resource = ejabberd_cluster:node_id(),
#streamhost{jid = jid:make(<<"">>, Host, Resource),
host = HostName,
@@ -251,8 +240,8 @@ get_streamhost(Host, ServerHost) ->
-spec get_endpoint(binary()) -> {inet:port_number(), inet:ip_address(), tcp}.
get_endpoint(Host) ->
- Port = gen_mod:get_module_opt(Host, mod_proxy65, port),
- IP = case gen_mod:get_module_opt(Host, mod_proxy65, ip) of
+ Port = mod_proxy65_opt:port(Host),
+ IP = case mod_proxy65_opt:ip(Host) of
undefined -> get_my_ip();
Addr -> Addr
end,
@@ -267,7 +256,7 @@ get_my_ip() ->
end.
max_connections(ServerHost) ->
- gen_mod:get_module_opt(ServerHost, mod_proxy65, max_connections).
+ mod_proxy65_opt:max_connections(ServerHost).
register_handlers(Host) ->
gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO,
diff --git a/src/mod_proxy65_sql.erl b/src/mod_proxy65_sql.erl
index 20fed2209..d655e2741 100644
--- a/src/mod_proxy65_sql.erl
+++ b/src/mod_proxy65_sql.erl
@@ -23,7 +23,6 @@
-module(mod_proxy65_sql).
-behaviour(mod_proxy65).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/0, register_stream/2, unregister_stream/1, activate_stream/4]).
diff --git a/src/mod_proxy65_stream.erl b/src/mod_proxy65_stream.erl
index 02d2643ee..06cefdb9f 100644
--- a/src/mod_proxy65_stream.erl
+++ b/src/mod_proxy65_stream.erl
@@ -39,8 +39,7 @@
stream_established/2]).
-export([start/3, stop/1, start_link/3, activate/2,
- relay/3, accept/1, listen_opt_type/1,
- listen_options/0]).
+ relay/3, accept/1, listen_options/0]).
-include("mod_proxy65.hrl").
@@ -65,28 +64,20 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
%%-------------------------------
-start(gen_tcp, Socket, Opts1) ->
- {[{server_host, Host}], Opts} = lists:partition(
- fun({server_host, _}) -> true;
- (_) -> false
- end, Opts1),
- p1_fsm:start(?MODULE, [Socket, Host, Opts], []).
-
-start_link(gen_tcp, Socket, Opts1) ->
- {[{server_host, Host}], Opts} = lists:partition(
- fun({server_host, _}) -> true;
- (_) -> false
- end, Opts1),
- start_link(Socket, Host, Opts);
-start_link(Socket, Host, Opts) ->
- p1_fsm:start_link(?MODULE, [Socket, Host, Opts], []).
-
-init([Socket, Host, Opts]) ->
+start(gen_tcp, Socket, Opts) ->
+ Host = proplists:get_value(server_host, Opts),
+ p1_fsm:start(?MODULE, [Socket, Host], []).
+
+start_link(gen_tcp, Socket, Opts) ->
+ Host = proplists:get_value(server_host, Opts),
+ p1_fsm:start_link(?MODULE, [Socket, Host], []).
+
+init([Socket, Host]) ->
process_flag(trap_exit, true),
- AuthType = gen_mod:get_opt(auth_type, Opts),
- Shaper = gen_mod:get_opt(shaper, Opts),
- RecvBuf = gen_mod:get_opt(recbuf, Opts),
- SendBuf = gen_mod:get_opt(sndbuf, Opts),
+ AuthType = mod_proxy65_opt:auth_type(Host),
+ Shaper = mod_proxy65_opt:shaper(Host),
+ RecvBuf = mod_proxy65_opt:recbuf(Host),
+ SendBuf = mod_proxy65_opt:sndbuf(Host),
TRef = erlang:send_after(?WAIT_TIMEOUT, self(), stop),
inet:setopts(Socket, [{recbuf, RecvBuf}, {sndbuf, SendBuf}]),
{ok, accepting,
@@ -284,35 +275,13 @@ select_auth_method(anonymous, AuthMethods) ->
%% Obviously, we must use shaper with maximum rate.
find_maxrate(Shaper, JID1, JID2, Host) ->
- MaxRate1 = case acl:match_rule(Host, Shaper, JID1) of
- deny -> none;
- R1 -> ejabberd_shaper:new(R1)
- end,
- MaxRate2 = case acl:match_rule(Host, Shaper, JID2) of
- deny -> none;
- R2 -> ejabberd_shaper:new(R2)
- end,
- if MaxRate1 == none; MaxRate2 == none -> none;
- true -> lists:max([MaxRate1, MaxRate2])
- end.
-
-listen_opt_type(server_host) ->
- fun iolist_to_binary/1;
-listen_opt_type(auth_type) ->
- fun (plain) -> plain;
- (anonymous) -> anonymous
- end;
-listen_opt_type(recbuf) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
-listen_opt_type(sndbuf) ->
- fun (I) when is_integer(I), I > 0 -> I end;
-listen_opt_type(accept_interval) ->
- fun(I) when is_integer(I), I>=0 -> I end.
+ R1 = ejabberd_shaper:match(Host, Shaper, JID1),
+ R2 = ejabberd_shaper:match(Host, Shaper, JID2),
+ R = case ejabberd_shaper:get_max_rate(R1) >= ejabberd_shaper:get_max_rate(R2) of
+ true -> R1;
+ false -> R2
+ end,
+ ejabberd_shaper:new(R).
listen_options() ->
- [{auth_type, anonymous},
- {recbuf, 65536},
- {sndbuf, 65536},
- {accept_interval, 0},
- {shaper, none}].
+ [].
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index 1f220f3c6..1656a7cbd 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -240,27 +240,25 @@ stop(Host) ->
init([ServerHost, Opts]) ->
process_flag(trap_exit, true),
?DEBUG("pubsub init ~p ~p", [ServerHost, Opts]),
- Hosts = gen_mod:get_opt_hosts(ServerHost, Opts),
- Access = gen_mod:get_opt(access_createnode, Opts),
- PepOffline = gen_mod:get_opt(ignore_pep_from_offline, Opts),
- LastItemCache = gen_mod:get_opt(last_item_cache, Opts),
- MaxItemsNode = gen_mod:get_opt(max_items_node, Opts),
- MaxSubsNode = gen_mod:get_opt(max_subscriptions_node, Opts),
+ Hosts = gen_mod:get_opt_hosts(Opts),
+ Access = mod_pubsub_opt:access_createnode(Opts),
+ PepOffline = mod_pubsub_opt:ignore_pep_from_offline(Opts),
+ LastItemCache = mod_pubsub_opt:last_item_cache(Opts),
+ MaxItemsNode = mod_pubsub_opt:max_items_node(Opts),
+ MaxSubsNode = mod_pubsub_opt:max_subscriptions_node(Opts),
ejabberd_mnesia:create(?MODULE, pubsub_last_item,
[{ram_copies, [node()]},
{attributes, record_info(fields, pubsub_last_item)}]),
+ DBMod = gen_mod:db_mod(Opts, ?MODULE),
AllPlugins =
lists:flatmap(
fun(Host) ->
+ DBMod:init(Host, ServerHost, Opts),
ejabberd_router:register_route(Host, ServerHost),
- case gen_mod:get_module_opt(ServerHost, ?MODULE, db_type) of
- mnesia -> pubsub_index:init(Host, ServerHost, Opts);
- _ -> ok
- end,
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
DefaultModule = plugin(Host, hd(Plugins)),
DefaultNodeCfg = merge_config(
- [gen_mod:get_opt(default_node_config, Opts),
+ [mod_pubsub_opt:default_node_config(Opts),
DefaultModule:options()]),
lists:foreach(
fun(H) ->
@@ -337,7 +335,7 @@ init([ServerHost, Opts]) ->
NodeTree = config(ServerHost, nodetree),
Plugins = config(ServerHost, plugins),
PepMapping = config(ServerHost, pep_mapping),
- DBType = gen_mod:get_module_opt(ServerHost, ?MODULE, db_type),
+ DBType = mod_pubsub_opt:db_type(ServerHost),
{ok, #state{hosts = Hosts, server_host = ServerHost,
access = Access, pep_mapping = PepMapping,
ignore_pep_from_offline = PepOffline,
@@ -345,13 +343,13 @@ init([ServerHost, Opts]) ->
max_items_node = MaxItemsNode, nodetree = NodeTree,
plugins = Plugins, db_type = DBType}}.
-depends(ServerHost, Opts0) ->
- Opts = Opts0 ++ mod_options(ServerHost),
- [Host|_] = gen_mod:get_opt_hosts(ServerHost, Opts),
- Plugins = gen_mod:get_opt(plugins, Opts),
+depends(ServerHost, Opts) ->
+ [Host|_] = gen_mod:get_opt_hosts(Opts),
+ Plugins = mod_pubsub_opt:plugins(Opts),
+ Db = mod_pubsub_opt:db_type(Opts),
lists:flatmap(
fun(Name) ->
- Plugin = plugin(ServerHost, Name),
+ Plugin = plugin(Db, Name),
try apply(Plugin, depends, [Host, ServerHost, Opts])
catch _:undef -> []
end
@@ -363,11 +361,11 @@ depends(ServerHost, Opts0) ->
%% <em>node_plugin</em>. The 'node_' prefix is mandatory.</p>
%% <p>See {@link node_hometree:init/1} for an example implementation.</p>
init_plugins(Host, ServerHost, Opts) ->
- TreePlugin = tree(Host, gen_mod:get_opt(nodetree, Opts)),
+ TreePlugin = tree(Host, mod_pubsub_opt:nodetree(Opts)),
?DEBUG("** tree plugin is ~p", [TreePlugin]),
TreePlugin:init(Host, ServerHost, Opts),
- Plugins = gen_mod:get_opt(plugins, Opts),
- PepMapping = gen_mod:get_opt(pep_mapping, Opts),
+ Plugins = mod_pubsub_opt:plugins(Opts),
+ PepMapping = mod_pubsub_opt:pep_mapping(Opts),
?DEBUG("** PEP Mapping : ~p~n", [PepMapping]),
PluginsOK = lists:foldl(
fun (Name, Acc) ->
@@ -431,7 +429,7 @@ disco_sm_identity(Acc, From, To, Node, _Lang) ->
disco_identity(jid:tolower(jid:remove_resource(To)), Node, From)
++ Acc.
--spec disco_identity(binary(), binary(), jid()) -> [identity()].
+-spec disco_identity(host(), binary(), jid()) -> [identity()].
disco_identity(_Host, <<>>, _From) ->
[#identity{category = <<"pubsub">>, type = <<"pep">>}];
disco_identity(Host, Node, From) ->
@@ -975,7 +973,7 @@ iq_disco_info(ServerHost, Host, SNode, From, Lang) ->
end,
case Node of
<<>> ->
- Name = gen_mod:get_module_opt(ServerHost, ?MODULE, name),
+ Name = mod_pubsub_opt:name(ServerHost),
{result,
#disco_info{
identities = [#identity{
@@ -999,7 +997,7 @@ iq_disco_info(ServerHost, Host, SNode, From, Lang) ->
-spec iq_disco_items(host(), binary(), jid(), undefined | rsm_set()) ->
{result, disco_items()} | {error, stanza_error()}.
iq_disco_items(Host, <<>>, From, _RSM) ->
- Items =
+ Items =
lists:map(
fun(#pubsub_node{nodeid = {_, SubNode}, options = Options}) ->
case get_option(Options, title) of
@@ -1256,7 +1254,7 @@ adhoc_request(Host, _ServerHost, Owner,
case send_pending_auth_events(Host, Node, Owner, Lang) of
ok ->
xmpp_util:make_adhoc_response(
- Request, #adhoc_command{action = completed});
+ Request, #adhoc_command{status = completed});
Err ->
Err
end
@@ -1310,7 +1308,7 @@ get_pending_nodes(Host, Owner, Plugins) ->
%% @doc <p>Send a subscription approval form to Owner for all pending
%% subscriptions on Host and Node.</p>
-spec send_pending_auth_events(binary(), binary(), jid(),
- binary()) -> adhoc_command() | {error, stanza_error()}.
+ binary()) -> ok | {error, stanza_error()}.
send_pending_auth_events(Host, Node, Owner, Lang) ->
?DEBUG("Sending pending auth events for ~s on ~s:~s",
[jid:encode(Owner), Host, Node]),
@@ -1336,8 +1334,7 @@ send_pending_auth_events(Host, Node, Owner, Lang) ->
fun({J, pending, _SubId}) -> send_authorization_request(N, jid:make(J));
({J, pending}) -> send_authorization_request(N, jid:make(J));
(_) -> ok
- end, Subs),
- #adhoc_command{};
+ end, Subs);
Err ->
Err
end.
@@ -1448,7 +1445,7 @@ update_auth(Host, Node, Type, Nidx, Subscriber, Allow, Subs) ->
{result, ok};
_ ->
Txt = <<"No pending subscriptions found">>,
- {error, xmpp:err_unexpected_request(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_unexpected_request(Txt, ejabberd_option:language())}
end.
%% @doc <p>Create new pubsub nodes</p>
@@ -1528,7 +1525,7 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
end;
_ ->
Txt = <<"You're not allowed to create nodes">>,
- {error, xmpp:err_forbidden(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_forbidden(Txt, ejabberd_option:language())}
end
end,
Reply = #pubsub{create = Node},
@@ -1564,7 +1561,7 @@ create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
%%</ul>
-spec delete_node(host(), binary(), jid()) -> {result, pubsub_owner()} | {error, stanza_error()}.
delete_node(_Host, <<>>, _Owner) ->
- {error, xmpp:err_not_allowed(<<"No node specified">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_not_allowed(<<"No node specified">>, ejabberd_option:language())};
delete_node(Host, Node, Owner) ->
Action = fun (#pubsub_node{type = Type, id = Nidx}) ->
case node_call(Host, Type, get_affiliation, [Nidx, Owner]) of
@@ -1576,7 +1573,7 @@ delete_node(Host, Node, Owner) ->
Error -> Error
end;
_ ->
- {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_option:language())}
end
end,
Reply = undefined,
@@ -1692,7 +1689,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
err_closed_node())};
true ->
Owners = node_owners_call(Host, Type, Nidx, O),
- {PS, RG} = get_presence_and_roster_permissions(Host, Subscriber,
+ {PS, RG} = get_presence_and_roster_permissions(Host, JID,
Owners, AccessModel, AllowedGroups),
node_call(Host, Type, subscribe_node,
[Nidx, From, Subscriber, AccessModel,
@@ -1726,10 +1723,10 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
{result, {_TNode, {Result, subscribed, _SubId}}} ->
{result, Result};
{result, {TNode, {default, pending, _SubId}}} ->
- send_authorization_request(TNode, Subscriber),
+ send_authorization_request(TNode, JID),
{result, Reply(pending)};
{result, {TNode, {Result, pending}}} ->
- send_authorization_request(TNode, Subscriber),
+ send_authorization_request(TNode, JID),
{result, Result};
{result, {_, Result}} ->
{result, Result};
@@ -1869,7 +1866,7 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access
end;
false ->
Txt = <<"Automatic node creation is not enabled">>,
- {error, xmpp:err_item_not_found(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_item_not_found(Txt, ejabberd_option:language())}
end;
Error ->
Error
@@ -1986,7 +1983,7 @@ purge_node(Host, Node, Owner) ->
%% <p>The number of items to return is limited by MaxItems.</p>
%% <p>The permission are not checked in this function.</p>
-spec get_items(host(), binary(), jid(), binary(),
- binary(), [binary()], undefined | rsm_set()) ->
+ undefined | non_neg_integer(), [binary()], undefined | rsm_set()) ->
{result, pubsub()} | {error, stanza_error()}.
get_items(Host, Node, From, SubId, MaxItems, ItemIds, undefined)
when MaxItems =/= undefined ->
@@ -2150,7 +2147,7 @@ get_affiliations(Host, Node, JID) ->
{error, extended_error(xmpp:err_feature_not_implemented(),
err_unsupported('modify-affiliations'))};
Affiliation /= owner ->
- {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_option:language())};
true ->
node_call(Host, Type, get_node_affiliations, [Nidx])
end
@@ -2216,7 +2213,7 @@ set_affiliations(Host, Node, From, Affs) ->
{result, undefined};
_ ->
{error, xmpp:err_forbidden(
- <<"Owner privileges required">>, ejabberd_config:get_mylang())}
+ <<"Owner privileges required">>, ejabberd_option:language())}
end
end,
case transaction(Host, Node, Action, sync_dirty) of
@@ -2362,9 +2359,11 @@ get_subscriptions(Host, Node, JID, Plugins) when is_list(Plugins) ->
({#pubsub_node{nodeid = {_, SubsNode}}, Sub}) ->
case Node of
<<>> ->
- [#ps_subscription{node = SubsNode, type = Sub}];
+ [#ps_subscription{jid = jid:remove_resource(JID),
+ node = SubsNode, type = Sub}];
SubsNode ->
- [#ps_subscription{type = Sub}];
+ [#ps_subscription{jid = jid:remove_resource(JID),
+ type = Sub}];
_ ->
[]
end;
@@ -2411,7 +2410,7 @@ get_subscriptions(Host, Node, JID) ->
{error, extended_error(xmpp:err_feature_not_implemented(),
err_unsupported('manage-subscriptions'))};
Affiliation /= owner ->
- {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_forbidden(<<"Owner privileges required">>, ejabberd_option:language())};
true ->
node_call(Host, Type, get_node_subscriptions, [Nidx])
end
@@ -2492,8 +2491,8 @@ set_subscriptions(Host, Node, From, Entities) ->
end;
_ ->
{error, xmpp:err_forbidden(
- <<"Owner privileges required">>, ejabberd_config:get_mylang())}
-
+ <<"Owner privileges required">>, ejabberd_option:language())}
+
end
end,
case transaction(Host, Node, Action, sync_dirty) of
@@ -2502,7 +2501,7 @@ set_subscriptions(Host, Node, From, Entities) ->
end.
-spec get_presence_and_roster_permissions(
- host(), ljid(), [ljid()], accessModel(),
+ host(), jid(), [ljid()], accessModel(),
[binary()]) -> {boolean(), boolean()}.
get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups) ->
if (AccessModel == presence) or (AccessModel == roster) ->
@@ -2517,6 +2516,7 @@ get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGrou
{true, true}
end.
+-spec get_roster_info(binary(), binary(), ljid() | jid(), [binary()]) -> {boolean(), boolean()}.
get_roster_info(_, _, {<<>>, <<>>, _}, _) ->
{false, false};
get_roster_info(OwnerUser, OwnerServer, {SubscriberUser, SubscriberServer, _}, AllowedGroups) ->
@@ -2625,7 +2625,7 @@ get_resource_state({U, S, R}, ShowValues, JIDs) ->
Show = case ejabberd_c2s:get_presence(Pid) of
#presence{type = unavailable} -> <<"unavailable">>;
#presence{show = undefined} -> <<"online">>;
- #presence{show = S} -> atom_to_binary(S, latin1)
+ #presence{show = Sh} -> atom_to_binary(Sh, latin1)
end,
case lists:member(Show, ShowValues) of
%% If yes, item can be delivered
@@ -2930,7 +2930,7 @@ send_stanza({LUser, LServer, _} = Publisher, USR, Node, BaseStanza) ->
[ejabberd_sm:route(jid:make(Publisher),
{pep_message, <<((Node))/binary, "+notify">>,
add_extended_headers(
- Stanza, extended_headers([Publisher])),
+ Stanza, extended_headers([jid:make(Publisher)])),
To}) || To <- USRs];
send_stanza(Host, USR, _Node, Stanza) ->
ejabberd_router:route(
@@ -3176,7 +3176,7 @@ node_owners_call(_Host, _Type, _Nidx, Owners) ->
Owners.
node_config(Node, ServerHost) ->
- Opts = gen_mod:get_module_opt(ServerHost, ?MODULE, force_node_config),
+ Opts = mod_pubsub_opt:force_node_config(ServerHost),
node_config(Node, ServerHost, Opts).
node_config(Node, ServerHost, [{RE, Opts}|NodeOpts]) ->
@@ -3341,9 +3341,7 @@ decode_get_pending(#xdata{fields = Fs}, Lang) ->
catch _:{pubsub_get_pending, Why} ->
Txt = pubsub_get_pending:format_error(Why),
{error, xmpp:err_resource_constraint(Txt, Lang)}
- end;
-decode_get_pending(undefined, Lang) ->
- {error, xmpp:err_bad_request(<<"No data form found">>, Lang)}.
+ end.
-spec check_opt_range(atom(), [proplists:property()], non_neg_integer()) -> boolean().
check_opt_range(_Opt, _Opts, undefined) ->
@@ -3365,7 +3363,7 @@ get_max_subscriptions_node(Host) ->
is_last_item_cache_enabled(Host) ->
config(Host, last_item_cache, false).
--spec set_cached_item(host(), nodeIdx(), binary(), binary(), [xmlel()]) -> ok.
+-spec set_cached_item(host(), nodeIdx(), binary(), jid(), [xmlel()]) -> ok.
set_cached_item({_, ServerHost, _}, Nidx, ItemId, Publisher, Payload) ->
set_cached_item(ServerHost, Nidx, ItemId, Publisher, Payload);
set_cached_item(Host, Nidx, ItemId, Publisher, Payload) ->
@@ -3390,7 +3388,7 @@ unset_cached_item(Host, Nidx) ->
_ -> ok
end.
--spec get_cached_item(host(), nodeIdx()) -> undefined | pubsubItem().
+-spec get_cached_item(host(), nodeIdx()) -> undefined | #pubsub_item{}.
get_cached_item({_, ServerHost, _}, Nidx) ->
get_cached_item(ServerHost, Nidx);
get_cached_item(Host, Nidx) ->
@@ -3426,13 +3424,13 @@ tree(Host) ->
Tree -> Tree
end.
--spec tree(host(), binary()) -> atom().
+-spec tree(host() | atom(), binary()) -> atom().
tree(_Host, <<"virtual">>) ->
nodetree_virtual; % special case, virtual does not use any backend
tree(Host, Name) ->
submodule(Host, <<"nodetree">>, Name).
--spec plugin(host(), binary()) -> atom().
+-spec plugin(host() | atom(), binary()) -> atom().
plugin(Host, Name) ->
submodule(Host, <<"node">>, Name).
@@ -3444,16 +3442,19 @@ plugins(Host) ->
Plugins -> Plugins
end.
--spec subscription_plugin(host()) -> atom().
+-spec subscription_plugin(host() | atom()) -> atom().
subscription_plugin(Host) ->
submodule(Host, <<"pubsub">>, <<"subscription">>).
--spec submodule(host(), binary(), binary()) -> atom().
-submodule(Host, Type, Name) ->
- case gen_mod:get_module_opt(serverhost(Host), ?MODULE, db_type) of
+-spec submodule(host() | atom(), binary(), binary()) -> atom().
+submodule(Db, Type, Name) when is_atom(Db) ->
+ case Db of
mnesia -> ejabberd:module_name([<<"pubsub">>, Type, Name]);
- Db -> ejabberd:module_name([<<"pubsub">>, Type, Name, misc:atom_to_binary(Db)])
- end.
+ _ -> ejabberd:module_name([<<"pubsub">>, Type, Name, misc:atom_to_binary(Db)])
+ end;
+submodule(Host, Type, Name) ->
+ Db = mod_pubsub_opt:db_type(serverhost(Host)),
+ submodule(Db, Type, Name).
-spec config(binary(), any()) -> any().
config(ServerHost, Key) ->
@@ -3518,7 +3519,7 @@ features() ->
% see plugin "subscribe", % REQUIRED
% see plugin "subscription-options", % OPTIONAL
% see plugin "subscription-notifications" % OPTIONAL
--spec plugin_features(binary(), binary()) -> [binary()].
+-spec plugin_features(host(), binary()) -> [binary()].
plugin_features(Host, Type) ->
Module = plugin(Host, Type),
case catch Module:features() of
@@ -3553,7 +3554,7 @@ tree_action(Host, Function, Args) ->
?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]),
ServerHost = serverhost(Host),
Fun = fun () -> tree_call(Host, Function, Args) end,
- case gen_mod:get_module_opt(ServerHost, ?MODULE, db_type) of
+ case mod_pubsub_opt:db_type(ServerHost) of
mnesia ->
mnesia:sync_dirty(Fun);
sql ->
@@ -3563,7 +3564,7 @@ tree_action(Host, Function, Args) ->
{aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
ErrTxt = <<"Database failure">>,
- {error, xmpp:err_internal_server_error(ErrTxt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_internal_server_error(ErrTxt, ejabberd_option:language())}
end;
_ ->
Fun()
@@ -3614,7 +3615,7 @@ transaction(Host, Node, Action, Trans) ->
transaction(Host, Fun, Trans) ->
ServerHost = serverhost(Host),
- DBType = gen_mod:get_module_opt(ServerHost, ?MODULE, db_type),
+ DBType = mod_pubsub_opt:db_type(ServerHost),
do_transaction(ServerHost, Fun, Trans, DBType).
do_transaction(ServerHost, Fun, Trans, DBType) ->
@@ -3641,10 +3642,10 @@ do_transaction(ServerHost, Fun, Trans, DBType) ->
{error, Error};
{aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())};
Other ->
?ERROR_MSG("transaction return internal error: ~p~n", [Other]),
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())}
end.
%%%% helpers
@@ -3823,7 +3824,7 @@ purge_offline(LJID) ->
?ERROR_MSG("can not purge offline: ~p", [Error])
end.
--spec purge_offline(host(), ljid(), binary()) -> ok | {error, stanza_error()}.
+-spec purge_offline(host(), ljid(), #pubsub_node{}) -> ok | {error, stanza_error()}.
purge_offline(Host, LJID, Node) ->
Nidx = Node#pubsub_node.id,
Type = Node#pubsub_node.type,
@@ -3858,43 +3859,51 @@ purge_offline(Host, LJID, Node) ->
Error
end.
-mod_opt_type(access_createnode) -> fun acl:access_rules_validator/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
-mod_opt_type(name) -> fun iolist_to_binary/1;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
+mod_opt_type(access_createnode) ->
+ econf:acl();
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(ignore_pep_from_offline) ->
- fun (A) when is_boolean(A) -> A end;
+ econf:bool();
mod_opt_type(last_item_cache) ->
- fun (A) when is_boolean(A) -> A end;
+ econf:bool();
mod_opt_type(max_items_node) ->
- fun (A) when is_integer(A) andalso A >= 0 -> A end;
+ econf:non_neg_int();
mod_opt_type(max_subscriptions_node) ->
- fun(A) when is_integer(A) andalso A >= 0 -> A;
- (undefined) -> undefined
- end;
+ econf:non_neg_int();
mod_opt_type(force_node_config) ->
- fun(NodeOpts) ->
- lists:map(
- fun({Node, Opts}) ->
- {ok, RE} = re:compile(
- ejabberd_regexp:sh_to_awk(Node)),
- {RE, lists:keysort(1, Opts)}
- end, NodeOpts)
- end;
+ econf:map(
+ econf:glob(),
+ econf:map(
+ econf:atom(),
+ econf:either(
+ econf:int(),
+ econf:atom()),
+ [{return, orddict}, unique]));
mod_opt_type(default_node_config) ->
- fun (A) when is_list(A) -> A end;
+ econf:map(
+ econf:atom(),
+ econf:either(
+ econf:int(),
+ econf:atom()),
+ [unique]);
mod_opt_type(nodetree) ->
- fun (A) when is_binary(A) -> A end;
+ econf:binary();
mod_opt_type(pep_mapping) ->
- fun (A) when is_list(A) -> A end;
+ econf:map(econf:binary(), econf:binary());
mod_opt_type(plugins) ->
- fun (A) when is_list(A) -> A end.
+ econf:list(econf:binary());
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE);
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE).
mod_options(Host) ->
[{access_createnode, all},
{db_type, ejabberd_config:default_db(Host, ?MODULE)},
- {host, <<"pubsub.@HOST@">>},
+ {host, <<"pubsub.", Host/binary>>},
{hosts, []},
{name, ?T("Publish-Subscribe")},
{ignore_pep_from_offline, true},
diff --git a/src/mod_pubsub_mnesia.erl b/src/mod_pubsub_mnesia.erl
new file mode 100644
index 000000000..8f780a623
--- /dev/null
+++ b/src/mod_pubsub_mnesia.erl
@@ -0,0 +1,32 @@
+%%%----------------------------------------------------------------------
+%%% 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(mod_pubsub_mnesia).
+
+%% API
+-export([init/3]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(Host, ServerHost, Opts) ->
+ pubsub_index:init(Host, ServerHost, Opts).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/mod_pubsub_opt.erl b/src/mod_pubsub_opt.erl
new file mode 100644
index 000000000..a77130976
--- /dev/null
+++ b/src/mod_pubsub_opt.erl
@@ -0,0 +1,104 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_pubsub_opt).
+
+-export([access_createnode/1]).
+-export([db_type/1]).
+-export([default_node_config/1]).
+-export([force_node_config/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([ignore_pep_from_offline/1]).
+-export([last_item_cache/1]).
+-export([max_items_node/1]).
+-export([max_subscriptions_node/1]).
+-export([name/1]).
+-export([nodetree/1]).
+-export([pep_mapping/1]).
+-export([plugins/1]).
+
+-spec access_createnode(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_createnode(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_createnode, Opts);
+access_createnode(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, access_createnode).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, db_type).
+
+-spec default_node_config(gen_mod:opts() | global | binary()) -> [{atom(),atom() | integer()}].
+default_node_config(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(default_node_config, Opts);
+default_node_config(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, default_node_config).
+
+-spec force_node_config(gen_mod:opts() | global | binary()) -> [{re:mp(),[{atom(),atom() | integer()}]}].
+force_node_config(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(force_node_config, Opts);
+force_node_config(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, force_node_config).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, hosts).
+
+-spec ignore_pep_from_offline(gen_mod:opts() | global | binary()) -> boolean().
+ignore_pep_from_offline(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ignore_pep_from_offline, Opts);
+ignore_pep_from_offline(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, ignore_pep_from_offline).
+
+-spec last_item_cache(gen_mod:opts() | global | binary()) -> boolean().
+last_item_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(last_item_cache, Opts);
+last_item_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, last_item_cache).
+
+-spec max_items_node(gen_mod:opts() | global | binary()) -> non_neg_integer().
+max_items_node(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_items_node, Opts);
+max_items_node(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, max_items_node).
+
+-spec max_subscriptions_node(gen_mod:opts() | global | binary()) -> 'undefined' | non_neg_integer().
+max_subscriptions_node(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_subscriptions_node, Opts);
+max_subscriptions_node(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, max_subscriptions_node).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, name).
+
+-spec nodetree(gen_mod:opts() | global | binary()) -> binary().
+nodetree(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(nodetree, Opts);
+nodetree(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, nodetree).
+
+-spec pep_mapping(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+pep_mapping(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(pep_mapping, Opts);
+pep_mapping(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, pep_mapping).
+
+-spec plugins(gen_mod:opts() | global | binary()) -> [binary()].
+plugins(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(plugins, Opts);
+plugins(Host) ->
+ gen_mod:get_module_opt(Host, mod_pubsub, plugins).
+
diff --git a/src/mod_pubsub_riak.erl b/src/mod_pubsub_riak.erl
new file mode 100644
index 000000000..87000a85c
--- /dev/null
+++ b/src/mod_pubsub_riak.erl
@@ -0,0 +1,32 @@
+%%%----------------------------------------------------------------------
+%%% 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(mod_pubsub_riak).
+
+%% API
+-export([init/3]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(_Host, _ServerHost, _Opts) ->
+ ok.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/mod_pubsub_sql.erl b/src/mod_pubsub_sql.erl
new file mode 100644
index 000000000..655d43ea9
--- /dev/null
+++ b/src/mod_pubsub_sql.erl
@@ -0,0 +1,32 @@
+%%%----------------------------------------------------------------------
+%%% 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(mod_pubsub_sql).
+
+%% API
+-export([init/3]).
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+init(_Host, _ServerHost, _Opts) ->
+ ok.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
diff --git a/src/mod_push.erl b/src/mod_push.erl
index 2cf34f65f..5dc924884 100644
--- a/src/mod_push.erl
+++ b/src/mod_push.erl
@@ -92,7 +92,7 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> ok.
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
register_iq_handlers(Host),
@@ -112,8 +112,8 @@ stop(Host) ->
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -124,31 +124,33 @@ reload(Host, NewOpts, OldOpts) ->
depends(_Host, _Opts) ->
[].
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(include_sender) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(include_body) ->
- fun (B) when is_boolean(B) -> B;
- (S) -> iolist_to_binary(S)
- end;
+ econf:either(
+ econf:bool(),
+ econf:binary());
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.
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
-spec mod_options(binary()) -> [{atom(), any()}].
mod_options(Host) ->
[{include_sender, false},
{include_body, <<"New message">>},
{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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
%%--------------------------------------------------------------------
%% ejabberd command callback.
@@ -169,11 +171,11 @@ delete_old_sessions(Days) ->
DBTypes = lists:usort(
lists:map(
fun(Host) ->
- case gen_mod:get_module_opt(Host, ?MODULE, db_type) of
+ case mod_push_opt:db_type(Host) of
sql -> {sql, Host};
Other -> {Other, global}
end
- end, ejabberd_config:get_myhosts())),
+ end, ejabberd_option:hosts())),
Results = lists:map(
fun({DBType, Host}) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
@@ -622,8 +624,8 @@ drop_online_sessions(LUser, LServer, Clients) ->
-spec make_summary(binary(), xmpp_element() | xmlel() | none, direction())
-> xdata() | undefined.
make_summary(Host, #message{from = From} = Pkt, recv) ->
- case {gen_mod:get_module_opt(Host, ?MODULE, include_sender),
- gen_mod:get_module_opt(Host, ?MODULE, include_body)} of
+ case {mod_push_opt:include_sender(Host),
+ mod_push_opt:include_body(Host)} of
{false, false} ->
undefined;
{IncludeSender, IncludeBody} ->
@@ -714,9 +716,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_push_opt:cache_size(Opts),
+ CacheMissed = mod_push_opt:cache_missed(Opts),
+ LifeTime = case mod_push_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -726,7 +728,7 @@ cache_opts(Opts) ->
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)
+ false -> mod_push_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl
index a7a7d8c92..574b1d7aa 100644
--- a/src/mod_push_keepalive.erl
+++ b/src/mod_push_keepalive.erl
@@ -47,7 +47,7 @@
%%--------------------------------------------------------------------
-spec start(binary(), gen_mod:opts()) -> ok.
start(Host, Opts) ->
- case gen_mod:get_opt(wake_on_start, Opts) of
+ case mod_push_keepalive_opt:wake_on_start(Opts) of
true ->
wake_all(Host);
false ->
@@ -61,13 +61,13 @@ stop(Host) ->
-spec reload(binary(), gen_mod:opts(), gen_mod:opts()) -> ok.
reload(Host, NewOpts, OldOpts) ->
- case gen_mod:is_equal_opt(wake_on_start, NewOpts, OldOpts) of
- {false, true, _} ->
+ case {mod_push_keepalive_opt:wake_on_start(NewOpts),
+ mod_push_keepalive_opt:wake_on_start(OldOpts)} of
+ {true, false} ->
wake_all(Host);
_ ->
ok
- end,
- ok.
+ end.
-spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}].
depends(_Host, _Opts) ->
@@ -75,15 +75,13 @@ depends(_Host, _Opts) ->
{mod_client_state, soft},
{mod_stream_mgmt, soft}].
--spec mod_opt_type(atom()) -> fun((term()) -> term()) | [atom()].
+-spec mod_opt_type(atom()) -> econf:validator().
mod_opt_type(resume_timeout) ->
- fun(I) when is_integer(I), I >= 0 -> I;
- (undefined) -> undefined
- end;
+ econf:non_neg_int();
mod_opt_type(wake_on_start) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(wake_on_timeout) ->
- fun (B) when is_boolean(B) -> B end.
+ econf:bool().
mod_options(_Host) ->
[{resume_timeout, 259200},
@@ -176,8 +174,8 @@ c2s_copy_session(State, _) ->
-spec c2s_handle_cast(c2s_state(), any()) -> c2s_state().
c2s_handle_cast(#{lserver := LServer} = State, push_enable) ->
- ResumeTimeout = gen_mod:get_module_opt(LServer, ?MODULE, resume_timeout),
- WakeOnTimeout = gen_mod:get_module_opt(LServer, ?MODULE, wake_on_timeout),
+ ResumeTimeout = mod_push_keepalive_opt:resume_timeout(LServer),
+ WakeOnTimeout = mod_push_keepalive_opt:wake_on_timeout(LServer),
State#{push_resume_timeout => ResumeTimeout,
push_wake_on_timeout => WakeOnTimeout};
c2s_handle_cast(State, push_disable) ->
@@ -226,7 +224,7 @@ maybe_start_wakeup_timer(#{push_wake_on_timeout := true,
maybe_start_wakeup_timer(State) ->
State.
--spec wake_all(binary()) -> ok | error.
+-spec wake_all(binary()) -> ok.
wake_all(LServer) ->
?INFO_MSG("Waking all push clients on ~s", [LServer]),
Mod = gen_mod:db_mod(LServer, mod_push),
@@ -239,5 +237,5 @@ wake_all(LServer) ->
IgnoreResponse)
end, Sessions);
error ->
- error
+ ok
end.
diff --git a/src/mod_push_keepalive_opt.erl b/src/mod_push_keepalive_opt.erl
new file mode 100644
index 000000000..82b1d51bb
--- /dev/null
+++ b/src/mod_push_keepalive_opt.erl
@@ -0,0 +1,27 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_push_keepalive_opt).
+
+-export([resume_timeout/1]).
+-export([wake_on_start/1]).
+-export([wake_on_timeout/1]).
+
+-spec resume_timeout(gen_mod:opts() | global | binary()) -> non_neg_integer().
+resume_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(resume_timeout, Opts);
+resume_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_push_keepalive, resume_timeout).
+
+-spec wake_on_start(gen_mod:opts() | global | binary()) -> boolean().
+wake_on_start(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(wake_on_start, Opts);
+wake_on_start(Host) ->
+ gen_mod:get_module_opt(Host, mod_push_keepalive, wake_on_start).
+
+-spec wake_on_timeout(gen_mod:opts() | global | binary()) -> boolean().
+wake_on_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(wake_on_timeout, Opts);
+wake_on_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_push_keepalive, wake_on_timeout).
+
diff --git a/src/mod_push_opt.erl b/src/mod_push_opt.erl
new file mode 100644
index 000000000..6ab94b9c7
--- /dev/null
+++ b/src/mod_push_opt.erl
@@ -0,0 +1,55 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_push_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([include_body/1]).
+-export([include_sender/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, db_type).
+
+-spec include_body(gen_mod:opts() | global | binary()) -> boolean() | binary().
+include_body(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(include_body, Opts);
+include_body(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, include_body).
+
+-spec include_sender(gen_mod:opts() | global | binary()) -> boolean().
+include_sender(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(include_sender, Opts);
+include_sender(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, include_sender).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_push, use_cache).
+
diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl
index 50ad30684..e665c1ce5 100644
--- a/src/mod_push_sql.erl
+++ b/src/mod_push_sql.erl
@@ -25,7 +25,6 @@
-module(mod_push_sql).
-behaviour(mod_push).
--compile([{parse_transform, ejabberd_sql_pt}]).
%% API
-export([init/2, store_session/6, lookup_session/4, lookup_session/3,
diff --git a/src/mod_register.erl b/src/mod_register.erl
index 3ac2ef3f5..9bbb02006 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -25,8 +25,6 @@
-module(mod_register).
--behaviour(ejabberd_config).
-
-author('alexey@process-one.net').
-protocol({xep, 77, '2.4'}).
@@ -36,8 +34,7 @@
-export([start/2, stop/1, reload/3, stream_feature_register/2,
c2s_unauthenticated_packet/2, try_register/4,
process_iq/1, send_registration_notifications/3,
- transform_options/1, transform_module_options/1,
- mod_opt_type/1, mod_options/1, opt_type/1, depends/2,
+ mod_opt_type/1, mod_options/1, depends/2,
format_error/1]).
-include("logger.hrl").
@@ -76,11 +73,11 @@ depends(_Host, _Opts) ->
-spec stream_feature_register([xmpp_element()], binary()) -> [xmpp_element()].
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;
+ case {mod_register_opt:access(Host),
+ mod_register_opt:ip_access(Host),
+ mod_register_opt:redirect_url(Host)} of
+ {none, _, undefined} -> Acc;
+ {_, none, undefined} -> Acc;
{_, _, _} -> [#feature_register{}|Acc]
end.
@@ -111,12 +108,12 @@ process_iq(#iq{from = From} = IQ) ->
process_iq(#iq{from = From, to = To} = IQ, Source) ->
IsCaptchaEnabled =
- case gen_mod:get_module_opt(To#jid.lserver, ?MODULE, captcha_protected) of
+ case mod_register_opt:captcha_protected(To#jid.lserver) of
true -> true;
false -> false
end,
Server = To#jid.lserver,
- Access = gen_mod:get_module_opt(Server, ?MODULE, access_remove),
+ Access = mod_register_opt:access_remove(Server),
AllowRemove = allow == acl:match_rule(Server, Access, From),
process_iq(IQ, Source, IsCaptchaEnabled, AllowRemove).
@@ -206,8 +203,8 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ,
Instr = translate:translate(
Lang, <<"Choose a username and password to register "
"with this server">>),
- URL = gen_mod:get_module_opt(Server, ?MODULE, redirect_url),
- if (URL /= <<"">>) and not IsRegistered ->
+ URL = mod_register_opt:redirect_url(Server),
+ if (URL /= undefined) and not IsRegistered ->
Txt = translate:translate(Lang, <<"To register, visit ~s">>),
Desc = str:format(Txt, [URL]),
xmpp:make_iq_result(
@@ -400,20 +397,19 @@ format_error(Unexpected) ->
send_welcome_message(JID) ->
Host = JID#jid.lserver,
- case gen_mod:get_module_opt(Host, ?MODULE, welcome_message) of
+ case mod_register_opt:welcome_message(Host) of
{<<"">>, <<"">>} -> ok;
{Subj, Body} ->
ejabberd_router:route(
#message{from = jid:make(Host),
to = JID,
subject = xmpp:mk_text(Subj),
- body = xmpp:mk_text(Body)});
- _ -> ok
+ body = xmpp:mk_text(Body)})
end.
send_registration_notifications(Mod, UJID, Source) ->
Host = UJID#jid.lserver,
- case gen_mod:get_module_opt(Host, ?MODULE, registration_watchers) of
+ case mod_register_opt:registration_watchers(Host) of
[] -> ok;
JIDs when is_list(JIDs) ->
Body =
@@ -438,12 +434,12 @@ check_from(#jid{user = <<"">>, server = <<"">>},
_Server) ->
allow;
check_from(JID, Server) ->
- Access = gen_mod:get_module_opt(Server, ?MODULE, access_from),
+ Access = mod_register_opt:access_from(Server),
acl:match_rule(Server, Access, JID).
check_timeout(undefined) -> true;
check_timeout(Source) ->
- Timeout = ejabberd_config:get_option(registration_timeout, 600),
+ Timeout = ejabberd_option:registration_timeout(),
if is_integer(Timeout) ->
Priority = -erlang:system_time(second),
CleanPriority = Priority + Timeout,
@@ -488,7 +484,7 @@ clean_treap(Treap, CleanPriority) ->
remove_timeout(undefined) -> true;
remove_timeout(Source) ->
- Timeout = ejabberd_config:get_option(registration_timeout, 600),
+ Timeout = ejabberd_option:registration_timeout(),
if is_integer(Timeout) ->
F = fun () ->
Treap = case mnesia:read(mod_register_ip, treap, write)
@@ -542,61 +538,13 @@ is_strong_password(Server, Password) ->
is_strong_password2(Server, Password) ->
LServer = jid:nameprep(Server),
- case gen_mod:get_module_opt(LServer, ?MODULE, password_strength) of
+ case mod_register_opt:password_strength(LServer) of
0 ->
true;
Entropy ->
ejabberd_auth:entropy(Password) >= Entropy
end.
-transform_options(Opts) ->
- Opts1 = transform_ip_access(Opts),
- transform_module_options(Opts1).
-
-transform_ip_access(Opts) ->
- try
- {value, {modules, ModOpts}, Opts1} = lists:keytake(modules, 1, Opts),
- {value, {?MODULE, RegOpts}, ModOpts1} = lists:keytake(?MODULE, 1, ModOpts),
- {value, {ip_access, L}, RegOpts1} = lists:keytake(ip_access, 1, RegOpts),
- true = is_list(L),
- ?WARNING_MSG("Old 'ip_access' format detected. "
- "The old format is still supported "
- "but it is better to fix your config: "
- "use access rules instead.", []),
- ACLs = lists:flatmap(
- fun({Action, S}) ->
- ACLName = misc:binary_to_atom(
- iolist_to_binary(
- ["ip_", S])),
- [{Action, ACLName},
- {acl, ACLName, {ip, S}}]
- end, L),
- Access = {access, mod_register_networks,
- [{Action, ACLName} || {Action, ACLName} <- ACLs]},
- [ACL || {acl, _, _} = ACL <- ACLs] ++
- [Access,
- {modules,
- [{mod_register,
- [{ip_access, mod_register_networks}|RegOpts1]}
- | ModOpts1]}|Opts1]
- catch error:{badmatch, false} ->
- Opts
- end.
-
-transform_module_options(Opts) ->
- lists:flatmap(
- fun({welcome_message, {Subj, Body}}) ->
- ?WARNING_MSG("Old 'welcome_message' format detected. "
- "The old format is still supported "
- "but it is better to fix your config: "
- "change it to {welcome_message, "
- "[{subject, Subject}, {body, Body}]}",
- []),
- [{welcome_message, [{subject, Subj}, {body, Body}]}];
- (Opt) ->
- [Opt]
- end, Opts).
-
%%%
%%% ip_access management
%%%
@@ -606,7 +554,7 @@ may_remove_resource({_, _, _} = From) ->
may_remove_resource(From) -> From.
get_ip_access(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, ip_access).
+ mod_register_opt:ip_access(Host).
check_ip_access({User, Server, Resource}, IPAccess) ->
case ejabberd_sm:get_user_ip(User, Server, Resource) of
@@ -622,39 +570,41 @@ check_ip_access(IPAddress, IPAccess) ->
check_access(User, Server, Source) ->
JID = jid:make(User, Server),
- Access = gen_mod:get_module_opt(Server, ?MODULE, access),
+ Access = mod_register_opt:access(Server),
IPAccess = get_ip_access(Server),
case acl:match_rule(Server, Access, JID) of
allow -> check_ip_access(Source, IPAccess);
deny -> deny
end.
-mod_opt_type(access) -> fun acl:access_rules_validator/1;
-mod_opt_type(access_from) -> fun acl:access_rules_validator/1;
-mod_opt_type(access_remove) -> fun acl:access_rules_validator/1;
+mod_opt_type(access) ->
+ econf:acl();
+mod_opt_type(access_from) ->
+ econf:acl();
+mod_opt_type(access_remove) ->
+ econf:acl();
mod_opt_type(captcha_protected) ->
- fun (B) when is_boolean(B) -> B end;
-mod_opt_type(ip_access) -> fun acl:access_rules_validator/1;
+ econf:bool();
+mod_opt_type(ip_access) ->
+ econf:acl();
mod_opt_type(password_strength) ->
- fun (N) when is_number(N), N >= 0 -> N end;
+ econf:number(0);
mod_opt_type(registration_watchers) ->
- fun (Ss) ->
- [jid:decode(iolist_to_binary(S)) || S <- Ss]
- end;
+ econf:list(econf:jid());
mod_opt_type(welcome_message) ->
- fun(L) ->
- {proplists:get_value(subject, L, <<"">>),
- proplists:get_value(body, L, <<"">>)}
- end;
-mod_opt_type({welcome_message, subject}) ->
- fun iolist_to_binary/1;
-mod_opt_type({welcome_message, body}) ->
- fun iolist_to_binary/1;
+ econf:and_then(
+ econf:options(
+ #{subject => econf:binary(),
+ body => econf:binary()}),
+ fun(Opts) ->
+ {proplists:get_value(subject, Opts, <<>>),
+ proplists:get_value(body, Opts, <<>>)}
+ end);
mod_opt_type(redirect_url) ->
- fun(<<>>) -> <<>>;
- (URL) -> misc:try_url(URL)
- end.
+ econf:url().
+-spec mod_options(binary()) -> [{welcome_message, {binary(), binary()}} |
+ {atom(), term()}].
mod_options(_Host) ->
[{access, all},
{access_from, none},
@@ -663,15 +613,5 @@ mod_options(_Host) ->
{ip_access, all},
{password_strength, 0},
{registration_watchers, []},
- {redirect_url, <<"">>},
- {welcome_message,
- [{subject, <<"">>},
- {body, <<"">>}]}].
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(registration_timeout) ->
- fun (TO) when is_integer(TO), TO > 0 -> TO;
- (infinity) -> infinity;
- (unlimited) -> infinity
- end;
-opt_type(_) -> [registration_timeout].
+ {redirect_url, undefined},
+ {welcome_message, {<<>>, <<>>}}].
diff --git a/src/mod_register_opt.erl b/src/mod_register_opt.erl
new file mode 100644
index 000000000..53c6ca6ea
--- /dev/null
+++ b/src/mod_register_opt.erl
@@ -0,0 +1,69 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_register_opt).
+
+-export([access/1]).
+-export([access_from/1]).
+-export([access_remove/1]).
+-export([captcha_protected/1]).
+-export([ip_access/1]).
+-export([password_strength/1]).
+-export([redirect_url/1]).
+-export([registration_watchers/1]).
+-export([welcome_message/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, access).
+
+-spec access_from(gen_mod:opts() | global | binary()) -> 'none' | acl:acl().
+access_from(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_from, Opts);
+access_from(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, access_from).
+
+-spec access_remove(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access_remove(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access_remove, Opts);
+access_remove(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, access_remove).
+
+-spec captcha_protected(gen_mod:opts() | global | binary()) -> boolean().
+captcha_protected(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(captcha_protected, Opts);
+captcha_protected(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, captcha_protected).
+
+-spec ip_access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+ip_access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ip_access, Opts);
+ip_access(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, ip_access).
+
+-spec password_strength(gen_mod:opts() | global | binary()) -> number().
+password_strength(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(password_strength, Opts);
+password_strength(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, password_strength).
+
+-spec redirect_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
+redirect_url(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(redirect_url, Opts);
+redirect_url(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, redirect_url).
+
+-spec registration_watchers(gen_mod:opts() | global | binary()) -> [jid:jid()].
+registration_watchers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(registration_watchers, Opts);
+registration_watchers(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, registration_watchers).
+
+-spec welcome_message(gen_mod:opts() | global | binary()) -> {binary(),binary()}.
+welcome_message(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(welcome_message, Opts);
+welcome_message(Host) ->
+ gen_mod:get_module_opt(Host, mod_register, welcome_message).
+
diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl
index 4cd0deb53..689d1383d 100644
--- a/src/mod_register_web.erl
+++ b/src/mod_register_web.erl
@@ -70,7 +70,7 @@
%%%----------------------------------------------------------------------
start(_Host, _Opts) ->
- %% case gen_mod:get_opt(docroot, Opts, fun(A) -> A end, undefined) of
+ %% case mod_register_web_opt:docroot(Opts, fun(A) -> A end, undefined) of
ok.
stop(_Host) -> ok.
@@ -361,15 +361,18 @@ build_captcha_li_list2(Lang, IP) ->
To = #jid{user = <<"">>, server = <<"test">>,
resource = <<"">>},
Args = [],
- case ejabberd_captcha:create_captcha(SID, From, To,
- Lang, IP, Args)
- of
- {ok, Id, _, _} ->
- {_, {CImg, CText, CId, CKey}} =
- ejabberd_captcha:build_captcha_html(Id, Lang),
- [?XE(<<"li">>,
- [CText, ?C(<<" ">>), CId, CKey, ?BR, CImg])];
- Error -> throw(Error)
+ case ejabberd_captcha:create_captcha(
+ SID, From, To, Lang, IP, Args) of
+ {ok, Id, _, _} ->
+ case ejabberd_captcha:build_captcha_html(Id, Lang) of
+ {_, {CImg, CText, CId, CKey}} ->
+ [?XE(<<"li">>,
+ [CText, ?C(<<" ">>), CId, CKey, ?BR, CImg])];
+ Error ->
+ throw(Error)
+ end;
+ Error ->
+ throw(Error)
end.
%%%----------------------------------------------------------------------
@@ -525,7 +528,7 @@ form_del_get(Host, Lang) ->
%% {error, not_allowed} |
%% {error, invalid_jid}
register_account(Username, Host, Password) ->
- Access = gen_mod:get_module_opt(Host, mod_register, access),
+ Access = mod_register_opt:access(Host),
case jid:make(Username, Host) of
error -> {error, invalid_jid};
JID ->
@@ -589,8 +592,6 @@ unregister_account(Username, Host, Password) ->
get_error_text({error, captcha_non_valid}) ->
<<"The captcha you entered is wrong">>;
-get_error_text({success, exists, _}) ->
- get_error_text({error, exists});
get_error_text({error, exists}) ->
<<"The account already exists">>;
get_error_text({error, password_incorrect}) ->
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index d313c2415..a29259c57 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -85,7 +85,7 @@
-optional_callbacks([use_cache/2, cache_nodes/1]).
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(roster_get, Host, ?MODULE,
@@ -132,8 +132,8 @@ stop(Host) ->
?NS_ROSTER).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -172,7 +172,7 @@ process_local_iq(#iq{type = set, from = From, lang = Lang,
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
false ->
#jid{lserver = LServer} = From,
- Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
+ Access = mod_roster_opt:access(LServer),
case acl:match_rule(LServer, Access, From) of
deny ->
Txt = <<"Access denied by service policy">>,
@@ -205,10 +205,10 @@ roster_hash(Items) ->
<- Items]))).
roster_versioning_enabled(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, versioning).
+ mod_roster_opt:versioning(Host).
roster_version_on_db(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, store_current_id).
+ mod_roster_opt:store_current_id(Host).
%% Returns a list that may contain an xmlelement with the XEP-237 feature if it's enabled.
-spec get_versioning_feature([xmpp_element()], binary()) -> [xmpp_element()].
@@ -489,8 +489,7 @@ push_item(To, OldItem, NewItem) ->
#jid{luser = LUser, lserver = LServer} = To,
Ver = case roster_versioning_enabled(LServer) of
true -> roster_version(LServer, LUser);
- false -> undefined;
- undefined -> undefined
+ false -> undefined
end,
lists:foreach(
fun(Resource) ->
@@ -1011,7 +1010,7 @@ build_contact_jid_td(RosterJID) ->
of
{<<"">>, _} -> <<"">>;
{CUser, CServer} ->
- case lists:member(CServer, ejabberd_config:get_myhosts()) of
+ case lists:member(CServer, ejabberd_option:hosts()) of
false -> <<"">>;
true ->
<<"/admin/server/", CServer/binary, "/user/",
@@ -1129,9 +1128,9 @@ init_cache(Mod, Host, Opts) ->
-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
+ MaxSize = mod_roster_opt:cache_size(Opts),
+ CacheMissed = mod_roster_opt:cache_missed(Opts),
+ LifeTime = case mod_roster_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -1141,7 +1140,7 @@ cache_opts(Opts) ->
use_cache(Mod, Host, Table) ->
case erlang:function_exported(Mod, use_cache, 2) of
true -> Mod:use_cache(Host, Table);
- false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
+ false -> mod_roster_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -1215,25 +1214,28 @@ import(LServer, {sql, _}, DBType, <<"roster_version">>, [LUser, Ver]) ->
Mod:import(LServer, <<"roster_version">>, [LUser, Ver]).
mod_opt_type(access) ->
- fun acl:access_rules_validator/1;
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end;
+ econf:acl();
mod_opt_type(store_current_id) ->
- fun (B) when is_boolean(B) -> B end;
+ econf:bool();
mod_opt_type(versioning) ->
- fun (B) when is_boolean(B) -> B 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.
+ econf:bool();
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
[{access, all},
{store_current_id, false},
{versioning, false},
{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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
index 41da9a6b4..cee22d06b 100644
--- a/src/mod_roster_mnesia.erl
+++ b/src/mod_roster_mnesia.erl
@@ -55,7 +55,7 @@ init(_Host, _Opts) ->
use_cache(Host, Table) ->
case mnesia:table_info(Table, storage_type) of
disc_only_copies ->
- gen_mod:get_module_opt(Host, mod_roster, use_cache);
+ mod_roster_opt:use_cache(Host);
_ ->
false
end.
@@ -122,10 +122,11 @@ import(LServer, <<"roster_version">>, [LUser, Ver]) ->
RV = #roster_version{us = {LUser, LServer}, version = Ver},
mnesia:dirty_write(RV).
-need_transform(#roster{usj = {U, S, _}}) when is_list(U) orelse is_list(S) ->
+need_transform({roster, {U, S, _}, _, _, _, _, _, _, _, _})
+ when is_list(U) orelse is_list(S) ->
?INFO_MSG("Mnesia table 'roster' will be converted to binary", []),
true;
-need_transform(#roster_version{us = {U, S}, version = Ver})
+need_transform({roster_version, {U, S}, Ver})
when is_list(U) orelse is_list(S) orelse is_list(Ver) ->
?INFO_MSG("Mnesia table 'roster_version' will be converted to binary", []),
true;
diff --git a/src/mod_roster_opt.erl b/src/mod_roster_opt.erl
new file mode 100644
index 000000000..4275bf4e2
--- /dev/null
+++ b/src/mod_roster_opt.erl
@@ -0,0 +1,62 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_roster_opt).
+
+-export([access/1]).
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([store_current_id/1]).
+-export([use_cache/1]).
+-export([versioning/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, access).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, db_type).
+
+-spec store_current_id(gen_mod:opts() | global | binary()) -> boolean().
+store_current_id(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(store_current_id, Opts);
+store_current_id(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, store_current_id).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, use_cache).
+
+-spec versioning(gen_mod:opts() | global | binary()) -> boolean().
+versioning(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(versioning, Opts);
+versioning(Host) ->
+ gen_mod:get_module_opt(Host, mod_roster, versioning).
+
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl
index a512f1bf5..f62695a89 100644
--- a/src/mod_roster_sql.erl
+++ b/src/mod_roster_sql.erl
@@ -24,7 +24,6 @@
-module(mod_roster_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_roster).
diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl
index 55854a82b..e64a0a935 100644
--- a/src/mod_s2s_dialback.erl
+++ b/src/mod_s2s_dialback.erl
@@ -21,16 +21,18 @@
%%%-------------------------------------------------------------------
-module(mod_s2s_dialback).
-behaviour(gen_mod).
-
+-dialyzer([{no_fail_call, s2s_out_packet/2},
+ {no_return, s2s_out_packet/2}]).
-protocol({xep, 220, '1.1.1'}).
-protocol({xep, 185, '1.0'}).
%% gen_mod API
--export([start/2, stop/1, reload/3, depends/2, mod_options/1]).
+-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
%% Hooks
-export([s2s_out_auth_result/2, s2s_out_downgraded/2,
s2s_in_packet/2, s2s_out_packet/2, s2s_in_recv/3,
- s2s_in_features/2, s2s_out_init/2, s2s_out_closed/2]).
+ s2s_in_features/2, s2s_out_init/2, s2s_out_closed/2,
+ s2s_out_tls_verify/2]).
-include("xmpp.hrl").
-include("logger.hrl").
@@ -39,31 +41,26 @@
%%% API
%%%===================================================================
start(Host, _Opts) ->
- case ejabberd_s2s:tls_verify(Host) of
- true ->
- ?ERROR_MSG("disabling ~s for host ~s because option "
- "'s2s_use_starttls' is set to 'required_trusted'",
- [?MODULE, Host]);
- false ->
- ejabberd_hooks:add(s2s_out_init, Host, ?MODULE, s2s_out_init, 50),
- ejabberd_hooks:add(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50),
- ejabberd_hooks:add(s2s_in_pre_auth_features, Host, ?MODULE,
- s2s_in_features, 50),
- ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE,
- s2s_in_features, 50),
- ejabberd_hooks:add(s2s_in_handle_recv, Host, ?MODULE,
- s2s_in_recv, 50),
- ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE,
- s2s_in_packet, 50),
- ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE,
- s2s_in_packet, 50),
- ejabberd_hooks:add(s2s_out_packet, Host, ?MODULE,
- s2s_out_packet, 50),
- ejabberd_hooks:add(s2s_out_downgraded, Host, ?MODULE,
- s2s_out_downgraded, 50),
- ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE,
- s2s_out_auth_result, 50)
- end.
+ ejabberd_hooks:add(s2s_out_init, Host, ?MODULE, s2s_out_init, 50),
+ ejabberd_hooks:add(s2s_out_closed, Host, ?MODULE, s2s_out_closed, 50),
+ ejabberd_hooks:add(s2s_in_pre_auth_features, Host, ?MODULE,
+ s2s_in_features, 50),
+ ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE,
+ s2s_in_features, 50),
+ ejabberd_hooks:add(s2s_in_handle_recv, Host, ?MODULE,
+ s2s_in_recv, 50),
+ ejabberd_hooks:add(s2s_in_unauthenticated_packet, Host, ?MODULE,
+ s2s_in_packet, 50),
+ ejabberd_hooks:add(s2s_in_authenticated_packet, Host, ?MODULE,
+ s2s_in_packet, 50),
+ ejabberd_hooks:add(s2s_out_packet, Host, ?MODULE,
+ s2s_out_packet, 50),
+ ejabberd_hooks:add(s2s_out_downgraded, Host, ?MODULE,
+ s2s_out_downgraded, 50),
+ ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE,
+ s2s_out_auth_result, 50),
+ ejabberd_hooks:add(s2s_out_tls_verify, Host, ?MODULE,
+ s2s_out_tls_verify, 50).
stop(Host) ->
ejabberd_hooks:delete(s2s_out_init, Host, ?MODULE, s2s_out_init, 50),
@@ -83,21 +80,21 @@ stop(Host) ->
ejabberd_hooks:delete(s2s_out_downgraded, Host, ?MODULE,
s2s_out_downgraded, 50),
ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE,
- s2s_out_auth_result, 50).
+ s2s_out_auth_result, 50),
+ ejabberd_hooks:delete(s2s_out_tls_verify, Host, ?MODULE,
+ s2s_out_tls_verify, 50).
-reload(Host, NewOpts, _OldOpts) ->
- case ejabberd_s2s:tls_verify(Host) of
- false ->
- start(Host, NewOpts);
- true ->
- stop(Host)
- end.
+reload(_Host, _NewOpts, _OldOpts) ->
+ ok.
depends(_Host, _Opts) ->
[].
+mod_opt_type(access) ->
+ econf:acl().
+
mod_options(_Host) ->
- [].
+ [{access, all}].
s2s_in_features(Acc, _) ->
[#db_feature{errors = true}|Acc].
@@ -258,12 +255,20 @@ s2s_out_packet(State, Pkt) when is_record(Pkt, db_result);
s2s_out_packet(State, _) ->
State.
+-spec s2s_out_tls_verify(boolean(), ejabberd_s2s_out:state()) -> boolean().
+s2s_out_tls_verify(_, #{server := LServer, remote_server := RServer}) ->
+ Access = mod_s2s_dialback_opt:access(LServer),
+ case acl:match_rule(LServer, Access, jid:make(RServer)) of
+ allow -> false;
+ deny -> true
+ end.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec make_key(binary(), binary(), binary()) -> binary().
make_key(From, To, StreamID) ->
- Secret = ejabberd_config:get_option(shared_key),
+ Secret = ejabberd_config:get_shared_key(),
str:to_hexlist(
crypto:hmac(sha256, str:to_hexlist(crypto:hash(sha256, Secret)),
[To, " ", From, " ", StreamID])).
diff --git a/src/mod_s2s_dialback_opt.erl b/src/mod_s2s_dialback_opt.erl
new file mode 100644
index 000000000..6f91c4dd1
--- /dev/null
+++ b/src/mod_s2s_dialback_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_s2s_dialback_opt).
+
+-export([access/1]).
+
+-spec access(gen_mod:opts() | global | binary()) -> 'all' | acl:acl().
+access(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(access, Opts);
+access(Host) ->
+ gen_mod:get_module_opt(Host, mod_s2s_dialback, access).
+
diff --git a/src/mod_service_log.erl b/src/mod_service_log.erl
index 62b5e289f..eca95cb27 100644
--- a/src/mod_service_log.erl
+++ b/src/mod_service_log.erl
@@ -67,7 +67,7 @@ log_user_receive({Packet, C2SState}) ->
-spec log_packet(stanza(), binary()) -> ok.
log_packet(Packet, Host) ->
- Loggers = gen_mod:get_module_opt(Host, ?MODULE, loggers),
+ Loggers = mod_service_log_opt:loggers(Host),
ForwardedMsg = #message{from = jid:make(Host),
id = p1_rand:get_string(),
sub_els = [#forwarded{
@@ -78,14 +78,7 @@ log_packet(Packet, Host) ->
end, Loggers).
mod_opt_type(loggers) ->
- fun (L) ->
- lists:map(fun (S) ->
- B = iolist_to_binary(S),
- N = jid:nameprep(B),
- if N /= error -> N end
- end,
- L)
- end.
+ econf:list(econf:domain()).
mod_options(_) ->
[{loggers, []}].
diff --git a/src/mod_service_log_opt.erl b/src/mod_service_log_opt.erl
new file mode 100644
index 000000000..34eae49a6
--- /dev/null
+++ b/src/mod_service_log_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_service_log_opt).
+
+-export([loggers/1]).
+
+-spec loggers(gen_mod:opts() | global | binary()) -> [binary()].
+loggers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(loggers, Opts);
+loggers(Host) ->
+ gen_mod:get_module_opt(Host, mod_service_log, loggers).
+
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index d80258db7..7e17e2005 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -71,7 +71,7 @@
-callback remove_user_from_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}.
start(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE,
webadmin_menu, 70),
@@ -122,8 +122,8 @@ stop(Host) ->
50).
reload(Host, NewOpts, OldOpts) ->
- NewMod = gen_mod:db_mod(Host, NewOpts, ?MODULE),
- OldMod = gen_mod:db_mod(Host, OldOpts, ?MODULE),
+ NewMod = gen_mod:db_mod(NewOpts, ?MODULE),
+ OldMod = gen_mod:db_mod(OldOpts, ?MODULE),
if NewMod /= OldMod ->
NewMod:init(Host, NewOpts);
true ->
@@ -662,7 +662,7 @@ push_user_to_group(LUser, LServer, Group, Host,
when (U == LUser) and (S == LServer) ->
ok;
({U, S}) ->
- case lists:member(S, ejabberd_config:get_myhosts()) of
+ case lists:member(S, ejabberd_option:hosts()) of
true ->
push_roster_item(U, S, LUser, LServer, GroupName,
Subscription);
@@ -1007,7 +1007,8 @@ import(LServer, {sql, _}, DBType, Tab, L) ->
Mod = gen_mod:db_mod(DBType, ?MODULE),
Mod:import(LServer, Tab, L).
-mod_opt_type(db_type) -> fun(T) -> ejabberd_config:v_db(?MODULE, T) end.
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE).
mod_options(Host) ->
[{db_type, ejabberd_config:default_db(Host, ?MODULE)}].
diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl
index 327ec0a9d..524f65113 100644
--- a/src/mod_shared_roster_ldap.erl
+++ b/src/mod_shared_roster_ldap.erl
@@ -26,8 +26,6 @@
%%%-------------------------------------------------------------------
-module(mod_shared_roster_ldap).
--behaviour(ejabberd_config).
-
-behaviour(gen_server).
-behaviour(gen_mod).
@@ -42,7 +40,7 @@
-export([get_user_roster/2,
get_jid_info/4, process_item/2, in_subscription/2,
out_subscription/1, mod_opt_type/1, mod_options/1,
- opt_type/1, depends/2, transform_module_options/1]).
+ depends/2]).
-include("logger.hrl").
-include("xmpp.hrl").
@@ -72,7 +70,7 @@
user_desc = <<"">> :: binary(),
user_uid = <<"">> :: binary(),
uid_format = <<"">> :: binary(),
- uid_format_re = <<"">> :: binary(),
+ uid_format_re :: undefined | re:mp(),
filter = <<"">> :: binary(),
ufilter = <<"">> :: binary(),
rfilter = <<"">> :: binary(),
@@ -351,7 +349,7 @@ get_user_name(User, Host) ->
search_group_info(State, Group) ->
Extractor = case State#state.uid_format_re of
- <<"">> ->
+ undefined ->
fun (UID) ->
catch eldap_utils:get_user_part(UID,
State#state.uid_format)
@@ -440,22 +438,22 @@ get_user_part_re(String, Pattern) ->
parse_options(Host, Opts) ->
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?MODULE)),
- Cfg = eldap_utils:get_config(Host, Opts),
- GroupAttr = gen_mod:get_opt(ldap_groupattr, Opts),
- GroupDesc = case gen_mod:get_opt(ldap_groupdesc, Opts) of
+ Cfg = ?eldap_config(mod_shared_roster_ldap_opt, Opts),
+ GroupAttr = mod_shared_roster_ldap_opt:ldap_groupattr(Opts),
+ GroupDesc = case mod_shared_roster_ldap_opt:ldap_groupdesc(Opts) of
undefined -> GroupAttr;
GD -> GD
end,
- UserDesc = gen_mod:get_opt(ldap_userdesc, Opts),
- UserUID = gen_mod:get_opt(ldap_useruid, Opts),
- UIDAttr = gen_mod:get_opt(ldap_memberattr, Opts),
- UIDAttrFormat = gen_mod:get_opt(ldap_memberattr_format, Opts),
- UIDAttrFormatRe = gen_mod:get_opt(ldap_memberattr_format_re, Opts),
- AuthCheck = gen_mod:get_opt(ldap_auth_check, Opts),
- ConfigFilter = gen_mod:get_opt(ldap_filter, Opts),
- ConfigUserFilter = gen_mod:get_opt(ldap_ufilter, Opts),
- ConfigGroupFilter = gen_mod:get_opt(ldap_gfilter, Opts),
- RosterFilter = gen_mod:get_opt(ldap_rfilter, Opts),
+ UserDesc = mod_shared_roster_ldap_opt:ldap_userdesc(Opts),
+ UserUID = mod_shared_roster_ldap_opt:ldap_useruid(Opts),
+ UIDAttr = mod_shared_roster_ldap_opt:ldap_memberattr(Opts),
+ UIDAttrFormat = mod_shared_roster_ldap_opt:ldap_memberattr_format(Opts),
+ UIDAttrFormatRe = mod_shared_roster_ldap_opt:ldap_memberattr_format_re(Opts),
+ AuthCheck = mod_shared_roster_ldap_opt:ldap_auth_check(Opts),
+ ConfigFilter = mod_shared_roster_ldap_opt:ldap_filter(Opts),
+ ConfigUserFilter = mod_shared_roster_ldap_opt:ldap_ufilter(Opts),
+ ConfigGroupFilter = mod_shared_roster_ldap_opt:ldap_gfilter(Opts),
+ RosterFilter = mod_shared_roster_ldap_opt:ldap_rfilter(Opts),
SubFilter = <<"(&(", UIDAttr/binary, "=",
UIDAttrFormat/binary, ")(", GroupAttr/binary, "=%g))">>,
UserSubFilter = case ConfigUserFilter of
@@ -516,103 +514,110 @@ init_cache(Host, Opts) ->
UseCache.
use_cache(_Host, Opts) ->
- gen_mod:get_opt(use_cache, Opts).
+ mod_shared_roster_ldap_opt:use_cache(Opts).
cache_opts(_Host, 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
+ MaxSize = mod_shared_roster_ldap_opt:cache_size(Opts),
+ CacheMissed = mod_shared_roster_ldap_opt:cache_missed(Opts),
+ LifeTime = case mod_shared_roster_ldap_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
-transform_module_options(Opts) ->
- lists:map(
- fun({ldap_group_cache_size, I}) ->
- ?WARNING_MSG("Option 'ldap_group_cache_size' is deprecated, "
- "use 'cache_size' instead", []),
- {cache_size, I};
- ({ldap_user_cache_size, I}) ->
- ?WARNING_MSG("Option 'ldap_user_cache_size' is deprecated, "
- "use 'cache_size' instead", []),
- {cache_size, I};
- ({ldap_group_cache_validity, Secs}) ->
- ?WARNING_MSG("Option 'ldap_group_cache_validity' is deprecated, "
- "use 'cache_life_time' instead", []),
- {cache_life_time, Secs};
- ({ldap_user_cache_validity, Secs}) ->
- ?WARNING_MSG("Option 'ldap_user_cache_validity' is deprecated, "
- "use 'cache_life_time' instead", []),
- {cache_life_time, Secs};
- (Opt) ->
- Opt
- end, Opts).
-
mod_opt_type(ldap_auth_check) ->
- fun (on) -> true;
- (off) -> false;
- (false) -> false;
- (true) -> true
- end;
+ econf:bool();
mod_opt_type(ldap_gfilter) ->
- opt_type(ldap_gfilter);
-mod_opt_type(O) when O == cache_size;
- O == cache_life_time ->
- 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_opt_type(ldap_groupattr) -> fun iolist_to_binary/1;
+ econf:ldap_filter();
+mod_opt_type(ldap_groupattr) ->
+ econf:binary();
mod_opt_type(ldap_groupdesc) ->
- fun(undefined) -> undefined;
- (G) -> iolist_to_binary(G)
- end;
-mod_opt_type(ldap_memberattr) -> fun iolist_to_binary/1;
+ econf:binary();
+mod_opt_type(ldap_memberattr) ->
+ econf:binary();
mod_opt_type(ldap_memberattr_format) ->
- fun iolist_to_binary/1;
+ econf:binary();
mod_opt_type(ldap_memberattr_format_re) ->
- fun (S) ->
- Re = iolist_to_binary(S),
- case Re of
- <<>> -> <<>>;
- _ -> {ok, MP} = re:compile(Re), MP
- end
- end;
+ econf:re();
mod_opt_type(ldap_rfilter) ->
- opt_type(ldap_rfilter);
+ econf:ldap_filter();
mod_opt_type(ldap_ufilter) ->
- opt_type(ldap_ufilter);
-mod_opt_type(ldap_userdesc) -> fun iolist_to_binary/1;
-mod_opt_type(ldap_useruid) -> fun iolist_to_binary/1;
-mod_opt_type(Opt) ->
- eldap_utils:opt_type(Opt).
-
+ econf:ldap_filter();
+mod_opt_type(ldap_userdesc) ->
+ econf:binary();
+mod_opt_type(ldap_useruid) ->
+ econf:binary();
+mod_opt_type(ldap_backups) ->
+ econf:list(econf:domain(), [unique]);
+mod_opt_type(ldap_base) ->
+ econf:binary();
+mod_opt_type(ldap_deref_aliases) ->
+ econf:enum([never, searching, finding, always]);
+mod_opt_type(ldap_encrypt) ->
+ econf:enum([tls, starttls, none]);
+mod_opt_type(ldap_filter) ->
+ econf:ldap_filter();
+mod_opt_type(ldap_password) ->
+ econf:binary();
+mod_opt_type(ldap_port) ->
+ econf:port();
+mod_opt_type(ldap_rootdn) ->
+ econf:binary();
+mod_opt_type(ldap_servers) ->
+ econf:list(econf:domain(), [unique]);
+mod_opt_type(ldap_tls_cacertfile) ->
+ econf:pem();
+mod_opt_type(ldap_tls_certfile) ->
+ econf:pem();
+mod_opt_type(ldap_tls_depth) ->
+ econf:non_neg_int();
+mod_opt_type(ldap_tls_verify) ->
+ econf:enum([hard, soft, false]);
+mod_opt_type(ldap_uids) ->
+ econf:either(
+ econf:list(
+ econf:and_then(
+ econf:binary(),
+ fun(U) -> {U, <<"%u">>} end)),
+ econf:map(econf:binary(), econf:binary(), [unique]));
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
+
+-spec mod_options(binary()) -> [{ldap_uids, [{binary(), binary()}]} |
+ {atom(), any()}].
mod_options(Host) ->
[{ldap_auth_check, true},
- {ldap_gfilter, ejabberd_config:get_option({ldap_gfilter, Host}, <<"">>)},
+ {ldap_gfilter, <<"">>},
{ldap_groupattr, <<"cn">>},
{ldap_groupdesc, undefined},
{ldap_memberattr, <<"memberUid">>},
{ldap_memberattr_format, <<"%u">>},
- {ldap_memberattr_format_re, <<"">>},
- {ldap_rfilter, ejabberd_config:get_option({ldap_rfilter, Host}, <<"">>)},
- {ldap_ufilter, ejabberd_config:get_option({ldap_ufilter, Host}, <<"">>)},
+ {ldap_memberattr_format_re, undefined},
+ {ldap_rfilter, <<"">>},
+ {ldap_ufilter, <<"">>},
{ldap_userdesc, <<"cn">>},
{ldap_useruid, <<"cn">>},
- {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)}
- | lists:map(
- fun({Opt, Default}) ->
- {Opt, ejabberd_config:get_option({Opt, Host}, Default)}
- end, eldap_utils:options(Host))].
-
-opt_type(O) when O == ldap_rfilter; O == ldap_gfilter; O == ldap_ufilter ->
- fun(<<>>) -> <<>>;
- (F) -> eldap_utils:check_filter(F)
- end;
-opt_type(_) ->
- [ldap_gfilter, ldap_rfilter, ldap_ufilter].
+ {ldap_backups, ejabberd_option:ldap_backups(Host)},
+ {ldap_base, ejabberd_option:ldap_base(Host)},
+ {ldap_uids, ejabberd_option:ldap_uids(Host)},
+ {ldap_deref_aliases, ejabberd_option:ldap_deref_aliases(Host)},
+ {ldap_encrypt, ejabberd_option:ldap_encrypt(Host)},
+ {ldap_password, ejabberd_option:ldap_password(Host)},
+ {ldap_port, ejabberd_option:ldap_port(Host)},
+ {ldap_rootdn, ejabberd_option:ldap_rootdn(Host)},
+ {ldap_servers, ejabberd_option:ldap_servers(Host)},
+ {ldap_filter, ejabberd_option:ldap_filter(Host)},
+ {ldap_tls_certfile, ejabberd_option:ldap_tls_certfile(Host)},
+ {ldap_tls_cacertfile, ejabberd_option:ldap_tls_cacertfile(Host)},
+ {ldap_tls_depth, ejabberd_option:ldap_tls_depth(Host)},
+ {ldap_tls_verify, ejabberd_option:ldap_tls_verify(Host)},
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_shared_roster_ldap_opt.erl b/src/mod_shared_roster_ldap_opt.erl
new file mode 100644
index 000000000..5703ed0b0
--- /dev/null
+++ b/src/mod_shared_roster_ldap_opt.erl
@@ -0,0 +1,209 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_shared_roster_ldap_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([ldap_auth_check/1]).
+-export([ldap_backups/1]).
+-export([ldap_base/1]).
+-export([ldap_deref_aliases/1]).
+-export([ldap_encrypt/1]).
+-export([ldap_filter/1]).
+-export([ldap_gfilter/1]).
+-export([ldap_groupattr/1]).
+-export([ldap_groupdesc/1]).
+-export([ldap_memberattr/1]).
+-export([ldap_memberattr_format/1]).
+-export([ldap_memberattr_format_re/1]).
+-export([ldap_password/1]).
+-export([ldap_port/1]).
+-export([ldap_rfilter/1]).
+-export([ldap_rootdn/1]).
+-export([ldap_servers/1]).
+-export([ldap_tls_cacertfile/1]).
+-export([ldap_tls_certfile/1]).
+-export([ldap_tls_depth/1]).
+-export([ldap_tls_verify/1]).
+-export([ldap_ufilter/1]).
+-export([ldap_uids/1]).
+-export([ldap_userdesc/1]).
+-export([ldap_useruid/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, cache_size).
+
+-spec ldap_auth_check(gen_mod:opts() | global | binary()) -> boolean().
+ldap_auth_check(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_auth_check, Opts);
+ldap_auth_check(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_auth_check).
+
+-spec ldap_backups(gen_mod:opts() | global | binary()) -> [binary()].
+ldap_backups(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_backups, Opts);
+ldap_backups(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_backups).
+
+-spec ldap_base(gen_mod:opts() | global | binary()) -> binary().
+ldap_base(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_base, Opts);
+ldap_base(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_base).
+
+-spec ldap_deref_aliases(gen_mod:opts() | global | binary()) -> 'always' | 'finding' | 'never' | 'searching'.
+ldap_deref_aliases(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_deref_aliases, Opts);
+ldap_deref_aliases(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_deref_aliases).
+
+-spec ldap_encrypt(gen_mod:opts() | global | binary()) -> 'none' | 'starttls' | 'tls'.
+ldap_encrypt(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_encrypt, Opts);
+ldap_encrypt(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_encrypt).
+
+-spec ldap_filter(gen_mod:opts() | global | binary()) -> binary().
+ldap_filter(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_filter, Opts);
+ldap_filter(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_filter).
+
+-spec ldap_gfilter(gen_mod:opts() | global | binary()) -> binary().
+ldap_gfilter(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_gfilter, Opts);
+ldap_gfilter(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_gfilter).
+
+-spec ldap_groupattr(gen_mod:opts() | global | binary()) -> binary().
+ldap_groupattr(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_groupattr, Opts);
+ldap_groupattr(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_groupattr).
+
+-spec ldap_groupdesc(gen_mod:opts() | global | binary()) -> 'undefined' | binary().
+ldap_groupdesc(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_groupdesc, Opts);
+ldap_groupdesc(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_groupdesc).
+
+-spec ldap_memberattr(gen_mod:opts() | global | binary()) -> binary().
+ldap_memberattr(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_memberattr, Opts);
+ldap_memberattr(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_memberattr).
+
+-spec ldap_memberattr_format(gen_mod:opts() | global | binary()) -> binary().
+ldap_memberattr_format(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_memberattr_format, Opts);
+ldap_memberattr_format(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_memberattr_format).
+
+-spec ldap_memberattr_format_re(gen_mod:opts() | global | binary()) -> 'undefined' | re:mp().
+ldap_memberattr_format_re(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_memberattr_format_re, Opts);
+ldap_memberattr_format_re(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_memberattr_format_re).
+
+-spec ldap_password(gen_mod:opts() | global | binary()) -> binary().
+ldap_password(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_password, Opts);
+ldap_password(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_password).
+
+-spec ldap_port(gen_mod:opts() | global | binary()) -> 1..1114111.
+ldap_port(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_port, Opts);
+ldap_port(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_port).
+
+-spec ldap_rfilter(gen_mod:opts() | global | binary()) -> binary().
+ldap_rfilter(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_rfilter, Opts);
+ldap_rfilter(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_rfilter).
+
+-spec ldap_rootdn(gen_mod:opts() | global | binary()) -> binary().
+ldap_rootdn(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_rootdn, Opts);
+ldap_rootdn(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_rootdn).
+
+-spec ldap_servers(gen_mod:opts() | global | binary()) -> [binary()].
+ldap_servers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_servers, Opts);
+ldap_servers(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_servers).
+
+-spec ldap_tls_cacertfile(gen_mod:opts() | global | binary()) -> binary().
+ldap_tls_cacertfile(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_cacertfile, Opts);
+ldap_tls_cacertfile(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_tls_cacertfile).
+
+-spec ldap_tls_certfile(gen_mod:opts() | global | binary()) -> binary().
+ldap_tls_certfile(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_certfile, Opts);
+ldap_tls_certfile(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_tls_certfile).
+
+-spec ldap_tls_depth(gen_mod:opts() | global | binary()) -> non_neg_integer().
+ldap_tls_depth(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_depth, Opts);
+ldap_tls_depth(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_tls_depth).
+
+-spec ldap_tls_verify(gen_mod:opts() | global | binary()) -> 'false' | 'hard' | 'soft'.
+ldap_tls_verify(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_verify, Opts);
+ldap_tls_verify(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_tls_verify).
+
+-spec ldap_ufilter(gen_mod:opts() | global | binary()) -> binary().
+ldap_ufilter(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_ufilter, Opts);
+ldap_ufilter(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_ufilter).
+
+-spec ldap_uids(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+ldap_uids(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_uids, Opts);
+ldap_uids(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_uids).
+
+-spec ldap_userdesc(gen_mod:opts() | global | binary()) -> binary().
+ldap_userdesc(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_userdesc, Opts);
+ldap_userdesc(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_userdesc).
+
+-spec ldap_useruid(gen_mod:opts() | global | binary()) -> binary().
+ldap_useruid(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_useruid, Opts);
+ldap_useruid(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, ldap_useruid).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster_ldap, use_cache).
+
diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl
index a54b9687f..f7403d7b9 100644
--- a/src/mod_shared_roster_mnesia.erl
+++ b/src/mod_shared_roster_mnesia.erl
@@ -144,11 +144,11 @@ import(LServer, <<"sr_user">>, [SJID, Group, _TimeStamp]) ->
User = #sr_user{us = {U, S}, group_host = {Group, LServer}},
mnesia:dirty_write(User).
-need_transform(#sr_group{group_host = {G, H}})
+need_transform({sr_group, {G, H}, _})
when is_list(G) orelse is_list(H) ->
?INFO_MSG("Mnesia table 'sr_group' will be converted to binary", []),
true;
-need_transform(#sr_user{us = {U, S}, group_host = {G, H}})
+need_transform({sr_user, {U, S}, {G, H}})
when is_list(U) orelse is_list(S) orelse is_list(G) orelse is_list(H) ->
?INFO_MSG("Mnesia table 'sr_user' will be converted to binary", []),
true;
diff --git a/src/mod_shared_roster_opt.erl b/src/mod_shared_roster_opt.erl
new file mode 100644
index 000000000..d0d2aaac1
--- /dev/null
+++ b/src/mod_shared_roster_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_shared_roster_opt).
+
+-export([db_type/1]).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_shared_roster, db_type).
+
diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl
index 39ca9fb0d..a761f5e11 100644
--- a/src/mod_shared_roster_sql.erl
+++ b/src/mod_shared_roster_sql.erl
@@ -24,7 +24,6 @@
-module(mod_shared_roster_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_shared_roster).
diff --git a/src/mod_sip.erl b/src/mod_sip.erl
index 3cb2ac13e..9dc4686a9 100644
--- a/src/mod_sip.erl
+++ b/src/mod_sip.erl
@@ -61,7 +61,7 @@ start(_Host, _Opts) ->
esip:set_config_value(max_server_transactions, 10000),
esip:set_config_value(max_client_transactions, 10000),
esip:set_config_value(
- software, <<"ejabberd ", (ejabberd_config:get_version())/binary>>),
+ software, <<"ejabberd ", (ejabberd_option:version())/binary>>),
esip:set_config_value(module, ?MODULE),
Spec = {mod_sip_registrar, {mod_sip_registrar, start_link, []},
transient, 2000, worker, [mod_sip_registrar]},
@@ -325,45 +325,36 @@ is_my_host(LServer) ->
gen_mod:is_loaded(LServer, ?MODULE).
mod_opt_type(always_record_route) ->
- fun (true) -> true;
- (false) -> false
- end;
+ econf:bool();
mod_opt_type(flow_timeout_tcp) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(flow_timeout_udp) ->
- fun (I) when is_integer(I), I > 0 -> I end;
+ econf:pos_int();
mod_opt_type(record_route) ->
- fun (IOList) ->
- S = iolist_to_binary(IOList),
- #uri{} = esip:decode_uri(S)
- end;
+ econf:sip_uri();
mod_opt_type(routes) ->
- fun (L) ->
- lists:map(fun (IOList) ->
- S = iolist_to_binary(IOList),
- #uri{} = esip:decode_uri(S)
- end,
- L)
- end;
+ econf:list(econf:sip_uri());
mod_opt_type(via) ->
- fun (L) ->
- lists:map(fun (Opts) ->
- Type = proplists:get_value(type, Opts),
- Host = proplists:get_value(host, Opts),
- Port = proplists:get_value(port, Opts),
- true = (Type == tcp) or (Type == tls) or
- (Type == udp),
- true = is_binary(Host) and (Host /= <<"">>),
- true = is_integer(Port) and (Port > 0) and
- (Port < 65536)
- or (Port == undefined),
- {Type, {Host, Port}}
- end,
- L)
- end.
-
+ econf:list(
+ econf:and_then(
+ econf:options(
+ #{type => econf:enum([tcp, tls, udp]),
+ host => econf:domain(),
+ port => econf:port()},
+ [{required, [type, host]}]),
+ fun(Opts) ->
+ Type = proplists:get_value(type, Opts),
+ Host = proplists:get_value(host, Opts),
+ Port = proplists:get_value(port, Opts),
+ {Type, {Host, Port}}
+ end)).
+
+-spec mod_options(binary()) -> [{via, [{tcp | tls | udp, {binary(), 1..65535}}]} |
+ {atom(), term()}].
mod_options(Host) ->
- Route = <<"sip:", Host/binary, ";lr">>,
+ Route = #uri{scheme = <<"sip">>,
+ host = Host,
+ params = [{<<"lr">>, <<>>}]},
[{always_record_route, true},
{flow_timeout_tcp, 120},
{flow_timeout_udp, 29},
diff --git a/src/mod_sip_opt.erl b/src/mod_sip_opt.erl
new file mode 100644
index 000000000..e160d2e12
--- /dev/null
+++ b/src/mod_sip_opt.erl
@@ -0,0 +1,48 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_sip_opt).
+
+-export([always_record_route/1]).
+-export([flow_timeout_tcp/1]).
+-export([flow_timeout_udp/1]).
+-export([record_route/1]).
+-export([routes/1]).
+-export([via/1]).
+
+-spec always_record_route(gen_mod:opts() | global | binary()) -> boolean().
+always_record_route(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(always_record_route, Opts);
+always_record_route(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, always_record_route).
+
+-spec flow_timeout_tcp(gen_mod:opts() | global | binary()) -> pos_integer().
+flow_timeout_tcp(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(flow_timeout_tcp, Opts);
+flow_timeout_tcp(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, flow_timeout_tcp).
+
+-spec flow_timeout_udp(gen_mod:opts() | global | binary()) -> pos_integer().
+flow_timeout_udp(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(flow_timeout_udp, Opts);
+flow_timeout_udp(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, flow_timeout_udp).
+
+-spec record_route(gen_mod:opts() | global | binary()) -> esip:uri().
+record_route(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(record_route, Opts);
+record_route(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, record_route).
+
+-spec routes(gen_mod:opts() | global | binary()) -> [esip:uri()].
+routes(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(routes, Opts);
+routes(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, routes).
+
+-spec via(gen_mod:opts() | global | binary()) -> [{'tcp' | 'tls' | 'udp',{binary(),1..65535}}].
+via(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(via, Opts);
+via(Host) ->
+ gen_mod:get_module_opt(Host, mod_sip, via).
+
diff --git a/src/mod_sip_proxy.erl b/src/mod_sip_proxy.erl
index b2d9543eb..ba1b90abf 100644
--- a/src/mod_sip_proxy.erl
+++ b/src/mod_sip_proxy.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%%% File : mod_sip_proxy.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Purpose :
+%%% Purpose :
%%% Created : 21 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
@@ -273,12 +273,7 @@ add_certfile(LServer, Opts) ->
{ok, CertFile} ->
[{certfile, CertFile}|Opts];
error ->
- case ejabberd_config:get_option({domain_certfile, LServer}) of
- CertFile when is_binary(CertFile) ->
- [{certfile, CertFile}|Opts];
- _ ->
- Opts
- end
+ Opts
end.
add_via(#sip_socket{type = Transport}, LServer, #sip{hdrs = Hdrs} = Req) ->
@@ -320,7 +315,7 @@ is_request_within_dialog(#sip{hdrs = Hdrs}) ->
esip:has_param(<<"tag">>, Params).
need_record_route(LServer) ->
- gen_mod:get_module_opt(LServer, mod_sip, always_record_route).
+ mod_sip_opt:always_record_route(LServer).
make_sign(TS, Hdrs) ->
{_, #uri{user = FUser, host = FServer}, FParams} = esip:get_hdr('from', Hdrs),
@@ -331,7 +326,7 @@ make_sign(TS, Hdrs) ->
LTServer = safe_nameprep(TServer),
FromTag = esip:get_param(<<"tag">>, FParams),
CallID = esip:get_hdr('call-id', Hdrs),
- SharedKey = ejabberd_config:get_option(shared_key),
+ SharedKey = ejabberd_config:get_shared_key(),
str:sha([SharedKey, LFUser, LFServer, LTUser, LTServer,
FromTag, CallID, TS]).
@@ -347,13 +342,13 @@ is_signed_by_me(TS_Sign, Hdrs) ->
end.
get_configured_vias(LServer) ->
- gen_mod:get_module_opt(LServer, mod_sip, via).
+ mod_sip_opt:via(LServer).
get_configured_record_route(LServer) ->
- gen_mod:get_module_opt(LServer, mod_sip, record_route).
+ mod_sip_opt:record_route(LServer).
get_configured_routes(LServer) ->
- gen_mod:get_module_opt(LServer, mod_sip, routes).
+ mod_sip_opt:routes(LServer).
mark_transaction_as_complete(TrID, State) ->
NewTrIDs = lists:delete(TrID, State#state.tr_ids),
diff --git a/src/mod_sip_registrar.erl b/src/mod_sip_registrar.erl
index 4805e788f..dc004a93c 100644
--- a/src/mod_sip_registrar.erl
+++ b/src/mod_sip_registrar.erl
@@ -1,7 +1,7 @@
%%%-------------------------------------------------------------------
%%% File : mod_sip_registrar.erl
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% Purpose :
+%%% Purpose :
%%% Created : 23 Apr 2014 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
@@ -493,11 +493,9 @@ need_ob_hdrs(Contacts, _IsOutboundSupported = true) ->
get_flow_timeout(LServer, #sip_socket{type = Type}) ->
case Type of
udp ->
- gen_mod:get_module_opt(
- LServer, mod_sip, flow_timeout_udp);
+ mod_sip_opt:flow_timeout_udp(LServer);
_ ->
- gen_mod:get_module_opt(
- LServer, mod_sip, flow_timeout_tcp)
+ mod_sip_opt:flow_timeout_tcp(LServer)
end.
update_table() ->
@@ -569,13 +567,8 @@ process_ping(SIPSocket) ->
mnesia:dirty_delete_object(Session),
Timeout = get_flow_timeout(LServer, SIPSocket),
NewTRef = set_timer(Session, Timeout),
- case mnesia:dirty_write(
- Session#sip_session{flow_tref = NewTRef}) of
- ok ->
- pong;
- _Err ->
- pang
- end;
+ mnesia:dirty_write(Session#sip_session{flow_tref = NewTRef}),
+ pong;
(_, Acc) ->
Acc
end, ErrResponse, Sessions).
diff --git a/src/mod_stats.erl b/src/mod_stats.erl
index 772d51098..d3a705564 100644
--- a/src/mod_stats.erl
+++ b/src/mod_stats.erl
@@ -136,7 +136,7 @@ get_local_stat(_Server, [], Name)
ejabberd_auth:count_users(Host)
+ Total
end,
- 0, ejabberd_config:get_myhosts()),
+ 0, ejabberd_option:hosts()),
?STATVAL((integer_to_binary(NumUsers)),
<<"users">>);
get_local_stat(_Server, _, Name) ->
diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl
index 1a4308c58..58b2c4395 100644
--- a/src/mod_stream_mgmt.erl
+++ b/src/mod_stream_mgmt.erl
@@ -28,7 +28,7 @@
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
%% hooks
--export([c2s_stream_init/2, c2s_stream_started/2, c2s_stream_features/2,
+-export([c2s_stream_started/2, c2s_stream_features/2,
c2s_authenticated_packet/2, c2s_unauthenticated_packet/2,
c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2,
c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3,
@@ -55,7 +55,6 @@
%%%===================================================================
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),
ejabberd_hooks:add(c2s_post_auth_features, Host, ?MODULE,
@@ -74,12 +73,6 @@ start(Host, Opts) ->
ejabberd_hooks:add(c2s_terminated, Host, ?MODULE, c2s_terminated, 50).
stop(Host) ->
- case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
- true ->
- ok;
- false ->
- ejabberd_hooks:delete(c2s_init, ?MODULE, c2s_stream_init, 50)
- end,
ejabberd_hooks:delete(c2s_stream_started, Host, ?MODULE,
c2s_stream_started, 50),
ejabberd_hooks:delete(c2s_post_auth_features, Host, ?MODULE,
@@ -105,21 +98,6 @@ reload(_Host, NewOpts, _OldOpts) ->
depends(_Host, _Opts) ->
[].
-c2s_stream_init({ok, State}, Opts) ->
- MgmtOpts = lists:filter(
- fun({stream_management, _}) -> true;
- ({max_ack_queue, _}) -> true;
- ({resume_timeout, _}) -> true;
- ({max_resume_timeout, _}) -> true;
- ({ack_timeout, _}) -> true;
- ({resend_on_timeout, _}) -> true;
- ({queue_type, _}) -> true;
- (_) -> false
- end, Opts),
- {ok, State#{mgmt_options => MgmtOpts}};
-c2s_stream_init(Acc, _Opts) ->
- Acc.
-
c2s_stream_started(#{lserver := LServer} = State, _StreamStart) ->
State1 = maps:remove(mgmt_options, State),
ResumeTimeout = get_configured_resume_timeout(LServer),
@@ -749,7 +727,7 @@ init_cache(Opts) ->
ets_cache:new(?STREAM_MGMT_CACHE, cache_opts(Opts)).
cache_opts(Opts) ->
- [{max_size, gen_mod:get_opt(cache_size, Opts)},
+ [{max_size, mod_stream_mgmt_opt:cache_size(Opts)},
{life_time, infinity}].
-spec store_stanzas_in(ljid(), erlang:timestamp(), non_neg_integer()) -> boolean().
@@ -772,61 +750,49 @@ pop_stanzas_in(LJID, Time) ->
%%% Configuration processing
%%%===================================================================
get_max_ack_queue(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, max_ack_queue).
+ mod_stream_mgmt_opt:max_ack_queue(Host).
get_configured_resume_timeout(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, resume_timeout).
+ mod_stream_mgmt_opt:resume_timeout(Host).
get_max_resume_timeout(Host, ResumeTimeout) ->
- case gen_mod:get_module_opt(Host, ?MODULE, max_resume_timeout) of
+ case mod_stream_mgmt_opt:max_resume_timeout(Host) of
undefined -> ResumeTimeout;
Max when Max >= ResumeTimeout -> Max;
_ -> ResumeTimeout
end.
get_ack_timeout(Host) ->
- case gen_mod:get_module_opt(Host, ?MODULE, ack_timeout) of
- infinity -> infinity;
- T -> timer:seconds(T)
- end.
+ mod_stream_mgmt_opt:ack_timeout(Host).
get_resend_on_timeout(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, resend_on_timeout).
+ mod_stream_mgmt_opt:resend_on_timeout(Host).
get_queue_type(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, queue_type).
+ mod_stream_mgmt_opt:queue_type(Host).
mod_opt_type(max_ack_queue) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
+ econf:pos_int(infinity);
mod_opt_type(resume_timeout) ->
- fun(I) when is_integer(I), I >= 0 -> I end;
+ econf:non_neg_int();
mod_opt_type(max_resume_timeout) ->
- fun(I) when is_integer(I), I >= 0 -> I;
- (undefined) -> undefined
- end;
+ econf:non_neg_int();
mod_opt_type(ack_timeout) ->
- fun(I) when is_integer(I), I > 0 -> I;
- (infinity) -> infinity
- end;
+ econf:timeout(second, infinity);
mod_opt_type(resend_on_timeout) ->
- fun(B) when is_boolean(B) -> B;
- (if_offline) -> if_offline
- end;
+ econf:either(
+ if_offline,
+ econf:bool());
mod_opt_type(cache_size) ->
- fun(I) when is_integer(I), I>0 -> I;
- (unlimited) -> infinity;
- (infinity) -> infinity
- end;
+ econf:well_known(cache_size, ?MODULE);
mod_opt_type(queue_type) ->
- fun(ram) -> ram; (file) -> file end.
+ econf:well_known(queue_type, ?MODULE).
mod_options(Host) ->
[{max_ack_queue, 5000},
{resume_timeout, 300},
{max_resume_timeout, undefined},
- {ack_timeout, 60},
- {cache_size, ejabberd_config:cache_size(Host)},
+ {ack_timeout, timer:seconds(60)},
+ {cache_size, ejabberd_option:cache_size(Host)},
{resend_on_timeout, false},
- {queue_type, ejabberd_config:default_queue_type(Host)}].
+ {queue_type, ejabberd_option:queue_type(Host)}].
diff --git a/src/mod_stream_mgmt_opt.erl b/src/mod_stream_mgmt_opt.erl
new file mode 100644
index 000000000..102906fdc
--- /dev/null
+++ b/src/mod_stream_mgmt_opt.erl
@@ -0,0 +1,55 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_stream_mgmt_opt).
+
+-export([ack_timeout/1]).
+-export([cache_size/1]).
+-export([max_ack_queue/1]).
+-export([max_resume_timeout/1]).
+-export([queue_type/1]).
+-export([resend_on_timeout/1]).
+-export([resume_timeout/1]).
+
+-spec ack_timeout(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+ack_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ack_timeout, Opts);
+ack_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, ack_timeout).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, cache_size).
+
+-spec max_ack_queue(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+max_ack_queue(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_ack_queue, Opts);
+max_ack_queue(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, max_ack_queue).
+
+-spec max_resume_timeout(gen_mod:opts() | global | binary()) -> 'undefined' | non_neg_integer().
+max_resume_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(max_resume_timeout, Opts);
+max_resume_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, max_resume_timeout).
+
+-spec queue_type(gen_mod:opts() | global | binary()) -> 'file' | 'ram'.
+queue_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(queue_type, Opts);
+queue_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, queue_type).
+
+-spec resend_on_timeout(gen_mod:opts() | global | binary()) -> 'false' | 'if_offline' | 'true'.
+resend_on_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(resend_on_timeout, Opts);
+resend_on_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, resend_on_timeout).
+
+-spec resume_timeout(gen_mod:opts() | global | binary()) -> non_neg_integer().
+resume_timeout(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(resume_timeout, Opts);
+resume_timeout(Host) ->
+ gen_mod:get_module_opt(Host, mod_stream_mgmt, resume_timeout).
+
diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl
index 6b6f5f7d5..dc8e83879 100644
--- a/src/mod_vcard.erl
+++ b/src/mod_vcard.erl
@@ -82,7 +82,7 @@ stop(Host) ->
%%====================================================================
init([Host, Opts]) ->
process_flag(trap_exit, true),
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+ Mod = gen_mod:db_mod(Opts, ?MODULE),
Mod:init(Host, Opts),
init_cache(Mod, Host, Opts),
ejabberd_hooks:add(remove_user, Host, ?MODULE,
@@ -94,8 +94,8 @@ init([Host, Opts]) ->
ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
get_sm_features, 50),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE, vcard_iq_set, 50),
- MyHosts = gen_mod:get_opt_hosts(Host, Opts),
- Search = gen_mod:get_opt(search, Opts),
+ MyHosts = gen_mod:get_opt_hosts(Opts),
+ Search = mod_vcard_opt:search(Opts),
if Search ->
lists:foreach(
fun(MyHost) ->
@@ -119,7 +119,7 @@ init([Host, Opts]) ->
false ->
?WARNING_MSG("vcard search functionality is "
"not implemented for ~s backend",
- [gen_mod:get_opt(db_type, Opts)]);
+ [mod_vcard_opt:db_type(Opts)]);
true ->
ejabberd_router:register_route(MyHost, Host)
end
@@ -205,7 +205,7 @@ process_local_iq(#iq{type = get, lang = Lang} = IQ) ->
-spec process_sm_iq(iq()) -> iq().
process_sm_iq(#iq{type = set, lang = Lang, from = From} = IQ) ->
#jid{lserver = LServer} = From,
- case lists:member(LServer, ejabberd_config:get_myhosts()) of
+ case lists:member(LServer, ejabberd_option:hosts()) of
true ->
case ejabberd_hooks:run_fold(vcard_iq_set, LServer, IQ, []) of
drop -> ignore;
@@ -284,7 +284,7 @@ disco_features(Acc, _From, _To, _Node, _Lang) ->
binary(), binary()) -> [identity()].
disco_identity(Acc, _From, To, <<"">>, Lang) ->
Host = ejabberd_router:host_of_route(To#jid.lserver),
- Name = gen_mod:get_module_opt(Host, ?MODULE, name),
+ Name = mod_vcard_opt:name(Host),
[#identity{category = <<"directory">>,
type = <<"user">>,
name = translate:translate(Lang, Name)}|Acc];
@@ -470,8 +470,8 @@ item_to_field(Items) ->
search(LServer, XFields) ->
Data = [{Var, Vals} || #xdata_field{var = Var, values = Vals} <- XFields],
Mod = gen_mod:db_mod(LServer, ?MODULE),
- AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all),
- MaxMatch = gen_mod:get_module_opt(LServer, ?MODULE, matches),
+ AllowReturnAll = mod_vcard_opt:allow_return_all(LServer),
+ MaxMatch = mod_vcard_opt:matches(LServer),
Mod:search(LServer, Data, AllowReturnAll, MaxMatch).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -495,9 +495,9 @@ init_cache(Mod, Host, Opts) ->
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
cache_opts(_Host, 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
+ MaxSize = mod_vcard_opt:cache_size(Opts),
+ CacheMissed = mod_vcard_opt:cache_missed(Opts),
+ LifeTime = case mod_vcard_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -507,7 +507,7 @@ cache_opts(_Host, Opts) ->
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)
+ false -> mod_vcard_opt:use_cache(Host)
end.
-spec cache_nodes(module(), binary()) -> [node()].
@@ -536,33 +536,37 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(allow_return_all) ->
- 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(name) -> fun iolist_to_binary/1;
-mod_opt_type(host) -> fun ejabberd_config:v_host/1;
-mod_opt_type(hosts) -> fun ejabberd_config:v_hosts/1;
+ econf:bool();
+mod_opt_type(name) ->
+ econf:binary();
mod_opt_type(matches) ->
- fun (infinity) -> infinity;
- (I) when is_integer(I), I > 0 -> I
- end;
+ econf:pos_int(infinity);
mod_opt_type(search) ->
- fun (B) when is_boolean(B) -> B 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.
+ econf:bool();
+mod_opt_type(host) ->
+ econf:well_known(host, ?MODULE);
+mod_opt_type(hosts) ->
+ econf:well_known(hosts, ?MODULE);
+mod_opt_type(db_type) ->
+ econf:well_known(db_type, ?MODULE);
+mod_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
[{allow_return_all, false},
- {host, <<"vjud.@HOST@">>},
+ {host, <<"vjud.", Host/binary>>},
{hosts, []},
{matches, 30},
{search, false},
{name, ?T("vCard User Search")},
{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)}].
+ {use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index 2d00d4465..cd2d9a1cf 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -57,8 +57,8 @@
dn = <<"">> :: binary(),
base = <<"">> :: binary(),
password = <<"">> :: binary(),
- uids = [] :: [{binary()} | {binary(), binary()}],
- vcard_map = [] :: [{binary(), binary(), [binary()]}],
+ uids = [] :: [{binary(), binary()}],
+ vcard_map = [] :: [{binary(), [{binary(), [binary()]}]}],
vcard_map_attrs = [] :: [binary()],
user_filter = <<"">> :: binary(),
search_filter :: eldap:filter(),
@@ -234,12 +234,10 @@ find_ldap_user(User, State) ->
end.
ldap_attributes_to_vcard(Attributes, VCardMap, UD) ->
- Attrs = lists:map(fun ({VCardName, _, _}) ->
- {stringprep:tolower(VCardName),
- map_vcard_attr(VCardName, Attributes, VCardMap,
- UD)}
- end,
- VCardMap),
+ Attrs = lists:map(
+ fun({VCardName, _}) ->
+ {VCardName, map_vcard_attr(VCardName, Attributes, VCardMap, UD)}
+ end, VCardMap),
lists:foldl(fun ldap_attribute_to_vcard/2, #vcard_temp{}, Attrs).
-spec ldap_attribute_to_vcard({binary(), binary()}, vcard_temp()) -> vcard_temp().
@@ -258,7 +256,7 @@ ldap_attribute_to_vcard({Attr, Value}, V) ->
[] -> #vcard_adr{};
As -> hd(As)
end,
- case Attr of
+ case str:to_lower(Attr) of
<<"fn">> -> V#vcard_temp{fn = Value};
<<"nickname">> -> V#vcard_temp{nickname = Value};
<<"title">> -> V#vcard_temp{title = Value};
@@ -283,13 +281,12 @@ ldap_attribute_to_vcard({Attr, Value}, V) ->
end.
map_vcard_attr(VCardName, Attributes, Pattern, UD) ->
- Res = lists:filter(fun ({Name, _, _}) ->
- eldap_utils:case_insensitive_match(Name,
- VCardName)
- end,
- Pattern),
+ Res = lists:filter(
+ fun({Name, _}) ->
+ eldap_utils:case_insensitive_match(Name, VCardName)
+ end, Pattern),
case Res of
- [{_, Str, Attrs}] ->
+ [{_, [{Str, Attrs}|_]}] ->
process_pattern(Str, UD,
[eldap_utils:get_ldap_attr(X, Attributes)
|| X <- Attrs]);
@@ -351,15 +348,15 @@ default_search_reported() ->
{?T("Organization Unit"), <<"ORGUNIT">>}].
parse_options(Host, Opts) ->
- MyHosts = gen_mod:get_opt_hosts(Host, Opts),
- Search = gen_mod:get_opt(search, Opts),
- Matches = gen_mod:get_opt(matches, Opts),
+ MyHosts = gen_mod:get_opt_hosts(Opts),
+ Search = mod_vcard_opt:search(Opts),
+ Matches = mod_vcard_opt:matches(Opts),
Eldap_ID = misc:atom_to_binary(gen_mod:get_module_proc(Host, ?PROCNAME)),
- Cfg = eldap_utils:get_config(Host, Opts),
- UIDsTemp = gen_mod:get_opt(ldap_uids, Opts),
+ Cfg = ?eldap_config(mod_vcard_ldap_opt, Opts),
+ UIDsTemp = mod_vcard_ldap_opt:ldap_uids(Opts),
UIDs = eldap_utils:uids_domain_subst(Host, UIDsTemp),
SubFilter = eldap_utils:generate_subfilter(UIDs),
- UserFilter = case gen_mod:get_opt(ldap_filter, Opts) of
+ UserFilter = case mod_vcard_ldap_opt:ldap_filter(Opts) of
<<"">> ->
SubFilter;
F ->
@@ -368,28 +365,27 @@ parse_options(Host, Opts) ->
{ok, SearchFilter} =
eldap_filter:parse(eldap_filter:do_sub(UserFilter,
[{<<"%u">>, <<"*">>}])),
- VCardMap = gen_mod:get_opt(ldap_vcard_map, Opts),
- SearchFields = gen_mod:get_opt(ldap_search_fields, Opts),
- SearchReported = gen_mod:get_opt(ldap_search_reported, Opts),
+ VCardMap = mod_vcard_ldap_opt:ldap_vcard_map(Opts),
+ SearchFields = mod_vcard_ldap_opt:ldap_search_fields(Opts),
+ SearchReported = mod_vcard_ldap_opt:ldap_search_reported(Opts),
UIDAttrs = [UAttr || {UAttr, _} <- UIDs],
- VCardMapAttrs = lists:usort(lists:append([A
- || {_, _, A} <- VCardMap])
- ++ UIDAttrs),
- SearchReportedAttrs = lists:usort(lists:flatmap(fun ({_,
- N}) ->
- case
- lists:keysearch(N,
- 1,
- VCardMap)
- of
- {value,
- {_, _, L}} ->
- L;
- _ -> []
- end
- end,
- SearchReported)
- ++ UIDAttrs),
+ VCardMapAttrs = lists:usort(
+ lists:flatten(
+ lists:map(
+ fun({_, Map}) ->
+ [Attrs || {_, Attrs} <- Map]
+ end, VCardMap) ++ UIDAttrs)),
+ SearchReportedAttrs = lists:usort(
+ lists:flatten(
+ lists:map(
+ fun ({_, N}) ->
+ case lists:keyfind(N, 1, VCardMap) of
+ {_, Map} ->
+ [Attrs || {_, Attrs} <- Map];
+ false ->
+ []
+ end
+ end, SearchReported) ++ UIDAttrs)),
#state{serverhost = Host, myhosts = MyHosts,
eldap_id = Eldap_ID, search = Search,
servers = Cfg#eldap_config.servers,
@@ -409,31 +405,71 @@ parse_options(Host, Opts) ->
matches = Matches}.
mod_opt_type(ldap_search_fields) ->
- fun (Ls) ->
- [{iolist_to_binary(S), iolist_to_binary(P)}
- || {S, P} <- Ls]
- end;
+ econf:map(
+ econf:binary(),
+ econf:binary());
mod_opt_type(ldap_search_reported) ->
- fun (Ls) ->
- [{iolist_to_binary(S), iolist_to_binary(P)}
- || {S, P} <- Ls]
- end;
+ econf:map(
+ econf:binary(),
+ econf:binary());
mod_opt_type(ldap_vcard_map) ->
- fun (Ls) ->
- lists:map(fun ({S, [{P, L}]}) ->
- {iolist_to_binary(S), iolist_to_binary(P),
- [iolist_to_binary(E) || E <- L]}
- end,
- Ls)
- end;
-mod_opt_type(Opt) ->
- eldap_utils:opt_type(Opt).
-
+ econf:map(
+ econf:binary(),
+ econf:map(
+ econf:binary(),
+ econf:list(
+ econf:binary())));
+mod_opt_type(ldap_backups) ->
+ econf:list(econf:domain(), [unique]);
+mod_opt_type(ldap_base) ->
+ econf:binary();
+mod_opt_type(ldap_deref_aliases) ->
+ econf:enum([never, searching, finding, always]);
+mod_opt_type(ldap_encrypt) ->
+ econf:enum([tls, starttls, none]);
+mod_opt_type(ldap_filter) ->
+ econf:ldap_filter();
+mod_opt_type(ldap_password) ->
+ econf:binary();
+mod_opt_type(ldap_port) ->
+ econf:port();
+mod_opt_type(ldap_rootdn) ->
+ econf:binary();
+mod_opt_type(ldap_servers) ->
+ econf:list(econf:domain(), [unique]);
+mod_opt_type(ldap_tls_cacertfile) ->
+ econf:pem();
+mod_opt_type(ldap_tls_certfile) ->
+ econf:pem();
+mod_opt_type(ldap_tls_depth) ->
+ econf:non_neg_int();
+mod_opt_type(ldap_tls_verify) ->
+ econf:enum([hard, soft, false]);
+mod_opt_type(ldap_uids) ->
+ econf:either(
+ econf:list(
+ econf:and_then(
+ econf:binary(),
+ fun(U) -> {U, <<"%u">>} end)),
+ econf:map(econf:binary(), econf:binary(), [unique])).
+
+-spec mod_options(binary()) -> [{ldap_uids, [{binary(), binary()}]} |
+ {atom(), any()}].
mod_options(Host) ->
[{ldap_search_fields, default_search_fields()},
{ldap_search_reported, default_search_reported()},
- {ldap_vcard_map, default_vcard_map()}
- | lists:map(
- fun({Opt, Default}) ->
- {Opt, ejabberd_config:get_option({Opt, Host}, Default)}
- end, eldap_utils:options(Host))].
+ {ldap_vcard_map, default_vcard_map()},
+ {ldap_backups, ejabberd_option:ldap_backups(Host)},
+ {ldap_base, ejabberd_option:ldap_base(Host)},
+ {ldap_uids, ejabberd_option:ldap_uids(Host)},
+ {ldap_deref_aliases, ejabberd_option:ldap_deref_aliases(Host)},
+ {ldap_encrypt, ejabberd_option:ldap_encrypt(Host)},
+ {ldap_password, ejabberd_option:ldap_password(Host)},
+ {ldap_port, ejabberd_option:ldap_port(Host)},
+ {ldap_rootdn, ejabberd_option:ldap_rootdn(Host)},
+ {ldap_servers, ejabberd_option:ldap_servers(Host)},
+ {ldap_filter, ejabberd_option:ldap_filter(Host)},
+ {ldap_tls_certfile, ejabberd_option:ldap_tls_certfile(Host)},
+ {ldap_tls_cacertfile, ejabberd_option:ldap_tls_cacertfile(Host)},
+ {ldap_tls_depth, ejabberd_option:ldap_tls_depth(Host)},
+ {ldap_tls_verify, ejabberd_option:ldap_tls_verify(Host)}].
diff --git a/src/mod_vcard_ldap_opt.erl b/src/mod_vcard_ldap_opt.erl
new file mode 100644
index 000000000..2f0109eb3
--- /dev/null
+++ b/src/mod_vcard_ldap_opt.erl
@@ -0,0 +1,125 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_vcard_ldap_opt).
+
+-export([ldap_backups/1]).
+-export([ldap_base/1]).
+-export([ldap_deref_aliases/1]).
+-export([ldap_encrypt/1]).
+-export([ldap_filter/1]).
+-export([ldap_password/1]).
+-export([ldap_port/1]).
+-export([ldap_rootdn/1]).
+-export([ldap_search_fields/1]).
+-export([ldap_search_reported/1]).
+-export([ldap_servers/1]).
+-export([ldap_tls_cacertfile/1]).
+-export([ldap_tls_certfile/1]).
+-export([ldap_tls_depth/1]).
+-export([ldap_tls_verify/1]).
+-export([ldap_uids/1]).
+-export([ldap_vcard_map/1]).
+
+-spec ldap_backups(gen_mod:opts() | global | binary()) -> [binary()].
+ldap_backups(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_backups, Opts);
+ldap_backups(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_backups).
+
+-spec ldap_base(gen_mod:opts() | global | binary()) -> binary().
+ldap_base(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_base, Opts);
+ldap_base(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_base).
+
+-spec ldap_deref_aliases(gen_mod:opts() | global | binary()) -> 'always' | 'finding' | 'never' | 'searching'.
+ldap_deref_aliases(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_deref_aliases, Opts);
+ldap_deref_aliases(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_deref_aliases).
+
+-spec ldap_encrypt(gen_mod:opts() | global | binary()) -> 'none' | 'starttls' | 'tls'.
+ldap_encrypt(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_encrypt, Opts);
+ldap_encrypt(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_encrypt).
+
+-spec ldap_filter(gen_mod:opts() | global | binary()) -> binary().
+ldap_filter(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_filter, Opts);
+ldap_filter(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_filter).
+
+-spec ldap_password(gen_mod:opts() | global | binary()) -> binary().
+ldap_password(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_password, Opts);
+ldap_password(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_password).
+
+-spec ldap_port(gen_mod:opts() | global | binary()) -> 1..1114111.
+ldap_port(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_port, Opts);
+ldap_port(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_port).
+
+-spec ldap_rootdn(gen_mod:opts() | global | binary()) -> binary().
+ldap_rootdn(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_rootdn, Opts);
+ldap_rootdn(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_rootdn).
+
+-spec ldap_search_fields(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+ldap_search_fields(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_search_fields, Opts);
+ldap_search_fields(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_search_fields).
+
+-spec ldap_search_reported(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+ldap_search_reported(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_search_reported, Opts);
+ldap_search_reported(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_search_reported).
+
+-spec ldap_servers(gen_mod:opts() | global | binary()) -> [binary()].
+ldap_servers(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_servers, Opts);
+ldap_servers(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_servers).
+
+-spec ldap_tls_cacertfile(gen_mod:opts() | global | binary()) -> binary().
+ldap_tls_cacertfile(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_cacertfile, Opts);
+ldap_tls_cacertfile(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_tls_cacertfile).
+
+-spec ldap_tls_certfile(gen_mod:opts() | global | binary()) -> binary().
+ldap_tls_certfile(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_certfile, Opts);
+ldap_tls_certfile(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_tls_certfile).
+
+-spec ldap_tls_depth(gen_mod:opts() | global | binary()) -> non_neg_integer().
+ldap_tls_depth(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_depth, Opts);
+ldap_tls_depth(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_tls_depth).
+
+-spec ldap_tls_verify(gen_mod:opts() | global | binary()) -> 'false' | 'hard' | 'soft'.
+ldap_tls_verify(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_tls_verify, Opts);
+ldap_tls_verify(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_tls_verify).
+
+-spec ldap_uids(gen_mod:opts() | global | binary()) -> [{binary(),binary()}].
+ldap_uids(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_uids, Opts);
+ldap_uids(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_uids).
+
+-spec ldap_vcard_map(gen_mod:opts() | global | binary()) -> [{binary(),[{binary(),[binary()]}]}].
+ldap_vcard_map(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(ldap_vcard_map, Opts);
+ldap_vcard_map(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_ldap, ldap_vcard_map).
+
diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl
index 31e9f6d43..d4394b677 100644
--- a/src/mod_vcard_mnesia.erl
+++ b/src/mod_vcard_mnesia.erl
@@ -155,12 +155,17 @@ import(LServer, <<"vcard_search">>,
orgname = OrgName, lorgname = LOrgName,
orgunit = OrgUnit, lorgunit = LOrgUnit}).
-need_transform(#vcard{us = {U, S}}) when is_list(U) orelse is_list(S) ->
+need_transform({vcard, {U, S}, _}) when is_list(U) orelse is_list(S) ->
?INFO_MSG("Mnesia table 'vcard' will be converted to binary", []),
true;
-need_transform(#vcard_search{us = {U, S}}) when is_list(U) orelse is_list(S) ->
- ?INFO_MSG("Mnesia table 'vcard_search' will be converted to binary", []),
- true;
+need_transform(R) when element(1, R) == vcard_search ->
+ case element(2, R) of
+ {U, S} when is_list(U) orelse is_list(S) ->
+ ?INFO_MSG("Mnesia table 'vcard_search' will be converted to binary", []),
+ true;
+ _ ->
+ false
+ end;
need_transform(_) ->
false.
@@ -192,8 +197,7 @@ filter_fields([{SVar, [Val]} | Ds], Match, LServer)
LVal = mod_vcard:string2lower(Val),
NewMatch = case SVar of
<<"user">> ->
- case gen_mod:get_module_opt(LServer, mod_vcard,
- search_all_hosts) of
+ case mod_vcard_mnesia_opt:search_all_hosts(LServer) of
true -> Match#vcard_search{luser = make_val(LVal)};
false ->
Host = find_my_host(LServer),
@@ -234,7 +238,7 @@ make_val(Val) ->
find_my_host(LServer) ->
Parts = str:tokens(LServer, <<".">>),
- find_my_host(Parts, ejabberd_config:get_myhosts()).
+ find_my_host(Parts, ejabberd_option:hosts()).
find_my_host([], _Hosts) -> ejabberd_config:get_myname();
find_my_host([_ | Tail] = Parts, Hosts) ->
@@ -266,7 +270,7 @@ record_to_item(R) ->
{<<"orgunit">>, (R#vcard_search.orgunit)}].
mod_opt_type(search_all_hosts) ->
- fun (B) when is_boolean(B) -> B end.
+ econf:bool().
mod_options(_) ->
[{search_all_hosts, true}].
diff --git a/src/mod_vcard_mnesia_opt.erl b/src/mod_vcard_mnesia_opt.erl
new file mode 100644
index 000000000..f326a84d2
--- /dev/null
+++ b/src/mod_vcard_mnesia_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_vcard_mnesia_opt).
+
+-export([search_all_hosts/1]).
+
+-spec search_all_hosts(gen_mod:opts() | global | binary()) -> boolean().
+search_all_hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(search_all_hosts, Opts);
+search_all_hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_mnesia, search_all_hosts).
+
diff --git a/src/mod_vcard_opt.erl b/src/mod_vcard_opt.erl
new file mode 100644
index 000000000..79be37a37
--- /dev/null
+++ b/src/mod_vcard_opt.erl
@@ -0,0 +1,83 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_vcard_opt).
+
+-export([allow_return_all/1]).
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([db_type/1]).
+-export([host/1]).
+-export([hosts/1]).
+-export([matches/1]).
+-export([name/1]).
+-export([search/1]).
+-export([use_cache/1]).
+
+-spec allow_return_all(gen_mod:opts() | global | binary()) -> boolean().
+allow_return_all(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(allow_return_all, Opts);
+allow_return_all(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, allow_return_all).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, cache_size).
+
+-spec db_type(gen_mod:opts() | global | binary()) -> atom().
+db_type(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(db_type, Opts);
+db_type(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, db_type).
+
+-spec host(gen_mod:opts() | global | binary()) -> binary().
+host(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(host, Opts);
+host(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, host).
+
+-spec hosts(gen_mod:opts() | global | binary()) -> [binary()].
+hosts(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(hosts, Opts);
+hosts(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, hosts).
+
+-spec matches(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+matches(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(matches, Opts);
+matches(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, matches).
+
+-spec name(gen_mod:opts() | global | binary()) -> binary().
+name(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(name, Opts);
+name(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, name).
+
+-spec search(gen_mod:opts() | global | binary()) -> boolean().
+search(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(search, Opts);
+search(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, search).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard, use_cache).
+
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index 93ef2e948..6b604161f 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -24,7 +24,6 @@
-module(mod_vcard_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-behaviour(mod_vcard).
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index a674598b8..1cdfcff32 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -158,9 +158,9 @@ init_cache(Host, Opts) ->
-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
+ MaxSize = mod_vcard_xupdate_opt:cache_size(Opts),
+ CacheMissed = mod_vcard_xupdate_opt:cache_missed(Opts),
+ LifeTime = case mod_vcard_xupdate_opt:cache_life_time(Opts) of
infinity -> infinity;
I -> timer:seconds(I)
end,
@@ -168,7 +168,7 @@ cache_opts(Opts) ->
-spec use_cache(binary()) -> boolean().
use_cache(Host) ->
- gen_mod:get_module_opt(Host, ?MODULE, use_cache).
+ mod_vcard_xupdate_opt:use_cache(Host).
-spec compute_hash(xmlel()) -> binary() | external.
compute_hash(VCard) ->
@@ -191,15 +191,17 @@ compute_hash(VCard) ->
%%====================================================================
%% Options
%%====================================================================
-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_opt_type(use_cache) ->
+ econf:well_known(use_cache, ?MODULE);
+mod_opt_type(cache_size) ->
+ econf:well_known(cache_size, ?MODULE);
+mod_opt_type(cache_missed) ->
+ econf:well_known(cache_missed, ?MODULE);
+mod_opt_type(cache_life_time) ->
+ econf:well_known(cache_life_time, ?MODULE).
mod_options(Host) ->
- [{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)}].
+ [{use_cache, ejabberd_option:use_cache(Host)},
+ {cache_size, ejabberd_option:cache_size(Host)},
+ {cache_missed, ejabberd_option:cache_missed(Host)},
+ {cache_life_time, ejabberd_option:cache_life_time(Host)}].
diff --git a/src/mod_vcard_xupdate_opt.erl b/src/mod_vcard_xupdate_opt.erl
new file mode 100644
index 000000000..a51e6884f
--- /dev/null
+++ b/src/mod_vcard_xupdate_opt.erl
@@ -0,0 +1,34 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_vcard_xupdate_opt).
+
+-export([cache_life_time/1]).
+-export([cache_missed/1]).
+-export([cache_size/1]).
+-export([use_cache/1]).
+
+-spec cache_life_time(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_life_time(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_life_time, Opts);
+cache_life_time(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_xupdate, cache_life_time).
+
+-spec cache_missed(gen_mod:opts() | global | binary()) -> boolean().
+cache_missed(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_missed, Opts);
+cache_missed(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_xupdate, cache_missed).
+
+-spec cache_size(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer().
+cache_size(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(cache_size, Opts);
+cache_size(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_xupdate, cache_size).
+
+-spec use_cache(gen_mod:opts() | global | binary()) -> boolean().
+use_cache(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(use_cache, Opts);
+use_cache(Host) ->
+ gen_mod:get_module_opt(Host, mod_vcard_xupdate, use_cache).
+
diff --git a/src/mod_version.erl b/src/mod_version.erl
index 7c1f28aea..41e97e496 100644
--- a/src/mod_version.erl
+++ b/src/mod_version.erl
@@ -54,12 +54,12 @@ process_local_iq(#iq{type = set, lang = Lang} = IQ) ->
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
process_local_iq(#iq{type = get, to = To} = IQ) ->
Host = To#jid.lserver,
- OS = case gen_mod:get_module_opt(Host, ?MODULE, show_os) of
+ OS = case mod_version_opt:show_os(Host) of
true -> get_os();
false -> undefined
end,
xmpp:make_iq_result(IQ, #version{name = <<"ejabberd">>,
- ver = ejabberd_config:get_version(),
+ ver = ejabberd_option:version(),
os = OS}).
get_os() ->
@@ -77,7 +77,7 @@ depends(_Host, _Opts) ->
[].
mod_opt_type(show_os) ->
- fun (B) when is_boolean(B) -> B end.
+ econf:bool().
mod_options(_Host) ->
[{show_os, true}].
diff --git a/src/mod_version_opt.erl b/src/mod_version_opt.erl
new file mode 100644
index 000000000..78d6231fb
--- /dev/null
+++ b/src/mod_version_opt.erl
@@ -0,0 +1,13 @@
+%% Generated automatically
+%% DO NOT EDIT: run `make options` instead
+
+-module(mod_version_opt).
+
+-export([show_os/1]).
+
+-spec show_os(gen_mod:opts() | global | binary()) -> boolean().
+show_os(Opts) when is_map(Opts) ->
+ gen_mod:get_opt(show_os, Opts);
+show_os(Host) ->
+ gen_mod:get_module_opt(Host, mod_version, show_os).
+
diff --git a/src/node_dag.erl b/src/node_dag.erl
index d1d8ccd8e..03633ef1e 100644
--- a/src/node_dag.erl
+++ b/src/node_dag.erl
@@ -80,7 +80,7 @@ publish_item(Nidx, Publisher, Model, MaxItems, ItemId, Payload, PubOpts) ->
collection ->
Txt = <<"Publishing items to collection node is not allowed">>,
{error, mod_pubsub:extended_error(
- xmpp:err_not_allowed(Txt, ejabberd_config:get_mylang()),
+ xmpp:err_not_allowed(Txt, ejabberd_option:language()),
mod_pubsub:err_unsupported('publish'))};
_ ->
node_hometree:publish_item(Nidx, Publisher, Model,
diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl
index adb7d59c6..5f494a9c0 100644
--- a/src/node_flat_sql.erl
+++ b/src/node_flat_sql.erl
@@ -33,7 +33,6 @@
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
--compile([{parse_transform, ejabberd_sql_pt}]).
-include("pubsub.hrl").
-include("xmpp.hrl").
@@ -767,7 +766,7 @@ get_item(Nidx, ItemId) ->
{selected, []} ->
{error, xmpp:err_item_not_found()};
{'EXIT', _} ->
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())}
end.
get_item(Nidx, ItemId, JID, AccessModel, PresenceSubscription, RosterGroup, _SubId) ->
diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl
index 4e6dd4872..ac3ab2196 100644
--- a/src/node_pep_sql.erl
+++ b/src/node_pep_sql.erl
@@ -30,7 +30,6 @@
-behaviour(gen_pubsub_node).
-author('christophe.romain@process-one.net').
--compile([{parse_transform, ejabberd_sql_pt}]).
-include("pubsub.hrl").
-include("ejabberd_sql_pt.hrl").
diff --git a/src/nodetree_dag.erl b/src/nodetree_dag.erl
index 1185ed817..7e31d7106 100644
--- a/src/nodetree_dag.erl
+++ b/src/nodetree_dag.erl
@@ -69,13 +69,13 @@ create_node(Key, Node, Type, Owner, Options, Parents) ->
Other -> Other
end;
_ ->
- {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_option:language())}
end.
delete_node(Key, Node) ->
case find_node(Key, Node) of
false ->
- {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())};
Record ->
lists:foreach(fun (#pubsub_node{options = Opts} = Child) ->
NewOpts = remove_config_parent(Node, Opts),
@@ -99,7 +99,7 @@ get_node(Host, Node, _From) ->
get_node(Host, Node) ->
case find_node(Host, Node) of
- false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())};
+ false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())};
Record -> Record
end.
@@ -115,7 +115,7 @@ get_nodes(Key) ->
get_parentnodes(Host, Node, _From) ->
case find_node(Host, Node) of
false ->
- {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())};
#pubsub_node{parents = Parents} ->
Q = qlc:q([N
|| #pubsub_node{nodeid = {NHost, NNode}} = N
@@ -139,7 +139,7 @@ get_subnodes(Host, <<>>) ->
get_subnodes_helper(Host, <<>>);
get_subnodes(Host, Node) ->
case find_node(Host, Node) of
- false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())};
+ false -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())};
_ -> get_subnodes_helper(Host, Node)
end.
@@ -215,7 +215,7 @@ remove_config_parent(Node, [H | T], Acc) ->
-spec validate_parentage(Key :: mod_pubsub:hostPubsub(), Owners :: [ljid(),...],
Parent_Nodes :: [mod_pubsub:nodeId()]) ->
- true | {error, xmlel()}.
+ true | {error, stanza_error()}.
validate_parentage(_Key, _Owners, []) ->
true;
@@ -226,7 +226,7 @@ validate_parentage(Key, Owners, [<<>> | T]) ->
validate_parentage(Key, Owners, [ParentID | T]) ->
case find_node(Key, ParentID) of
false ->
- {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())};
#pubsub_node{owners = POwners, options = POptions} ->
NodeType = find_opt(node_type, ?DEFAULT_NODETYPE, POptions),
MutualOwners = [O || O <- Owners, PO <- POwners, O == PO],
diff --git a/src/nodetree_tree.erl b/src/nodetree_tree.erl
index 084fa322a..3398f0900 100644
--- a/src/nodetree_tree.erl
+++ b/src/nodetree_tree.erl
@@ -71,13 +71,13 @@ get_node(Host, Node, _From) ->
get_node(Host, Node) ->
case mnesia:read({pubsub_node, {Host, Node}}) of
[Record] when is_record(Record, pubsub_node) -> Record;
- _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())}
+ _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())}
end.
get_node(Nidx) ->
case mnesia:index_read(pubsub_node, Nidx, #pubsub_node.id) of
[Record] when is_record(Record, pubsub_node) -> Record;
- _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())}
+ _ -> {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())}
end.
get_nodes(Host, _From) ->
@@ -189,7 +189,7 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
{error, xmpp:err_forbidden()}
end;
_ ->
- {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_option:language())}
end.
delete_node(Host, Node) ->
diff --git a/src/nodetree_tree_sql.erl b/src/nodetree_tree_sql.erl
index 311bbbf07..0a06645c3 100644
--- a/src/nodetree_tree_sql.erl
+++ b/src/nodetree_tree_sql.erl
@@ -37,7 +37,6 @@
-behaviour(gen_pubsub_nodetree).
-author('christophe.romain@process-one.net').
--compile([{parse_transform, ejabberd_sql_pt}]).
-include("pubsub.hrl").
-include("xmpp.hrl").
@@ -94,7 +93,7 @@ set_node(Record) when is_record(Record, pubsub_node) ->
case Nidx of
none ->
Txt = <<"Node index not found">>,
- {error, xmpp:err_internal_server_error(Txt, ejabberd_config:get_mylang())};
+ {error, xmpp:err_internal_server_error(Txt, ejabberd_option:language())};
_ ->
lists:foreach(fun ({Key, Value}) ->
SKey = iolist_to_binary(atom_to_list(Key)),
@@ -121,9 +120,9 @@ get_node(Host, Node) ->
{selected, [RItem]} ->
raw_to_node(Host, RItem);
{'EXIT', _Reason} ->
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())};
_ ->
- {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())}
end.
get_node(Nidx) ->
@@ -135,9 +134,9 @@ get_node(Nidx) ->
{selected, [{Host, Node, Parent, Type}]} ->
raw_to_node(Host, {Node, Parent, Type, Nidx});
{'EXIT', _Reason} ->
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())};
_ ->
- {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_item_not_found(<<"Node not found">>, ejabberd_option:language())}
end.
get_nodes(Host, _From) ->
@@ -259,9 +258,9 @@ create_node(Host, Node, Type, Owner, Options, Parents) ->
{error, xmpp:err_forbidden()}
end;
{result, _} ->
- {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_config:get_mylang())};
+ {error, xmpp:err_conflict(<<"Node already exists">>, ejabberd_option:language())};
{error, db_fail} ->
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_config:get_mylang())}
+ {error, xmpp:err_internal_server_error(<<"Database failure">>, ejabberd_option:language())}
end.
delete_node(Host, Node) ->
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index 6eb5689c1..472f38b67 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -528,11 +528,11 @@ find_serverhost(Host) ->
fun(ServerHost) ->
case gen_mod:is_loaded(ServerHost, mod_muc) of
true ->
- Host == gen_mod:get_module_opt_host(ServerHost, mod_muc, <<"conference.@HOST@">>);
+ lists:member(Host, gen_mod:get_module_opt_hosts(ServerHost, mod_muc));
false ->
false
end
- end, ejabberd_config:get_myhosts()),
+ end, ejabberd_option:hosts()),
ServerHost.
deserialize(L) ->
diff --git a/src/pubsub_db_sql.erl b/src/pubsub_db_sql.erl
index a709ce8b2..4df6a8695 100644
--- a/src/pubsub_db_sql.erl
+++ b/src/pubsub_db_sql.erl
@@ -25,7 +25,6 @@
-module(pubsub_db_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
-author("pablo.polvorin@process-one.net").
diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl
index e3587df53..fc0086f04 100644
--- a/src/pubsub_migrate.erl
+++ b/src/pubsub_migrate.erl
@@ -24,7 +24,7 @@
%%%----------------------------------------------------------------------
-module(pubsub_migrate).
-
+-dialyzer({no_return, report_and_stop/2}).
-include("pubsub.hrl").
-include("logger.hrl").
diff --git a/src/pubsub_subscription.erl b/src/pubsub_subscription.erl
index 1ce1dc73f..66664a8ae 100644
--- a/src/pubsub_subscription.erl
+++ b/src/pubsub_subscription.erl
@@ -207,13 +207,13 @@ val_xfield(digest_frequency = Opt, [Val]) ->
N when is_integer(N) -> N;
_ ->
Txt = {<<"Value of '~s' should be integer">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(expire = Opt, [Val]) ->
try xmpp_util:decode_timestamp(Val)
catch _:{bad_timestamp, _} ->
Txt = {<<"Value of '~s' should be datetime string">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
val_xfield(show_values, Vals) -> Vals;
@@ -225,7 +225,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
N when is_integer(N) -> N;
_ ->
Txt = {<<"Value of '~s' should be integer">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end.
%% Convert XForm booleans to Erlang booleans.
@@ -235,7 +235,7 @@ xopt_to_bool(_, <<"false">>) -> false;
xopt_to_bool(_, <<"true">>) -> true;
xopt_to_bool(Option, _) ->
Txt = {<<"Value of '~s' should be boolean">>, [Option]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}.
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}.
%% Return a field for an XForm for Key, with data filled in, if
%% applicable, from Options.
diff --git a/src/pubsub_subscription_sql.erl b/src/pubsub_subscription_sql.erl
index 2b60ad0b3..f960d0382 100644
--- a/src/pubsub_subscription_sql.erl
+++ b/src/pubsub_subscription_sql.erl
@@ -172,13 +172,13 @@ val_xfield(digest_frequency = Opt, [Val]) ->
N when is_integer(N) -> N;
_ ->
Txt = {<<"Value of '~s' should be integer">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(expire = Opt, [Val]) ->
try xmpp_util:decode_timestamp(Val)
catch _:{bad_timestamp, _} ->
Txt = {<<"Value of '~s' should be datetime string">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end;
val_xfield(include_body = Opt, [Val]) -> xopt_to_bool(Opt, Val);
val_xfield(show_values, Vals) -> Vals;
@@ -190,7 +190,7 @@ val_xfield(subscription_depth = Opt, [Depth]) ->
N when is_integer(N) -> N;
_ ->
Txt = {<<"Value of '~s' should be integer">>, [Opt]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}
end.
%% Convert XForm booleans to Erlang booleans.
@@ -200,7 +200,7 @@ xopt_to_bool(_, <<"false">>) -> false;
xopt_to_bool(_, <<"true">>) -> true;
xopt_to_bool(Option, _) ->
Txt = {<<"Value of '~s' should be boolean">>, [Option]},
- {error, xmpp:err_not_acceptable(Txt, ejabberd_config:get_mylang())}.
+ {error, xmpp:err_not_acceptable(Txt, ejabberd_option:language())}.
%% Return a field for an XForm for Key, with data filled in, if
%% applicable, from Options.
diff --git a/src/rest.erl b/src/rest.erl
index 9c1b28068..b8cd84dea 100644
--- a/src/rest.erl
+++ b/src/rest.erl
@@ -25,11 +25,11 @@
-module(rest).
--behaviour(ejabberd_config).
-
-export([start/1, stop/1, get/2, get/3, post/4, delete/2,
put/4, patch/4, request/6, with_retry/4,
- opt_type/1]).
+ encode_json/1]).
+
+-include("logger.hrl").
-define(HTTP_TIMEOUT, 10000).
-define(CONNECT_TIMEOUT, 8000).
@@ -37,7 +37,7 @@
start(Host) ->
application:start(inets),
- Size = ejabberd_config:get_option({ext_api_http_pool_size, Host}, 100),
+ Size = ejabberd_option:ext_api_http_pool_size(Host),
httpc:set_options([{max_sessions, Size}]).
stop(_Host) ->
@@ -160,8 +160,7 @@ decode_json(<<"\r\n">>) -> [];
decode_json(Data) -> jiffy:decode(Data).
custom_headers(Server) ->
- case ejabberd_config:get_option({ext_api_headers, Server},
- <<>>) of
+ case ejabberd_option:ext_api_headers(Server) of
<<>> ->
[];
Hdrs ->
@@ -181,8 +180,7 @@ base_url(Server, Path) ->
Url = case BPath of
<<"http", _/binary>> -> BPath;
_ ->
- Base = ejabberd_config:get_option({ext_api_url, Server},
- <<"http://localhost/api">>),
+ Base = ejabberd_option:ext_api_url(Server),
case binary:last(Base) of
$/ -> <<Base/binary, BPath/binary>>;
_ -> <<Base/binary, "/", BPath/binary>>
@@ -210,12 +208,3 @@ url(Server, Path, Params) ->
|| P <- binary:split(Extra, <<"&">>, [global])],
url(Url, Custom++Params)
end.
-
--spec opt_type(atom()) -> fun((any()) -> any()) | [atom()].
-opt_type(ext_api_http_pool_size) ->
- fun (X) when is_integer(X), X > 0 -> X end;
-opt_type(ext_api_url) ->
- fun (X) -> iolist_to_binary(X) end;
-opt_type(ext_api_headers) ->
- fun (X) -> iolist_to_binary(X) end;
-opt_type(_) -> [ext_api_http_pool_size, ext_api_url, ext_api_headers].
diff --git a/src/str.erl b/src/str.erl
index bbf0d6a6e..07a5d09d5 100644
--- a/src/str.erl
+++ b/src/str.erl
@@ -289,7 +289,7 @@ format(Format, Args) ->
iolist_to_binary(io_lib:format(Format, Args)).
--spec sha(binary()) -> binary().
+-spec sha(iodata()) -> binary().
sha(Text) ->
Bin = crypto:hash(sha, Text),
diff --git a/src/translate.erl b/src/translate.erl
index b2c3e4481..0d262ac89 100644
--- a/src/translate.erl
+++ b/src/translate.erl
@@ -103,7 +103,8 @@ load(ForceCacheRebuild) ->
-spec load([file:filename()], file:filename()) -> ok.
load(Files, Dir) ->
- try ets:new(translations, [named_table, public])
+ try ets:new(translations, [named_table, public]) of
+ _ -> ok
catch _:badarg -> ok
end,
case Files of
@@ -124,9 +125,17 @@ load(Files, Dir) ->
load_file(Lang, File) ->
case file:open(File, [read]) of
{ok, Fd} ->
- io:setopts(Fd, [{encoding,latin1}]),
- load_file_loop(Fd, 1, File, Lang),
- file:close(Fd);
+ case io:setopts(Fd, [{encoding,latin1}]) of
+ ok ->
+ load_file_loop(Fd, 1, File, Lang),
+ file:close(Fd);
+ {error, Error} ->
+ ExitText = iolist_to_binary([File, ": ",
+ file:format_error(Error)]),
+ ?ERROR_MSG("Problem loading translation file ~n~s",
+ [ExitText]),
+ exit(ExitText)
+ end;
{error, Error} ->
ExitText = iolist_to_binary([File, ": ",
file:format_error(Error)]),
@@ -195,7 +204,7 @@ translate(Lang, Msg) ->
end.
translate(Msg) ->
- case ejabberd_config:get_mylang() of
+ case ejabberd_option:language() of
<<"en">> -> Msg;
Lang ->
LLang = ascii_tolower(Lang),