]> granicus.if.org Git - ejabberd/commitdiff
Use new configuration validator
authorEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 14 Jun 2019 09:33:26 +0000 (12:33 +0300)
committerEvgeny Khramtsov <ekhramtsov@process-one.net>
Fri, 14 Jun 2019 09:33:26 +0000 (12:33 +0300)
265 files changed:
Makefile.in
ejabberd.yml.example
include/ejabberd_http.hrl
include/ejabberd_router.hrl
include/ejabberd_sm.hrl
include/ejabberd_sql_pt.hrl
include/eldap.hrl
include/logger.hrl
include/mod_mam.hrl
include/mod_muc_room.hrl
rebar.config
src/ELDAPv3.erl
src/acl.erl
src/econf.erl [new file with mode: 0644]
src/ejabberd.erl
src/ejabberd_access_permissions.erl
src/ejabberd_acme.erl
src/ejabberd_admin.erl
src/ejabberd_app.erl
src/ejabberd_auth.erl
src/ejabberd_auth_anonymous.erl
src/ejabberd_auth_external.erl
src/ejabberd_auth_ldap.erl
src/ejabberd_auth_mnesia.erl
src/ejabberd_auth_pam.erl
src/ejabberd_auth_riak.erl
src/ejabberd_auth_sql.erl
src/ejabberd_bosh.erl
src/ejabberd_c2s.erl
src/ejabberd_c2s_config.erl
src/ejabberd_captcha.erl
src/ejabberd_cluster.erl
src/ejabberd_commands.erl
src/ejabberd_config.erl
src/ejabberd_config_transformer.erl [new file with mode: 0644]
src/ejabberd_ctl.erl
src/ejabberd_db_sup.erl [new file with mode: 0644]
src/ejabberd_hooks.erl
src/ejabberd_http.erl
src/ejabberd_http_ws.erl
src/ejabberd_iq.erl
src/ejabberd_listener.erl
src/ejabberd_local.erl
src/ejabberd_logger.erl
src/ejabberd_oauth.erl
src/ejabberd_oauth_mnesia.erl
src/ejabberd_oauth_rest.erl
src/ejabberd_oauth_sql.erl
src/ejabberd_old_config.erl [new file with mode: 0644]
src/ejabberd_option.erl [new file with mode: 0644]
src/ejabberd_options.erl [new file with mode: 0644]
src/ejabberd_piefxis.erl
src/ejabberd_pkix.erl
src/ejabberd_rdbms.erl
src/ejabberd_redis.erl
src/ejabberd_redis_sup.erl
src/ejabberd_regexp.erl
src/ejabberd_riak.erl
src/ejabberd_riak_sup.erl
src/ejabberd_router.erl
src/ejabberd_router_mnesia.erl
src/ejabberd_router_riak.erl
src/ejabberd_router_sql.erl
src/ejabberd_s2s.erl
src/ejabberd_s2s_in.erl
src/ejabberd_s2s_out.erl
src/ejabberd_service.erl
src/ejabberd_shaper.erl
src/ejabberd_sip.erl
src/ejabberd_sm.erl
src/ejabberd_sm_sql.erl
src/ejabberd_sql.erl
src/ejabberd_sql_pt.erl
src/ejabberd_sql_sup.erl
src/ejabberd_stun.erl
src/ejabberd_sup.erl
src/ejabberd_system_monitor.erl
src/ejabberd_web_admin.erl
src/ejabberd_websocket.erl
src/ejabberd_xmlrpc.erl
src/eldap_utils.erl
src/elixir_logger_backend.erl
src/ext_mod.erl
src/extauth.erl
src/gen_iq_handler.erl
src/gen_mod.erl
src/gen_pubsub_node.erl
src/gen_pubsub_nodetree.erl
src/misc.erl
src/mod_adhoc.erl
src/mod_adhoc_opt.erl [new file with mode: 0644]
src/mod_admin_extra.erl
src/mod_admin_update_sql.erl
src/mod_announce.erl
src/mod_announce_mnesia.erl
src/mod_announce_opt.erl [new file with mode: 0644]
src/mod_announce_sql.erl
src/mod_avatar.erl
src/mod_avatar_opt.erl [new file with mode: 0644]
src/mod_block_strangers.erl
src/mod_block_strangers_opt.erl [new file with mode: 0644]
src/mod_bosh.erl
src/mod_bosh_opt.erl [new file with mode: 0644]
src/mod_bosh_riak.erl
src/mod_bosh_sql.erl
src/mod_caps.erl
src/mod_caps_opt.erl [new file with mode: 0644]
src/mod_caps_sql.erl
src/mod_carboncopy.erl
src/mod_client_state.erl
src/mod_client_state_opt.erl [new file with mode: 0644]
src/mod_configure.erl
src/mod_delegation.erl
src/mod_delegation_opt.erl [new file with mode: 0644]
src/mod_disco.erl
src/mod_disco_opt.erl [new file with mode: 0644]
src/mod_echo.erl
src/mod_echo_opt.erl [new file with mode: 0644]
src/mod_fail2ban.erl
src/mod_fail2ban_opt.erl [new file with mode: 0644]
src/mod_http_api.erl
src/mod_http_api_opt.erl [new file with mode: 0644]
src/mod_http_fileserver.erl
src/mod_http_fileserver_opt.erl [new file with mode: 0644]
src/mod_http_upload.erl
src/mod_http_upload_opt.erl [new file with mode: 0644]
src/mod_http_upload_quota.erl
src/mod_http_upload_quota_opt.erl [new file with mode: 0644]
src/mod_last.erl
src/mod_last_mnesia.erl
src/mod_last_opt.erl [new file with mode: 0644]
src/mod_last_sql.erl
src/mod_legacy_auth.erl
src/mod_mam.erl
src/mod_mam_mnesia.erl
src/mod_mam_opt.erl [new file with mode: 0644]
src/mod_mam_sql.erl
src/mod_metrics.erl
src/mod_metrics_opt.erl [new file with mode: 0644]
src/mod_mix.erl
src/mod_mix_mnesia.erl
src/mod_mix_opt.erl [new file with mode: 0644]
src/mod_mix_pam.erl
src/mod_mix_pam_mnesia.erl
src/mod_mix_pam_opt.erl [new file with mode: 0644]
src/mod_mix_pam_sql.erl
src/mod_mix_sql.erl
src/mod_mqtt.erl
src/mod_mqtt_mnesia.erl
src/mod_mqtt_opt.erl [new file with mode: 0644]
src/mod_mqtt_session.erl
src/mod_mqtt_sql.erl
src/mod_muc.erl
src/mod_muc_admin.erl
src/mod_muc_log.erl
src/mod_muc_log_opt.erl [new file with mode: 0644]
src/mod_muc_mnesia.erl
src/mod_muc_opt.erl [new file with mode: 0644]
src/mod_muc_room.erl
src/mod_muc_sql.erl
src/mod_multicast.erl
src/mod_multicast_opt.erl [new file with mode: 0644]
src/mod_offline.erl
src/mod_offline_mnesia.erl
src/mod_offline_opt.erl [new file with mode: 0644]
src/mod_offline_sql.erl
src/mod_ping.erl
src/mod_ping_opt.erl [new file with mode: 0644]
src/mod_pres_counter.erl
src/mod_pres_counter_opt.erl [new file with mode: 0644]
src/mod_privacy.erl
src/mod_privacy_mnesia.erl
src/mod_privacy_opt.erl [new file with mode: 0644]
src/mod_privacy_sql.erl
src/mod_private.erl
src/mod_private_mnesia.erl
src/mod_private_opt.erl [new file with mode: 0644]
src/mod_private_sql.erl
src/mod_privilege.erl
src/mod_privilege_opt.erl [new file with mode: 0644]
src/mod_proxy65.erl
src/mod_proxy65_opt.erl [new file with mode: 0644]
src/mod_proxy65_riak.erl
src/mod_proxy65_service.erl
src/mod_proxy65_sql.erl
src/mod_proxy65_stream.erl
src/mod_pubsub.erl
src/mod_pubsub_mnesia.erl [new file with mode: 0644]
src/mod_pubsub_opt.erl [new file with mode: 0644]
src/mod_pubsub_riak.erl [new file with mode: 0644]
src/mod_pubsub_sql.erl [new file with mode: 0644]
src/mod_push.erl
src/mod_push_keepalive.erl
src/mod_push_keepalive_opt.erl [new file with mode: 0644]
src/mod_push_opt.erl [new file with mode: 0644]
src/mod_push_sql.erl
src/mod_register.erl
src/mod_register_opt.erl [new file with mode: 0644]
src/mod_register_web.erl
src/mod_roster.erl
src/mod_roster_mnesia.erl
src/mod_roster_opt.erl [new file with mode: 0644]
src/mod_roster_sql.erl
src/mod_s2s_dialback.erl
src/mod_s2s_dialback_opt.erl [new file with mode: 0644]
src/mod_service_log.erl
src/mod_service_log_opt.erl [new file with mode: 0644]
src/mod_shared_roster.erl
src/mod_shared_roster_ldap.erl
src/mod_shared_roster_ldap_opt.erl [new file with mode: 0644]
src/mod_shared_roster_mnesia.erl
src/mod_shared_roster_opt.erl [new file with mode: 0644]
src/mod_shared_roster_sql.erl
src/mod_sip.erl
src/mod_sip_opt.erl [new file with mode: 0644]
src/mod_sip_proxy.erl
src/mod_sip_registrar.erl
src/mod_stats.erl
src/mod_stream_mgmt.erl
src/mod_stream_mgmt_opt.erl [new file with mode: 0644]
src/mod_vcard.erl
src/mod_vcard_ldap.erl
src/mod_vcard_ldap_opt.erl [new file with mode: 0644]
src/mod_vcard_mnesia.erl
src/mod_vcard_mnesia_opt.erl [new file with mode: 0644]
src/mod_vcard_opt.erl [new file with mode: 0644]
src/mod_vcard_sql.erl
src/mod_vcard_xupdate.erl
src/mod_vcard_xupdate_opt.erl [new file with mode: 0644]
src/mod_version.erl
src/mod_version_opt.erl [new file with mode: 0644]
src/node_dag.erl
src/node_flat_sql.erl
src/node_pep_sql.erl
src/nodetree_dag.erl
src/nodetree_tree.erl
src/nodetree_tree_sql.erl
src/prosody2ejabberd.erl
src/pubsub_db_sql.erl
src/pubsub_migrate.erl
src/pubsub_subscription.erl
src/pubsub_subscription_sql.erl
src/rest.erl
src/str.erl
src/translate.erl
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ejabberd.extauth.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.ldap.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.mnesia.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.mysql.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.pgsql.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.redis.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.riak.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.sqlite.yml [new file with mode: 0644]
test/ejabberd_SUITE_data/ejabberd.yml
test/ejabberd_SUITE_data/macros.yml [new file with mode: 0644]
test/ldap_srv.erl
test/mam_tests.erl
test/muc_tests.erl
test/offline_tests.erl
test/suite.erl
test/suite.hrl
tools/hook_deps.sh
tools/opt_types.sh [new file with mode: 0755]
tools/xml_compress_gen.erl

index 1c3fbc204160eb2f46e47e076d6c7e8616446579..ca20ffc3dfedae955dca0f944f4fd8616b850969 100644 (file)
@@ -119,6 +119,11 @@ update:
 xref: all
        $(REBAR) skip_deps=true xref
 
+hooks: all
+       tools/hook_deps.sh ebin
+
+options: all
+       tools/opt_types.sh ebin
 
 translations:
        tools/prepare-tr.sh
@@ -335,8 +340,8 @@ dialyzer/erlang.plt:
        @mkdir -p dialyzer
        @dialyzer --build_plt --output_plt dialyzer/erlang.plt \
        -o dialyzer/erlang.log --apps kernel stdlib sasl crypto \
-       public_key ssl mnesia inets odbc tools compiler erts \
-       runtime_tools asn1 observer xmerl et gs wx syntax_tools; \
+       public_key ssl mnesia inets odbc compiler erts \
+       os_mon asn1 syntax_tools; \
        status=$$? ; if [ $$status -ne 2 ]; then exit $$status; else exit 0; fi
 
 dialyzer/deps.plt:
@@ -377,4 +382,4 @@ test:
 
 .PHONY: src edoc dialyzer Makefile TAGS clean clean-rel distclean rel \
        install uninstall uninstall-binary uninstall-all translations deps test \
-       quicktest erlang_plt deps_plt ejabberd_plt
+       quicktest erlang_plt deps_plt ejabberd_plt xref hooks options
index d3befd0071627a18c2e21c7e1984c20207dd63a7..51e4b53c4e734803d0e3790207746978848b0dc4 100644 (file)
 ### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
 ### *******************************************************
 ### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
-### However, ejabberd treats different literals as different types:
-###
-### - unquoted or single-quoted strings. They are called "atoms".
-###   Example: dog, 'Jupiter', '3.14159', YELLOW
-###
-### - numeric literals. Example: 3, -45.0, .0
-###
-### - quoted or folded strings.
-###   Examples of quoted string: "Lizzard", "orange".
-###   Example of folded string:
-###   > Art thou not Romeo,
-###     and a Montague?
 ###
 
 hosts:
-  - "localhost"
+  - localhost
 
 loglevel: 4
 log_rotate_size: 10485760
@@ -36,8 +24,8 @@ log_rotate_count: 1
 log_rate_limit: 100
 
 certfiles:
-  - "/etc/letsencrypt/live/localhost/fullchain.pem"
-  - "/etc/letsencrypt/live/localhost/privkey.pem"
+  - /etc/letsencrypt/live/localhost/fullchain.pem
+  - /etc/letsencrypt/live/localhost/privkey.pem
 
 listen:
   -
@@ -84,25 +72,25 @@ acl:
     user_regexp: ""
   loopback:
     ip:
-      - "127.0.0.0/8"
-      - "::1/128"
+      - 127.0.0.0/8
+      - ::1/128
 
 access_rules:
   local:
-    allow: local
+    allow: local
   c2s:
-    deny: blocked
-    - allow
+    deny: blocked
+    allow: all
   announce:
-    allow: admin
+    allow: admin
   configure:
-    allow: admin
+    allow: admin
   muc_create:
-    allow: local
+    allow: local
   pubsub_createnode:
-    allow: local
+    allow: local
   trusted_network:
-    allow: loopback
+    allow: loopback
 
 api_permissions:
   "console commands":
@@ -112,26 +100,26 @@ api_permissions:
     what: "*"
   "admin access":
     who:
-      access:
-        allow:
-          acl: loopback
-          acl: admin
-      oauth:
-        scope: "ejabberd:admin"
-        access:
-          allow:
-            acl: loopback
-            acl: admin
+      access:
+        allow:
+          acl: loopback
+          acl: admin
+      oauth:
+        scope: "ejabberd:admin"
+        access:
+          allow:
+            acl: loopback
+            acl: admin
     what:
       - "*"
       - "!stop"
       - "!start"
   "public commands":
     who:
-      - ip: "127.0.0.1/8"
+      ip: 127.0.0.1/8
     what:
-      - "status"
-      - "connected_users_number"
+      - status
+      - connected_users_number
 
 shaper:
   normal: 1000
@@ -140,11 +128,11 @@ shaper:
 shaper_rules:
   max_user_sessions: 10
   max_user_offline_messages:
-    5000: admin
-    - 100
+    5000: admin
+    100: all
   c2s_shaper:
-    none: admin
-    - normal
+    none: admin
+    normal: all
   s2s_shaper: fast
 
 modules:
@@ -163,7 +151,7 @@ modules:
   mod_fail2ban: {}
   mod_http_api: {}
   mod_http_upload:
-    put_url: "https://@HOST@:5443/upload"
+    put_url: https://@HOST@:5443/upload
   mod_last: {}
   mod_mam:
     ## Mnesia is limited to 2GB, better to use an SQL backend
@@ -196,11 +184,11 @@ modules:
   mod_pubsub:
     access_createnode: pubsub_createnode
     plugins:
-      - "flat"
-      - "pep"
+      - flat
+      - pep
     force_node_config:
       ## Avoid buggy clients to make their bookmarks public
-      "storage:bookmarks":
+      storage:bookmarks:
         access_model: whitelist
   mod_push: {}
   mod_push_keepalive: {}
index 33c02ca2b7be002e64990fdd4bb1d50f5e446798..7c237a223f092f7f7c69dad54b5ba28da402548d 100644 (file)
@@ -23,7 +23,7 @@
         path = []         :: [binary()],
         q = []            :: [{binary() | nokey, binary()}],
         us = {<<>>, <<>>} :: {binary(), binary()},
-        auth              :: {binary(), binary()} | {oauth, binary(), []} | undefined,
+        auth              :: {binary(), binary()} | {oauth, binary(), []} | undefined | invalid,
         lang = <<"">>     :: binary(),
         data = <<"">>     :: binary(),
         ip                :: {inet:ip_address(), inet:port_number()},
index 04ea6e304a83d07a740ffb9870a7eaedf545f73e..060ab79a1a4173f44ba7c4887ad43c5ff909b8ae 100644 (file)
@@ -2,7 +2,7 @@
 
 -type local_hint() :: integer() | {apply, atom(), atom()}.
 
--record(route, {domain :: binary() | '_',
-               server_host :: binary() | '_',
+-record(route, {domain :: binary(),
+               server_host :: binary(),
                pid :: undefined | pid(),
-               local_hint :: local_hint() | undefined | '_'}).
+               local_hint :: local_hint() | undefined}).
index 0d216e90c9377f9e9b3244d6ae244807aabc1cfa..8e7b6c7f4ccec4dc7176eb2679b8493f20abe9d3 100644 (file)
@@ -30,7 +30,7 @@
 -type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()}
                  | {oor, boolean()} | {auth_module, atom()}
                  | {num_stanzas_in, non_neg_integer()}
-                | offline].
+                | {atom(), term()}].
 -type prio() :: undefined | integer().
 
 -endif.
index 5906a2efb6dbce715693731362e170ff039ac5d8..54330bc463245454f1f601943439e3c9a16bd73f 100644 (file)
 %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 %%%
 %%%----------------------------------------------------------------------
-
--define(SQL_MARK, sql__mark_).
--define(SQL(SQL), ?SQL_MARK(SQL)).
-
--define(SQL_UPSERT_MARK, sql_upsert__mark_).
--define(SQL_UPSERT(Host, Table, Fields),
-        ejabberd_sql:sql_query(Host, ?SQL_UPSERT_MARK(Table, Fields))).
--define(SQL_UPSERT_T(Table, Fields),
-        ejabberd_sql:sql_query_t(?SQL_UPSERT_MARK(Table, Fields))).
-
--define(SQL_INSERT_MARK, sql_insert__mark_).
--define(SQL_INSERT(Table, Fields), ?SQL_INSERT_MARK(Table, Fields)).
-
--record(sql_query, {hash, format_query, format_res, args, loc}).
-
--record(sql_escape, {string, integer, boolean, in_array_string}).
+-compile([{parse_transform, ejabberd_sql_pt}]).
+-include("ejabberd_sql.hrl").
index f839aa94fd3c9fb95be985b090783068ceb4df83..be1d53d6ba152fa6036659c2ae6aaeb81cfb791d 100644 (file)
@@ -44,6 +44,7 @@
                       attributes = []    :: [{binary(), [binary()]}]}).
 
 -type tlsopts() :: [{encrypt, tls | starttls | none} |
+                   {tls_certfile, binary() | undefined} |
                     {tls_cacertfile, binary() | undefined} |
                     {tls_depth, non_neg_integer() | undefined} |
                     {tls_verify, hard | soft | false}].
 -type eldap_config() :: #eldap_config{}.
 -type eldap_search() :: #eldap_search{}.
 -type eldap_entry() :: #eldap_entry{}.
+
+-define(eldap_config(M, H),
+       #eldap_config{
+          servers = M:ldap_servers(H),
+          backups = M:ldap_backups(H),
+          tls_options = [{encrypt, M:ldap_encrypt(H)},
+                         {tls_verify, M:ldap_tls_verify(H)},
+                         {tls_certfile, M:ldap_tls_certfile(H)},
+                         {tls_cacertfile, M:ldap_tls_cacertfile(H)},
+                         {tls_depth, M:ldap_tls_depth(H)}],
+          port = M:ldap_port(H),
+          dn = M:ldap_rootdn(H),
+          password = M:ldap_password(H),
+          base = M:ldap_base(H),
+          deref_aliases = M:ldap_deref_aliases(H)}).
index 2dd23e1ad7e5476693b5ffd9b44edc1ec7cff3dd..aa3e9b3e1eac311455c9ebcbb6c0952bdc1df372 100644 (file)
 -compile([{parse_transform, lager_transform}]).
 
 -define(DEBUG(Format, Args),
-       lager:debug(Format, Args)).
+       lager:debug(Format, Args), ok).
 
 -define(INFO_MSG(Format, Args),
-       lager:info(Format, Args)).
+       lager:info(Format, Args), ok).
 
 -define(WARNING_MSG(Format, Args),
-       lager:warning(Format, Args)).
+       lager:warning(Format, Args), ok).
 
 -define(ERROR_MSG(Format, Args),
-       lager:error(Format, Args)).
+       lager:error(Format, Args), ok).
 
 -define(CRITICAL_MSG(Format, Args),
-       lager:critical(Format, Args)).
+       lager:critical(Format, Args), ok).
 
 %% Use only when trying to troubleshoot test problem with ExUnit
 -define(EXUNIT_LOG(Format, Args),
index 0d03014ad2de7e9a18efa0a8a75c38fd1d6c1be6..6ed6f84bf410c2911a6b42b52e04dfafae4fc9ec 100644 (file)
 %%%----------------------------------------------------------------------
 
 -record(archive_msg,
-       {us = {<<"">>, <<"">>}                :: {binary(), binary()} | '$2',
-        id = <<>>                            :: binary() | '_',
-        timestamp = erlang:timestamp()       :: erlang:timestamp() | '_' | '$1',
-        peer = {<<"">>, <<"">>, <<"">>}      :: ljid() | '_' | '$3' | undefined,
-        bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
-        packet = #xmlel{}                    :: xmlel() | message() | '_',
+       {us = {<<"">>, <<"">>}                :: {binary(), binary()},
+        id = <<>>                            :: binary(),
+        timestamp = erlang:timestamp()       :: erlang:timestamp(),
+        peer = {<<"">>, <<"">>, <<"">>}      :: ljid() | undefined,
+        bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid(),
+        packet = #xmlel{}                    :: xmlel() | message(),
         nick = <<"">>                        :: binary(),
         type = chat                          :: chat | groupchat}).
 
index efb702337a721b37249d99a39d95985c9b65b536..ac58c1f51da2d55cb35b9ca15630cb87e7506acb 100644 (file)
 
 -record(lqueue,
 {
-    queue   :: p1_queue:queue(),
-    max = 0 :: integer()
+    queue = p1_queue:new()  :: p1_queue:queue(),
+    max   = 0               :: integer()
 }).
 
 -type lqueue() :: #lqueue{}.
+-type lqueue_elem() :: {binary(), message(), boolean(),
+                       erlang:timestamp(), non_neg_integer()}.
 
 -record(config,
 {
@@ -63,7 +65,7 @@
     captcha_whitelist                    = (?SETS):empty() :: gb_sets:set(),
     mam                                  = false :: boolean(),
     pubsub                               = <<"">> :: binary(),
-    lang                                 = ejabberd_config:get_mylang() :: binary()
+    lang                                 = ejabberd_option:language() :: binary()
 }).
 
 -type config() :: #config{}.
@@ -89,8 +91,8 @@
 {
     message_time    = 0 :: integer(),
     presence_time   = 0 :: integer(),
-    message_shaper  = none :: shaper:shaper(),
-    presence_shaper = none :: shaper:shaper(),
+    message_shaper  = none :: ejabberd_shaper:shaper(),
+    presence_shaper = none :: ejabberd_shaper:shaper(),
     message :: message() | undefined,
     presence :: {binary(), presence()} | undefined
 }).
     robots                  = #{} :: map(),
     nicks                   = #{} :: map(),
     affiliations            = #{} :: map(),
-    history                 :: lqueue(),
+    history                 = #lqueue{} :: lqueue(),
     subject                 = [] :: [text()],
     subject_author          = <<"">> :: binary(),
     just_created            = erlang:system_time(microsecond) :: true | integer(),
     activity                = treap:empty() :: treap:treap(),
-    room_shaper             = none :: shaper:shaper(),
+    room_shaper             = none :: ejabberd_shaper:shaper(),
     room_queue              :: p1_queue:queue() | undefined
 }).
index 3673d44fcc32fb75d2908137b9fa90021f0511ea..1307b64e9466a093182adece50bc61e9097378ad 100644 (file)
         {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.36"}}},
         {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.3.4"}}},
         {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.19"}}},
+       {yconf, ".*", {git, "https://github.com/processone/yconf", "master"}},
         {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "0.14.8"}}},
         {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.5"}}},
         {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.2"}}},
         {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.8.4"}}},
-        {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.11"}}},
+        {eimp, ".*", {git, "https://github.com/processone/eimp", "master"}},
         {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.3"}}},
         {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.28"}}}},
         {if_var_true, sip, {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.29"}}}},
index 49457316450760dc19f2c6a2a2eaa7d2f46a9456..3c102e7ec463f64db6d56ac49d328a0ea3960981 100644 (file)
@@ -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),
 
index 959faeaf0a08be67af50de9faceae1aad3152b25..73dd5000a28707cf97936662dccf52d697d87c9b 100644 (file)
@@ -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
 %%% 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 (file)
index 0000000..ac8b9ca
--- /dev/null
@@ -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)).
index a7de9ab11187c9c50cf37bd79cb7dd4d7dc9897a..f7157f61d6309de3ae9848315fddb479a232e7e9 100644 (file)
@@ -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.".
index 0c53795b84af7c8d8aefc3fbe4c3fea024a303de..2f63cb576d45b08d7b67c83c96b2e90256f473b6 100644 (file)
 -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,
 
 -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]).
index 9e25ed4e6cbb6028db5e63d095c707a3bddd2aa2..cae6b71be6d20ba7306ac3b77961436ad09a7ada 100644 (file)
@@ -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].
index 017586ae6bb538d23c4602097c38d46a5f88e46c..da50b013be724285e6673bd93f91587bd6416ed1 100644 (file)
@@ -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]),
index 41e284de415757f68be26edd582a334075e417e0..ebb5bbeb2b894247471be7e4960dc060a6a6bdb2 100644 (file)
 -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.
index a3256364e7f75b5b62d3eeb2fca2eebc1dfcaf04..82ec5393eb2782e21732a60338669b4cfc577aca 100644 (file)
@@ -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].
index 767d99bf2053bbaa86aa958fb04b901b11c7e339..805076aff6e7ba5d66e5867cf7657e03b0fda021 100644 (file)
@@ -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].
index 6b2e2852eaa1da2f75bc88b093050f32f1165beb..bae540e218882dc9559c6ab842f593329ef1f74d 100644 (file)
 
 -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].
index 06000c7f1b1340e33d70908302a19d7c4a00e138..d52e7a1c02766e0530afe3969751cbdc104e489f 100644 (file)
@@ -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].
index 1f75f686d5a4d090a2f7dad4aea258197501226e..5c252b277fcc2c4e3769c03c579bdc055413233b 100644 (file)
@@ -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
index ffbab1f1fc16792f3f5219a8645bef4bd78cff9c..840fa9f40f570b538c0be65e9efeb294b9eeb971 100644 (file)
 %%%-------------------------------------------------------------------
 -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).
index 29da504c9a9d7ad943ae2dbd7e52c1e40e54043f..00f3f5227d89716812508921ee415896d6473da5 100644 (file)
@@ -25,8 +25,6 @@
 
 -module(ejabberd_auth_riak).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
-
 -author('alexey@process-one.net').
 
 -behaviour(ejabberd_auth).
index f6f4f9efbfc484c42965ee673cb3940dea9b5955..0d7ff1b81685d0ceb7d3e5477bfacf7070567a94 100644 (file)
 
 -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].
index 578466bba3f3beeb241810803c814c75ab6626b0..d119e90b84f87a7c28c4d724a744900dd2273b7c 100644 (file)
@@ -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) ->
index 492e2d89339585f2f44f638a2f9d3bec0364b14e..7caa109bb7e53048ce4d534f067bd5337b9545a6 100644 (file)
 %%%-------------------------------------------------------------------
 -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}].
index d6782de4f9cee6a4d3b65f9bbfda4eebf6d05af8..e3f982ebff5639c4df1fbabe1b4151ff08d4a89f 100644 (file)
@@ -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).
index 84317eb5593fb00a5b9c35cdd3fe0d8ab9343396..2d9a97fe32c068ec1429c7e93ca7343249d6b8ab 100644 (file)
@@ -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].
index 10f945fe718d9fb96c955f624c3a5f4711f81cd0..bf420e0f7fb8607a43d0a5a923413d87d14c40db 100644 (file)
@@ -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().
index 921ed25c72e29e7811dd64c0542c215d513676d1..921047e9f0b1495741f99c663421387ffcbd72cc 100644 (file)
 -author('badlop@process-one.net').
 
 -behaviour(gen_server).
--behaviour(ejabberd_config).
 
 -define(DEFAULT_VERSION, 1000000).
 
         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,
 
 -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.
index f4abf8d5509611732edff337e23859cc5e15c46f..84fd13394417ac6a5d98f64d3e7d9369a3cdef33 100644 (file)
 %%% 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 (file)
index 0000000..fc16207
--- /dev/null
@@ -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, []))},
+      []).
index f4a898c157b69b79ad0c5455b7acbd06906cda3f..e1c29400812fb5d9c2b0d8c22ab4cb23cf9afd19 100644 (file)
 
 -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 (file)
index 0000000..f16e60a
--- /dev/null
@@ -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
+%%%===================================================================
index bbd2050a3e0f751db3e1735c2107257f0b557584..6a5caaaca8c36c4bce89a8c38a9ec0c98a8b9b82 100644 (file)
@@ -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.
index 29d23e082cdbb2ae53c521e809e7cc4d59001f98..39413e2f3d8be786a4d22195859cdc188fae345f 100644 (file)
 
 -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},
index 26e68fdaa63e7cec7a59fe29288405bbadbf92ff..6e7a0901dff427abbd38f7060bb3b9315762d692 100644 (file)
 
 -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.
index 65902eeb929a93972e0d676aad3cf4843eaee4eb..4de88afa15b6f9ea18f6666d51d926eb69ffaf9f 100644 (file)
@@ -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().
index 73baf3142dfffd603cdd0b62600df7ebaa781429..f648b3eb3b616b77541dee34f9c7af07debfb50c 100644 (file)
 
 -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.
index 1384a2359dd579aa9d8caaf52a31b373d1bd0d4b..3b7c0f4f27b8be97a16d124dbaef6507d8249f3d 100644 (file)
@@ -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.
index e35a769e513540e0225cdf598e53ace680767400..529bc75f221aee1593c2c24497ce4f36022257c8 100644 (file)
 %%%-------------------------------------------------------------------
 -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
index 2913c8ef964636b907e1add3b8fc4a67dc48bbfd..3e1a0cf1c5a8f8ca828af857fa8e476f6289880a 100644 (file)
@@ -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].
index 1c55877ebb37f766da253f23082d3eb7ec82c017..dcc70505c553af9aa49074891d9bd1ed0567852c 100644 (file)
@@ -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).
-
index 215766d00a81d8738367c667b5227fd09320bb47..793e928b3c020bb97075dbb933d56d30a84feb9c 100644 (file)
 
 -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].
index e86e5be9ffd8e81fb1cc952a5355ece544c20b02..8ce2bc574baa32c7e98eb87e7f14b100caecf502 100644 (file)
@@ -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 (file)
index 0000000..13a0060
--- /dev/null
@@ -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 (file)
index 0000000..c7a2467
--- /dev/null
@@ -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 (file)
index 0000000..e25cc2e
--- /dev/null
@@ -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, <<"|">>).
index 02b79ed4d8c3a1f2fdee053bfa833feebdcfbeb6..68e5119a98b9f70405700b7c03ee3bd90594737a 100644 (file)
@@ -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) ->
index 39b69d03358d4813cf9482809303ff3efcff7b9d..fc5c5379a9b9d22671b44f4b5a67cac4cc271e53 100644 (file)
 %%%-------------------------------------------------------------------
 -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
index c6f5536d73821fd354f2080e9437c063ee43d714..3acb044f03b2df82043ef7ff99ae671eb11953bb 100644 (file)
 -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].
index 6284238a2abe545b66fd83408bf6db99d11afc6e..55e9dfcff0980ebf601c6ceb54b1be37267d1af2 100644 (file)
@@ -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) ->
index f34a96655361ae52a5217b8ab2723f4c7cc3e513..3b5d4b7aff17ea7c3bb033f30bafef49583aa522 100644 (file)
 -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.
index c454835a9a3d999f7377139b071d5bbcba513842..9e1a979a4e30c2e2ddac6b8fad66870d310ac540 100644 (file)
@@ -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.
-
index a86ac06d3e4585637996b555fcab11b23080f977..5eadcc8baff274f29e173da39d0b8bb98c26b328 100644 (file)
@@ -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
index 2598297b9fa3a80ca8848bf1499396a4ca0c9515..ce807267059045fba7dda03678d8f3d9fed1cb8c 100644 (file)
 -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].
index f6c6d2db26d87d2e15cdffd31349c6e2f7e59f14..6f75944c417148a195be302f371bb8a1990b10aa 100644 (file)
@@ -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].
index 03fbe495f83ce9fa805eaba110adfa07f5e7472a..329f5e0613a228b5836540259ee45b75e9a76af0 100644 (file)
@@ -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) ->
index 20346c369ccee425e684fa511462f4540222206f..434034cd94bf64bae8fc462879cfe8c2981b63a5 100644 (file)
@@ -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...", []),
index bc3ef52ef9501f49d611558795946df5ce598053..940333d40f407020785775d329e6d61838cdb312 100644 (file)
@@ -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,
index 77c511d928f19186ca647563c10903309eab57b6..4a56191ffbfe626789674b1ef76a7ba888b1ca84 100644 (file)
@@ -27,8 +27,6 @@
 
 -protocol({xep, 220, '1.1'}).
 
--behaviour(ejabberd_config).
-
 -author('alexey@process-one.net').
 
 -behaviour(gen_server).
         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].
index be2f8537037d47381c4de878ed81ebb6e8f2b5af..0eb5f2a1d434b80a7521ef9f8c4481462e119110 100644 (file)
 -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},
index d940284eff4a942acc20ef4d693c002808c42710..5b27f96c4386dd4189a9b3ecd943782c4be275d2 100644 (file)
 %%%-------------------------------------------------------------------
 -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].
index d78a1e2eafe77f0b0bb58383ed3f3c20d9cfea7c..3130823fcecbfbe004b68a050ef7667f7db17da7 100644 (file)
@@ -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},
index cad04986c448e439f9354f2f660da647d9d28a5c..8617b3609bc045b471c6e8994da62c1d41541074 100644 (file)
@@ -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
 %%% 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).
index 226cad3e8542c3d35d53c248d79f234c34ca9a32..4a2270beccbc5b9baa4b1397f2770aa9bfd71d9f 100644 (file)
@@ -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}].
index d60daca63d3dc7e5b9444bbe8e9e1e32aeb8d931..b85dbd9ccb0f773cc615ab25183d1d9ff5ef66f7 100644 (file)
@@ -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").
 
 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].
index 8c3efc9b35125868ac30019344ba7d6a717ae98e..71defb06ce4f6bd6620b732f643087c3c54230e7 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(ejabberd_sm_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(ejabberd_sm).
 
index 3431e61b8b238bc91488c4b5d8fe854479ef5e22..c7c2d2d4df02394024834d5546e70de4cdf5acc2 100644 (file)
@@ -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").
 
 -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].
index 2497c2a74db2d1fe3a06d9bff6ba2482ae7077fc..0896b4b1a812bf65698cda1af1aea9ef597c0865 100644 (file)
@@ -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' = [],
 %% 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}
index f16c23a00cf75d2ea6b814b8503192218fc0e461..b5553ec401147d36122b9a082bec74dbbf1069d2 100644 (file)
 
 -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].
index 159c576f4ddd6dfddded72ee485a509b31552514..e48183398d56852135153632c69b7ec8cb88771f 100644 (file)
@@ -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},
index edf15e43820f775899fde6ea7ee59ef55f14dd0d..1868a85ced2da84c83130ffc734862c5508e7f20 100644 (file)
@@ -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),
index e5af257a80ecd36ef86b2d01e3a8e2e18b3c5a11..1a4e3c765a13937c862684be5bc319988a5c6c7a 100644 (file)
 
 -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].
index a112eac35f71111da0ea9b1b21b0cf0e07c8e382..5eaef7582ce7b0a876bff5c689ca1af1d759d08a 100644 (file)
 
 -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=%%%%,%%%=:
index 2dde8add2a20eeeee99e2821f3c7fffcadcbb4a6..1767a1603f2ffbf2a5dd9a17417a628044f7251a 100644 (file)
 %%%----------------------------------------------------------------------
 
 -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().
index 067b26803849e5bc705ecd193afbc733bb03e83e..2c299d7f1e1653a0c2a74bf7c735c2af69f87baf 100644 (file)
@@ -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(
index 47e18aac3c1b0ba509630912562c7b73a20a56cb..40771d4aded4847925a1c1b576f2e1964d2963c9 100644 (file)
 
 -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}].
index 2bb8889afe16ae337477ee5cd1c33ada57a5b43c..794af31711c8b47e63c2050c217336113e6a7ee8 100644 (file)
 %%% 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.
index 328bb829e85f2a366f17becdba6f0d14316abb51..607f7b080c783756b545d209c69b5e19d2849595 100644 (file)
@@ -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].
index c617e6c2687d0dd50a03a2a3ca5ee1ba600948a1..9d9a59de25c7f455d117cbd9b8309ae889d97c6c 100644 (file)
@@ -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
index 38aa32e36e9f12c77f89ae11a5b206294310070f..cd94f0575fcf3111c75fb3922a086ee957b0d36b 100644 (file)
 
 -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
 %%====================================================================
index b972285f5719dae3ec18a698def4f3aa7d504c72..f0a35e316fcef3983d37ef865ba3dea1a113a434 100644 (file)
 %%% 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").
          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)]).
index 624b2fd07f5b93935d1ad64404abb202dded636e..e455a4292e19c05feac09ef791db7f4d47d1f91c 100644 (file)
     {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(),
index aba78a89a853f742faea9c87b7beff24fd5d80c3..813ed71cee0f49091d35e75333233cbc19ed5182 100644 (file)
@@ -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()) ->
index 4f683a431216698e6daf9b0fe91c5b6bd68d873d..0ebcbef78885a9e627b4b6d1aaab53ec916956e7 100644 (file)
@@ -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.
index f6a61fe03285d9ff2ffc8b7518503ef2badfd8b0..528da91d64f8012487476f20e15d40b0c2df5edc 100644 (file)
@@ -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 (file)
index 0000000..bb805a4
--- /dev/null
@@ -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).
+
index 739e533410c356608196b677d189d86cd18cf863..a066dc487bfdce8793c7502741bdba3153c27d0e 100644 (file)
@@ -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.
index 0212dab25ff62dd023e6007f1c2c36042aaad311..4f45afa94da4ae8e9f2b38f5d6fa03981acf39ec 100644 (file)
@@ -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;
index 6d11cde226bed8211e601b8e99eeb99d423b453b..cee9384d65054002d04d83831912a383d30c4c18 100644 (file)
@@ -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)}].
index ea7c54f36a2fd585b392633d6d6ea27edca562ce..c5a2a2d9a732a2ca8cbc942b9559078918366a81 100644 (file)
@@ -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 (file)
index 0000000..7292378
--- /dev/null
@@ -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).
+
index 60c3edcf6c1ef3b3a547c70d86866da5e13e8026..dc05afe20cf4de3e984f8498a19b4609e2b411e5 100644 (file)
@@ -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,
index e706a23c3781f81bff5c902086e259f579fbb31b..047b33e8256dac96ba7ecc3447e988ff70728f42 100644 (file)
@@ -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 (file)
index 0000000..187ed16
--- /dev/null
@@ -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).
+
index 486bea7fd8424ccd9349a47d4050142b53a34a1a..80504c9fb7c5f56c5d0b76b01a097d09ed6d29bd 100644 (file)
@@ -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 (file)
index 0000000..33dc6cc
--- /dev/null
@@ -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).
+
index 03bdc6e15140c93818f8c99e85f5bf4ac33c2afd..d8268e2be29e24515810bda06d9f0bd8e4d4fcbd 100644 (file)
@@ -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 (file)
index 0000000..44b9085
--- /dev/null
@@ -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).
+
index 7ebd1bd6fb8775d6a5149840cc3f5300fb944d08..08214174718b9f8275fa22b2463db932cbc74d74 100644 (file)
@@ -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...", []),
index 4ec65e779c11304f8937dd3782192f01de26b1ef..328211b839161c71a7de67c404be4d3b653b265c 100644 (file)
@@ -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]).
index 96b44cd6806c9a003e946b8c3f9e8ab292c935df..75c4d6356fe03eb72b1866d42e2dbc249ef8acd7 100644 (file)
@@ -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 (file)
index 0000000..9af68f1
--- /dev/null
@@ -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).
+
index b0829156ef420ed17ffbb4702b044fb0e02568fd..f32759564552052a700d0ebd77ea0f1d597696ce 100644 (file)
@@ -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]).
index 72c098570cf26e273aa9005cca67e62f9be914ff..4fe9540435e458848f52ad793cdcebc2d0399733 100644 (file)
@@ -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}].
+    [].
index e87a2635bea7779ac89c0d6c2f1f9a9d908384d7..0b9bab34d06f6dd302cb0a4001793c300bf4eec4 100644 (file)
@@ -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 (file)
index 0000000..ff286dc
--- /dev/null
@@ -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).
+
index 36ea751419bc0d06375a45dbb14ab37487fc65f6..915fa4a87514fb0cd0d4ed9e5666a91035ea8680 100644 (file)
@@ -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
index 92d238c0d7cace39f62a5fb1d529111fc5617f1e..e45ad293d3667f41a9b2c7e47ccc2007c9cf51c7 100644 (file)
@@ -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 (file)
index 0000000..9096500
--- /dev/null
@@ -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).
+
index f0d23a0ca1de7e3bd544944d241e8b2d575bbfa5..fd992d49df2f7508e5caa840f72297eab98ba8b3 100644 (file)
@@ -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 (file)
index 0000000..a66c329
--- /dev/null
@@ -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).
+
index eaa630ac7258d7fd2e94d7415b817a522bab14a1..32b17ef39cb0f7171f3c0c3f6e2f7d773d71c142 100644 (file)
@@ -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 (file)
index 0000000..8c030e6
--- /dev/null
@@ -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).
+
index 29247a0d12d1c08b3bdfdcbc968370c6c38f7d02..6ee65ca3e82baac55aaec33c36704265b8225a86 100644 (file)
@@ -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 (file)
index 0000000..15abbed
--- /dev/null
@@ -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).
+
index e3a738a07e86fba97a4b983ef83d6ff0ef46bc81..4e2ecdcdd1c35239699afc072318ca881937f721 100644 (file)
@@ -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").
 %% -------------------
 
 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 (file)
index 0000000..3d928fc
--- /dev/null
@@ -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).
+
index d34d7193eea4f9de8b92d847393da00fc5669274..fbbd1eebec27ce10f094a9fcef9a189ba323df55 100644 (file)
@@ -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 (file)
index 0000000..442ce1d
--- /dev/null
@@ -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).
+
index 364b3a0194e5f4745300b7f7817ebb4ef261eb49..dc02bb11dc583514ffa3b2c623cc8bfad679787c 100644 (file)
@@ -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,
 %%--------------------------------------------------------------------
 -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 (file)
index 0000000..a985c22
--- /dev/null
@@ -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).
+
index 10f7831bdc2e3eda89b630f693886c823d357b18..4f0d49c0b389e55dbee76b64c367fedd50ac5797 100644 (file)
 %%--------------------------------------------------------------------
 %% 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 (file)
index 0000000..acf739f
--- /dev/null
@@ -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).
+
index 1cb7470607d89f14713deb2bb0dafd568cddad32..17db7fabbe830fbdde48a17466d952e6a03a72da 100644 (file)
@@ -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)}].
index d8d5296f3fc18049142f06d727509d25ad09c373..7e4411443fad2dee339d194a0cf39bf9adb39722 100644 (file)
@@ -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 (file)
index 0000000..470ffce
--- /dev/null
@@ -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).
+
index 85f3e3895bf3bde6f10749351b3e1d7d1a9ca89b..46079717c14f811e50db00c6d19232b992fc9762 100644 (file)
@@ -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,
index 9848f5457f609caea342bdafc5f2a44adfb98d39..064d7eb3e904f19509c1b288c4c096d4ee5314fc 100644 (file)
@@ -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
index f050128cd87beaa4296d3e5c45373110b5dca85c..ba8a9bcfc42b1b31d3583a58da65542f9f7f4c8e 100644 (file)
@@ -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)}].
index dff10ef96b6030ec6602d775b039b0b9957b42e4..b964c977c5d54b4e69fb236bfba18f389f940ee2 100644 (file)
@@ -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 (file)
index 0000000..d8d970a
--- /dev/null
@@ -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).
+
index be87e64da1162aed57d99e060982903be287a7cf..94e50e0386f21951b8edc10730cb98ca4ae633ea 100644 (file)
@@ -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;
index 070f927e232ed9c9099656c7b14e799d1c0ce8b6..8c33aa89cc262f38db83811b62ad1474ec951ec4 100644 (file)
@@ -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 (file)
index 0000000..22b6567
--- /dev/null
@@ -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).
+
index 5625beac17ddfa902770a9409a4424df701490f2..01446314b0ee261cf6c5a465906b7112a2a87fbd 100644 (file)
@@ -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) ->
index 38c03d7610d877243a0876b43d0b06a75b73266d..2ffd32beec6c95a058e62511c402c4e23c8d3ef9 100644 (file)
@@ -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 (file)
index 0000000..b8225b1
--- /dev/null
@@ -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).
+
index 9bcbfbf21de1afe95d29027c838a0f17c9516ef2..b9eba337e0972cc616364474e24d47efcc852995 100644 (file)
@@ -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()].
index 568c4b9fa5d67c9cde0eaa66d0d808b506d2ed0f..7d14579ebe0f34c4fd98d6dcc593720a26a1beb3 100644 (file)
@@ -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 (file)
index 0000000..103e603
--- /dev/null
@@ -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).
+
index eda5966f11d77aeaebbe09ed6a620f9d5092b91c..c23046154bc7cef4b82ce7735ac24b33df9c62b3 100644 (file)
@@ -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]).
index 16f7c0d1749538bdd65c088fbfaeb629337e9693..e678486081b4fa2bfa839f3b721456f1b0c5ce2c 100644 (file)
@@ -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]).
index 566804f36f889c547e829214c0934ed8f54bc539..196b6efbe1169a6d96cad9a45de900e4061a49fa 100644 (file)
@@ -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()].
index b1b8b9dbd46a4cb729f78c077201361a87c36141..6f29a15224ff97ed75df5b2960ac0f9c7c8d2b76 100644 (file)
@@ -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 (file)
index 0000000..5459f39
--- /dev/null
@@ -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).
+
index bbcf9258a24b2e306f72f07b21a60a30ee1a6b82..c0777d266d145347711bcd246afb8393a45da488 100644 (file)
@@ -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
index a11f8e04cebb8633ceaad5b3343804ffadab0548..560d995fe49f23cd2e6102071bf27c743e9ab8eb 100644 (file)
@@ -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]).
index e546a94a9fa0df51231aa08e66aaaf296425d5e3..fb4595c94e4f1bf2670ae508db945d4dc6538b77 100644 (file)
@@ -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},
index 3672c2b9cdeaffe4ce599c21f1a401daa06bbb07..499987e1e1c31a09fcc400c67ecb2b3e83272fd9 100644 (file)
@@ -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) ->
index 3e4f672476975a2028f66264e8e4ccdac2918948..0e2ff69846e68556437b77de7a04940b3fc80a94 100644 (file)
@@ -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 (file)
index 0000000..1fbad70
--- /dev/null
@@ -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).
+
index fdd109a854ee76338cda16fc4310a47eab65ae07..1bc81e97d33aaacbed4e7f8b9fecd5b8e37babb5 100644 (file)
@@ -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 (file)
index 0000000..67c42e9
--- /dev/null
@@ -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).
+
index 67642a4de137b68c040c259efd240acbe8740607..e41b737f5335dc6d91275e9fb2c3ced999f7a9f4 100644 (file)
@@ -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;
                                     _ ->
index f041257f8aff76220cac2c36fb8c43420440787f..54e0315dfa0bac937e5ebab525637b436a2db117 100644 (file)
@@ -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);
        _ ->
index 509fe88931ac5c473c56d281f369c93c069a61e9..e8513bac2e4754ba74879d0c91601c8c3a8da195 100644 (file)
@@ -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 (file)
index 0000000..f149d1d
--- /dev/null
@@ -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).
+
index 76682d06c86b23d13e9da0298b0024f5e61fb0a4..1c1b22663c017f8ae9882852fcdc5b2a4e96cc1b 100644 (file)
@@ -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)}].
index 2356bbf034ad0c15b29667774a8a081c1b6f3bb9..d82735dee923a4e5ed3cc6fb1d15dc3130c24424 100644 (file)
@@ -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 (file)
index 0000000..bb5eac6
--- /dev/null
@@ -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).
+
index 97231695455a6f084ff4a4c890f821cac2c5f2ec..fe2ae42a4649f70c3ec13db186cc79b5d785a69a 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(mod_offline_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(mod_offline).
 
index fd3a08909bf58fdaedfd7b1b40fd3ef71fef5a2e..aeb3aedbd582b70ab357c195507237a490322077 100644 (file)
@@ -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 (file)
index 0000000..fd00521
--- /dev/null
@@ -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).
+
index 6c12904694551f8093d256e14d511362a389087b..9c50eb104d17ab575a42f445592701c4afb3de3a 100644 (file)
@@ -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 (file)
index 0000000..7964fe3
--- /dev/null
@@ -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).
+
index a25d30515b42cc5c858f20bf08ad31c6b9390546..988333f4f0174ae656883da36a6c6beafcae0ab8 100644 (file)
@@ -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)}].
index 1be9912fb1415e464a6477aa9f05f3294d4bef7c..be50894e0ec330d9e8c7a30496606c13b3844f00 100644 (file)
@@ -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 (file)
index 0000000..acc0f2a
--- /dev/null
@@ -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).
+
index 92c9f6c605a689dc61c6f627709dec637c07c053..ea3a86919af274a4164b385d6ce47cb86be2d330 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(mod_privacy_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(mod_privacy).
 
index f69f2cc96bc3c622e855ab3dd60f863ad5e5e64f..350817eea4069feab2fd5c802fe032518fff3db8 100644 (file)
@@ -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()].
index 03d98b1caacf43b21077e4ccc694c6ba722060a1..bf0ce26e8ce19c5172dc9f50d233de71d4a52ef1 100644 (file)
@@ -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 (file)
index 0000000..7125721
--- /dev/null
@@ -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).
+
index 1fa91a9d434989631b6d79d7d0e3b09ad2a392f2..e92c3e6ca0f2315752e2f735f7b90883d0bfca20 100644 (file)
@@ -23,7 +23,6 @@
 %%%----------------------------------------------------------------------
 
 -module(mod_private_sql).
--compile([{parse_transform, ejabberd_sql_pt}]).
 -behaviour(mod_private).
 
 %% API
index abb38456a03af8d135fdcaaf837df5b803ee6430..5eb4a15e4bfc65757790ff6b964450b424480c72 100644 (file)
@@ -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 (file)
index 0000000..64198b3
--- /dev/null
@@ -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).
+
index 0fca1cdcba4f49e5586c1ae397bd7206b9e1feff..4d8d06b43bf728905547518b3f6b98c05425c818 100644 (file)
@@ -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]).
     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 (file)
index 0000000..d65e74d
--- /dev/null
@@ -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).
+
index ec10157726dd5f85a5029d07387f49eacb8c21fc..a537a1b0146f268c29adddf4e7bd95fa60b0a441 100644 (file)
@@ -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...", []),
index 433371240cbe6473a59d990a244993292c51b0b4..35205fe38e53446df1b4ffbef417e558d09675dc 100644 (file)
@@ -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,
index 20fed220938c238a8a01edbdbeac51fec866f962..d655e274128da59634210d1d171dda5a2aa954c0 100644 (file)
@@ -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]).
index 02d2643ee56f829acb85133a2f3382c2cb9092d7..06cefdb9ff9dde4542284be87387243f669c42b0 100644 (file)
@@ -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}].
+    [].
index 1f220f3c69c9cbe1e322daa8134ca37c5311a915..1656a7cbd50a7affc80f3e88deae802c5e531775 100644 (file)
@@ -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 (file)
index 0000000..8f780a6
--- /dev/null
@@ -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 (file)
index 0000000..a771309
--- /dev/null
@@ -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 (file)
index 0000000..87000a8
--- /dev/null
@@ -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 (file)
index 0000000..655d43e
--- /dev/null
@@ -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
+%%%===================================================================
index 2cf34f65fdd176a3df337f1bbfe3519e7325b4b3..5dc924884fd9f276986b8d832a690ddbd0fd0d20 100644 (file)
@@ -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()].
index a7a7d8c92f2e41ca758883262a82fe71a11bad9f..574b1d7aaa810159469a8a427f9b325b5495db86 100644 (file)
@@ -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 (file)
index 0000000..82b1d51
--- /dev/null
@@ -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 (file)
index 0000000..6ab94b9
--- /dev/null
@@ -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).
+
index 50ad306849579b444360084c6ebe0baa035ac028..e665c1ce58bf1cc96efa0e9dd96babbec3f306cb 100644 (file)
@@ -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,
index 3ac2ef3f5021d8dee3df32c4c025d46026f2d044..9bbb02006c12a7b258e3c8aa9c94f73d19caa423 100644 (file)
@@ -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 (file)
index 0000000..53c6ca6
--- /dev/null
@@ -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).
+
index 4cd0deb534c0fa28a3852e0c52bf44a9f3c8a8e3..689d1383d0e08f796ea0876c6b92c6120d31a6a5 100644 (file)
@@ -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}) ->
index d313c2415230401549ec0d8b5055892a62cb80e4..a29259c579cda56893746334762b562ad76237ad 100644 (file)
@@ -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)}].
index 41da9a6b464c3e5b2e17ca4f1549a705d7d812c1..cee22d06bc24ff1d368f6f096277e51f93d21299 100644 (file)
@@ -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 (file)
index 0000000..4275bf4
--- /dev/null
@@ -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).
+
index a512f1bf5e8abba8e134fae96804d5ce9409b208..f62695a89bb92d500bbebea60b992dcaf54ced84 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(mod_roster_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(mod_roster).
 
index 55854a82bbf25f52f4f94412d7633453377f90bc..e64a0a93588241f15a9f2cff9c7b37e7210db23e 100644 (file)
 %%%-------------------------------------------------------------------
 -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").
 %%% 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 (file)
index 0000000..6f91c4d
--- /dev/null
@@ -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).
+
index 62b5e289fde6345869ac1f4017dbedfa98bf5463..eca95cb27b09df5fbeffcd92897503908983ef4b 100644 (file)
@@ -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 (file)
index 0000000..34eae49
--- /dev/null
@@ -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).
+
index d80258db7f175a6c4a06f66e5dd19dc41ea3797a..7e17e200591121da9f5fabf2c53eeb6a559e1061 100644 (file)
@@ -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)}].
index 327ec0a9d78af700ed13c078217130c8a1bda3de..524f65113e2b5a56e84cdeefd66231cf3bf81c6f 100644 (file)
@@ -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 (file)
index 0000000..5703ed0
--- /dev/null
@@ -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).
+
index a54b9687fec88e22c5fcdd3a0a1664eb3dd8820d..f7403d7b9de9e6d1cd9deeb85861244be902555a 100644 (file)
@@ -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 (file)
index 0000000..d0d2aaa
--- /dev/null
@@ -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).
+
index 39ca9fb0d7e86512da3651b8e8b85f8cf76dc714..a761f5e1117c9093842e4d66daf441d3f58cc6c7 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(mod_shared_roster_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(mod_shared_roster).
 
index 3cb2ac13e0918ea0a6aacf7ee0fde5a7a50e4e83..9dc4686a96b3b90257124631542766d5e8ffe135 100644 (file)
@@ -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 (file)
index 0000000..e160d2e
--- /dev/null
@@ -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).
+
index b2d9543ebf5123ca1568902b549923f61d5f7faa..ba1b90abf166debd82b15286d8b04a29ef786abe 100644 (file)
@@ -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),
index 4805e788f8570ed9d20702265d5b83d154aa4094..dc004a93c663fdce28a37e124c19771b599d50df 100644 (file)
@@ -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).
index 772d5109805659aaec1fa6dc042d7748e0509750..d3a70556400bf8ea5612a147c02863bc38f676c9 100644 (file)
@@ -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) ->
index 1a4308c58cb992dc6c5ea52ce9b66ea53045042b..58b2c4395a8fe95cb83f93050fca83f20828177a 100644 (file)
@@ -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 (file)
index 0000000..102906f
--- /dev/null
@@ -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).
+
index 6b6f5f7d548933f8b43eb79b6bfd17277fb1f54c..dc8e83879d4ef0d57ba2b55689cb03b02bdb71f0 100644 (file)
@@ -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)}].
index 2d00d44653e8db5bac0091aa9c43f9ad19743c60..cd2d9a1cfbe96fcd026226665b12ac5cc88e7dc0 100644 (file)
@@ -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 (file)
index 0000000..2f0109e
--- /dev/null
@@ -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).
+
index 31e9f6d4350ce5e6e5658bf188d9e0f81edfe8ae..d4394b67784a256fddba0ed23c3f73d23fe50861 100644 (file)
@@ -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 (file)
index 0000000..f326a84
--- /dev/null
@@ -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 (file)
index 0000000..79be37a
--- /dev/null
@@ -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).
+
index 93ef2e94824ef7617fb1e878e3bdc506387ca531..6b604161f9d1070ea655f290f271fe827620cba9 100644 (file)
@@ -24,7 +24,6 @@
 
 -module(mod_vcard_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -behaviour(mod_vcard).
 
index a674598b8713332614dc2451b061374918020806..1cdfcff329848080e114aa52a2b549fc9e697e4d 100644 (file)
@@ -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 (file)
index 0000000..a51e688
--- /dev/null
@@ -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).
+
index 7c1f28aea6dbaff351ca4f84ac509c0f545d4e34..41e97e4963605950ef5303c32ca4d1cf1aab18e5 100644 (file)
@@ -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 (file)
index 0000000..78d6231
--- /dev/null
@@ -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).
+
index d1d8ccd8e8a40307eae7c70afb56c2d6244cd6a9..03633ef1e8552042e1c5ac774424bd21d6e52623 100644 (file)
@@ -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,
index adb7d59c63b2a9751ae2579efb114e4e559baf71..5f494a9c056aa244573493ffe3198fde077d0e9b 100644 (file)
@@ -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) ->
index 4e6dd48722fc0890c1a20f17551942a607cf166c..ac3ab2196963bfac5331f4a9887024bcb7852703 100644 (file)
@@ -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").
index 1185ed81737dc7529844e82068da397f13613aa8..7e31d7106f0ebdd00874c2cad5681e801fd494d3 100644 (file)
@@ -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],
index 084fa322a92e4a93a5efead8583a9859e10a53a5..3398f0900c4531451d187cf9e132066a0e650615 100644 (file)
@@ -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) ->
index 311bbbf078fa1df0b8e57f009dc6ff50f87f7c4c..0a06645c3accdd2ad1b042193eecbf5a77925290 100644 (file)
@@ -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) ->
index 6eb5689c1bb081b0e568892e2dfd93e16a83aeba..472f38b67b196257a915d5640fe0cbd3c3ef0964 100644 (file)
@@ -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) ->
index a709ce8b2a51e2fc3d0f77d01f7c15ee649328ea..4df6a86953e5d48a176d472681d0e4746aa86768 100644 (file)
@@ -25,7 +25,6 @@
 
 -module(pubsub_db_sql).
 
--compile([{parse_transform, ejabberd_sql_pt}]).
 
 -author("pablo.polvorin@process-one.net").
 
index e3587df5333cd1abdf8313f119027e79c0fd6275..fc0086f04938e191b2f8c397a703e544af38dd23 100644 (file)
@@ -24,7 +24,7 @@
 %%%----------------------------------------------------------------------
 
 -module(pubsub_migrate).
-
+-dialyzer({no_return, report_and_stop/2}).
 -include("pubsub.hrl").
 -include("logger.hrl").
 
index 1ce1dc73f131b7c0e01f8f00becc93a8905c607d..66664a8aea8fb5c9c97204011880bffa0be7e9e0 100644 (file)
@@ -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.
index 2b60ad0b3c27e70a13d791abe0c1534c8e7d1b08..f960d0382c5a3f6d96c975f2b459b78f64d94800 100644 (file)
@@ -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.
index 9c1b28068a32bcade8e3ee10d66b4cd6e0f40d28..b8cd84deae27ced38346e99ad2063ed48970ecf5 100644 (file)
 
 -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].
index bbf0d6a6e14f7347968136b9613b3b14a04aac1d..07a5d09d5ced31206c21d9f745f650b54a9e0c7f 100644 (file)
@@ -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),
index b2c3e44811ad5a548b2b912ec533c713c55c1038..0d262ac89ba2313aeed6047f6a628b525a5201d2 100644 (file)
@@ -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),
index 6fa424e76db57788ad601904e181b3efb1902032..0f4db5e8fb64609c3ba509ccfa108c663842ad01 100644 (file)
 %%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 %%%
 %%%----------------------------------------------------------------------
-
-
 -module(ejabberd_SUITE).
-
 -compile(export_all).
 
 -import(suite, [init_config/1, connect/1, disconnect/1, recv_message/1,
@@ -62,23 +59,12 @@ init_per_suite(Config) ->
     start_ejabberd(NewConfig),
     NewConfig.
 
-start_ejabberd(Config) ->
-    case proplists:get_value(backends, Config) of
-        all ->
-            {ok, _} = application:ensure_all_started(ejabberd, transient);
-        Backends when is_list(Backends) ->
-            Hosts = lists:map(fun(Backend) -> Backend ++ ".localhost" end, Backends),
-            application:load(ejabberd),
-            AllHosts = Hosts ++ ["localhost"],    %% We always need localhost for the generic no_db tests
-            application:set_env(ejabberd, hosts, AllHosts),
-            {ok, _} = application:ensure_all_started(ejabberd, transient)
-    end.
+start_ejabberd(_) ->
+    {ok, _} = application:ensure_all_started(ejabberd, transient).
 
 end_per_suite(_Config) ->
     application:stop(ejabberd).
 
--define(BACKENDS, [mnesia,redis,mysql,pgsql,sqlite,ldap,extauth,riak]).
-
 init_per_group(Group, Config) ->
     case lists:member(Group, ?BACKENDS) of
         false ->
@@ -91,7 +77,7 @@ init_per_group(Group, Config) ->
                     do_init_per_group(Group, Config);
                 Backends ->
                     %% Skipped backends that were not explicitely enabled
-                    case lists:member(atom_to_list(Group), Backends) of
+                    case lists:member(Group, Backends) of
                         true ->
                             do_init_per_group(Group, Config);
                         false ->
@@ -149,8 +135,8 @@ do_init_per_group(riak, Config) ->
            {skip, {riak_not_available, Err}}
     end;
 do_init_per_group(s2s, Config) ->
-    ejabberd_config:add_option(s2s_use_starttls, required_trusted),
-    ejabberd_config:add_option(domain_certfile, "cert.pem"),
+    ejabberd_config:set_option({s2s_use_starttls, ?COMMON_VHOST}, required),
+    ejabberd_config:set_option(ca_file, "ca.pem"),
     Port = ?config(s2s_port, Config),
     set_opt(server, ?COMMON_VHOST,
            set_opt(xmlns, ?NS_SERVER,
@@ -200,8 +186,9 @@ end_per_group(riak, Config) ->
     end;
 end_per_group(component, _Config) ->
     ok;
-end_per_group(s2s, _Config) ->
-    ejabberd_config:add_option(s2s_use_starttls, false);
+end_per_group(s2s, Config) ->
+    Server = ?config(server, Config),
+    ejabberd_config:set_option({s2s_use_starttls, Server}, false);
 end_per_group(_GroupName, Config) ->
     stop_event_relay(Config),
     set_opt(anonymous, false, Config).
@@ -297,7 +284,7 @@ init_per_testcase(TestCase, OrigConfig) ->
         "test_starttls" ++ _ ->
             connect(Config);
         "test_zlib" ->
-            connect(Config);
+            auth(connect(starttls(connect(Config))));
         "test_register" ->
             connect(Config);
         "auth_md5" ->
@@ -355,8 +342,8 @@ no_db_tests() ->
        unauthenticated_message,
        unauthenticated_presence,
        test_starttls,
-       test_zlib,
        test_auth,
+       test_zlib,
        test_bind,
        test_open_session,
        codec_failure,
@@ -373,8 +360,7 @@ no_db_tests() ->
        presence,
        s2s_dialback,
        s2s_optional,
-       s2s_required,
-       s2s_required_trusted]},
+       s2s_required]},
      auth_external,
      auth_external_no_jid,
      auth_external_no_user,
@@ -389,7 +375,9 @@ no_db_tests() ->
      proxy65_tests:single_cases(),
      proxy65_tests:master_slave_cases(),
      replaced_tests:master_slave_cases(),
-     upload_tests:single_cases()].
+     upload_tests:single_cases(),
+     carbons_tests:single_cases(),
+     carbons_tests:master_slave_cases()].
 
 db_tests(riak) ->
     %% No support for mod_pubsub
@@ -406,15 +394,13 @@ db_tests(riak) ->
        vcard_tests:single_cases(),
        muc_tests:single_cases(),
        offline_tests:single_cases(),
-       carbons_tests:single_cases(),
        test_unregister]},
      muc_tests:master_slave_cases(),
      privacy_tests:master_slave_cases(),
      roster_tests:master_slave_cases(),
      offline_tests:master_slave_cases(riak),
      vcard_tests:master_slave_cases(),
-     announce_tests:master_slave_cases(),
-     carbons_tests:master_slave_cases()];
+     announce_tests:master_slave_cases()];
 db_tests(DB) when DB == mnesia; DB == redis ->
     [{single_user, [sequence],
       [test_register,
@@ -431,7 +417,6 @@ db_tests(DB) when DB == mnesia; DB == redis ->
        muc_tests:single_cases(),
        offline_tests:single_cases(),
        mam_tests:single_cases(),
-       carbons_tests:single_cases(),
        csi_tests:single_cases(),
        push_tests:single_cases(),
        test_unregister]},
@@ -443,7 +428,6 @@ db_tests(DB) when DB == mnesia; DB == redis ->
      mam_tests:master_slave_cases(),
      vcard_tests:master_slave_cases(),
      announce_tests:master_slave_cases(),
-     carbons_tests:master_slave_cases(),
      csi_tests:master_slave_cases(),
      push_tests:master_slave_cases()];
 db_tests(DB) ->
@@ -472,7 +456,6 @@ db_tests(DB) ->
      mam_tests:master_slave_cases(),
      vcard_tests:master_slave_cases(),
      announce_tests:master_slave_cases(),
-     carbons_tests:master_slave_cases(),
      push_tests:master_slave_cases()].
 
 ldap_tests() ->
@@ -754,27 +737,29 @@ test_component_send(Config) ->
     disconnect(Config).
 
 s2s_dialback(Config) ->
+    Server = ?config(server, Config),
     ejabberd_s2s:stop_s2s_connections(),
-    ejabberd_config:add_option(s2s_use_starttls, false),
-    ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"),
+    ejabberd_config:set_option({s2s_use_starttls, Server}, false),
+    ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, false),
+    ejabberd_config:set_option(ca_file, pkix:get_cafile()),
     s2s_ping(Config).
 
 s2s_optional(Config) ->
+    Server = ?config(server, Config),
     ejabberd_s2s:stop_s2s_connections(),
-    ejabberd_config:add_option(s2s_use_starttls, optional),
-    ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"),
+    ejabberd_config:set_option({s2s_use_starttls, Server}, optional),
+    ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, optional),
+    ejabberd_config:set_option(ca_file, pkix:get_cafile()),
     s2s_ping(Config).
 
 s2s_required(Config) ->
+    Server = ?config(server, Config),
     ejabberd_s2s:stop_s2s_connections(),
-    ejabberd_config:add_option(s2s_use_starttls, required),
-    ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"),
-    s2s_ping(Config).
-
-s2s_required_trusted(Config) ->
-    ejabberd_s2s:stop_s2s_connections(),
-    ejabberd_config:add_option(s2s_use_starttls, required),
-    ejabberd_config:add_option(domain_certfile, "cert.pem"),
+    gen_mod:stop_module(Server, mod_s2s_dialback),
+    gen_mod:stop_module(?MNESIA_VHOST, mod_s2s_dialback),
+    ejabberd_config:set_option({s2s_use_starttls, Server}, required),
+    ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, required),
+    ejabberd_config:set_option(ca_file, "ca.pem"),
     s2s_ping(Config).
 
 s2s_ping(Config) ->
diff --git a/test/ejabberd_SUITE_data/ejabberd.extauth.yml b/test/ejabberd_SUITE_data/ejabberd.extauth.yml
new file mode 100644 (file)
index 0000000..660ddcc
--- /dev/null
@@ -0,0 +1,5 @@
+define_macro:
+  EXTAUTH_CONFIG:
+    queue_type: ram
+    extauth_program: "python extauth.py"
+    auth_method: external
diff --git a/test/ejabberd_SUITE_data/ejabberd.ldap.yml b/test/ejabberd_SUITE_data/ejabberd.ldap.yml
new file mode 100644 (file)
index 0000000..5c481cb
--- /dev/null
@@ -0,0 +1,35 @@
+define_macro:
+  LDAP_CONFIG:
+    queue_type: ram
+    ldap_servers:
+      - "localhost"
+    ldap_rootdn: "cn=admin,dc=localhost"
+    ldap_port: 1389
+    ldap_password: "password"
+    ldap_base: "ou=users,dc=localhost"
+    auth_method: ldap
+    modules:
+      mod_vcard:
+        db_type: ldap
+      mod_roster: [] # mod_roster is required by mod_shared_roster
+      mod_shared_roster_ldap:
+        ldap_auth_check: off
+        ldap_base: "dc=localhost"
+        ldap_rfilter: "(objectClass=posixGroup)"
+        ldap_gfilter: "(&(objectClass=posixGroup)(cn=%g))"
+        ldap_memberattr: "memberUid"
+        ldap_ufilter: "(uid=%u)"
+        ldap_userdesc: "cn"
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.mnesia.yml b/test/ejabberd_SUITE_data/ejabberd.mnesia.yml
new file mode 100644 (file)
index 0000000..1c252d1
--- /dev/null
@@ -0,0 +1,62 @@
+define_macro:
+  MNESIA_CONFIG:
+    queue_type: ram
+    auth_method: internal
+    modules:
+      mod_announce:
+        db_type: internal
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: internal
+      mod_last:
+        db_type: internal
+      mod_muc:
+        db_type: internal
+      mod_offline:
+        db_type: internal
+      mod_privacy:
+        db_type: internal
+      mod_private:
+        db_type: internal
+      mod_pubsub:
+        access_createnode: pubsub_createnode
+        ignore_pep_from_offline: true
+        last_item_cache: false
+        plugins:
+          - "flat"
+          - "hometree"
+          - "pep"
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: internal
+      mod_mam:
+        db_type: internal
+      mod_vcard:
+        db_type: internal
+      mod_vcard_xupdate: []
+      mod_client_state:
+        queue_presence: true
+        queue_chat_states: true
+        queue_pep: true
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_push:
+        include_body: false
+      mod_push_keepalive: []
+      mod_s2s_dialback: []
+      mod_stream_mgmt:
+        resume_timeout: 3
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.mysql.yml b/test/ejabberd_SUITE_data/ejabberd.mysql.yml
new file mode 100644 (file)
index 0000000..89440b0
--- /dev/null
@@ -0,0 +1,68 @@
+define_macro:
+  MYSQL_CONFIG:
+    sql_username: MYSQL_USER
+    sql_type: mysql
+    sql_server: MYSQL_SERVER
+    sql_port: MYSQL_PORT
+    sql_pool_size: 1
+    sql_password: MYSQL_PASS
+    sql_database: MYSQL_DB
+    auth_method: sql
+    sm_db_type: sql
+    modules:
+      mod_announce:
+        db_type: sql
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: sql
+      mod_last:
+        db_type: sql
+      mod_muc:
+        db_type: sql
+        ram_db_type: sql
+      mod_offline:
+        db_type: sql
+      mod_privacy:
+        db_type: sql
+      mod_private:
+        db_type: sql
+      mod_pubsub:
+        db_type: sql
+        access_createnode: pubsub_createnode
+        ignore_pep_from_offline: true
+        last_item_cache: false
+        plugins:
+          - "flat"
+          - "hometree"
+          - "pep"
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: sql
+      mod_mam:
+        db_type: sql
+      mod_vcard:
+        db_type: sql
+      mod_vcard_xupdate: []
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_push:
+        db_type: sql
+        include_body: false
+      mod_push_keepalive: []
+      mod_s2s_dialback: []
+      mod_stream_mgmt:
+        resume_timeout: 3
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.pgsql.yml b/test/ejabberd_SUITE_data/ejabberd.pgsql.yml
new file mode 100644 (file)
index 0000000..840a513
--- /dev/null
@@ -0,0 +1,68 @@
+define_macro:
+  PGSQL_CONFIG:
+    sql_username: PGSQL_USER
+    sql_type: pgsql
+    sql_server: PGSQL_SERVER
+    sql_port: PGSQL_PORT
+    sql_pool_size: 1
+    sql_password: PGSQL_PASS
+    sql_database: PGSQL_DB
+    auth_method: sql
+    sm_db_type: sql
+    modules:
+      mod_announce:
+        db_type: sql
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: sql
+      mod_last:
+        db_type: sql
+      mod_muc:
+        db_type: sql
+        ram_db_type: sql
+      mod_offline:
+        db_type: sql
+      mod_privacy:
+        db_type: sql
+      mod_private:
+        db_type: sql
+      mod_pubsub:
+        db_type: sql
+        access_createnode: pubsub_createnode
+        ignore_pep_from_offline: true
+        last_item_cache: false
+        plugins:
+          - "flat"
+          - "hometree"
+          - "pep"
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: sql
+      mod_mam:
+        db_type: sql
+      mod_vcard:
+        db_type: sql
+      mod_vcard_xupdate: []
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_push:
+        db_type: sql
+        include_body: false
+      mod_push_keepalive: []
+      mod_s2s_dialback: []
+      mod_stream_mgmt:
+        resume_timeout: 3
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.redis.yml b/test/ejabberd_SUITE_data/ejabberd.redis.yml
new file mode 100644 (file)
index 0000000..91d5e54
--- /dev/null
@@ -0,0 +1,63 @@
+define_macro:
+  REDIS_CONFIG:
+    queue_type: ram
+    auth_method: internal
+    sm_db_type: redis
+    modules:
+      mod_announce:
+        db_type: internal
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: internal
+      mod_last:
+        db_type: internal
+      mod_muc:
+        db_type: internal
+      mod_offline:
+        db_type: internal
+      mod_privacy:
+        db_type: internal
+      mod_private:
+        db_type: internal
+      mod_pubsub:
+        access_createnode: pubsub_createnode
+        ignore_pep_from_offline: true
+        last_item_cache: false
+        plugins:
+          - "flat"
+          - "hometree"
+          - "pep"
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: internal
+      mod_mam:
+        db_type: internal
+      mod_vcard:
+        db_type: internal
+      mod_vcard_xupdate: []
+      mod_client_state:
+        queue_presence: true
+        queue_chat_states: true
+        queue_pep: true
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_push:
+        include_body: false
+      mod_push_keepalive: []
+      mod_s2s_dialback: []
+      mod_stream_mgmt:
+        resume_timeout: 3
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.riak.yml b/test/ejabberd_SUITE_data/ejabberd.riak.yml
new file mode 100644 (file)
index 0000000..0a64db9
--- /dev/null
@@ -0,0 +1,44 @@
+define_macro:
+  RIAK_CONFIG:
+    queue_type: ram
+    auth_method: riak
+    sm_db_type: riak
+    modules:
+      mod_announce:
+        db_type: riak
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: riak
+      mod_last:
+        db_type: riak
+      mod_muc:
+        db_type: riak
+      mod_offline:
+        db_type: riak
+      mod_privacy:
+        db_type: riak
+      mod_private:
+        db_type: riak
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: riak
+      mod_vcard:
+        db_type: riak
+      mod_vcard_xupdate: []
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_s2s_dialback: []
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
diff --git a/test/ejabberd_SUITE_data/ejabberd.sqlite.yml b/test/ejabberd_SUITE_data/ejabberd.sqlite.yml
new file mode 100644 (file)
index 0000000..3cf5dc5
--- /dev/null
@@ -0,0 +1,63 @@
+define_macro:
+  SQLITE_CONFIG:
+    sql_type: sqlite
+    sql_pool_size: 1
+    auth_method: sql
+    sm_db_type: sql
+    modules:
+      mod_announce:
+        db_type: sql
+        access: local
+      mod_blocking: []
+      mod_caps:
+        db_type: sql
+      mod_last:
+        db_type: sql
+      mod_muc:
+        db_type: sql
+        ram_db_type: sql
+      mod_offline:
+        db_type: sql
+      mod_privacy:
+        db_type: sql
+      mod_private:
+        db_type: sql
+      mod_pubsub:
+        db_type: sql
+        access_createnode: pubsub_createnode
+        ignore_pep_from_offline: true
+        last_item_cache: false
+        plugins:
+          - "flat"
+          - "hometree"
+          - "pep"
+      mod_roster:
+        versioning: true
+        store_current_id: true
+        db_type: sql
+      mod_mam:
+        db_type: sql
+      mod_vcard:
+        db_type: sql
+      mod_vcard_xupdate: []
+      mod_adhoc: []
+      mod_configure: []
+      mod_disco: []
+      mod_ping: []
+      mod_proxy65: []
+      mod_push:
+        db_type: sql
+        include_body: false
+      mod_push_keepalive: []
+      mod_s2s_dialback: []
+      mod_stream_mgmt:
+        resume_timeout: 3
+      mod_legacy_auth: []
+      mod_register:
+        welcome_message:
+          subject: "Welcome!"
+          body: "Hi.
+Welcome to this XMPP server."
+      mod_stats: []
+      mod_time: []
+      mod_version: []
index e720ac103272ed85ef592c9c1ded34ab3bf3696b..e5c3e783d85ff4c06f49db29d67e11f7fd3c9f60 100644 (file)
-host_config: 
-  "pgsql.localhost": 
-    sql_username: "@@pgsql_user@@"
-    sql_type: pgsql
-    sql_server: "@@pgsql_server@@"
-    sql_port: @@pgsql_port@@
-    sql_pool_size: 1
-    sql_password: "@@pgsql_pass@@"
-    sql_database: "@@pgsql_db@@"
-    auth_method: sql
-    sm_db_type: sql
-    modules: 
-      mod_announce: 
-        db_type: sql
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: sql
-      mod_last: 
-        db_type: sql
-      mod_muc: 
-        db_type: sql
-        ram_db_type: sql
-      mod_offline: 
-        db_type: sql
-      mod_privacy: 
-        db_type: sql
-      mod_private: 
-        db_type: sql
-      mod_pubsub: 
-        db_type: sql
-        access_createnode: pubsub_createnode
-        ignore_pep_from_offline: true
-        last_item_cache: false
-        plugins: 
-          - "flat"
-          - "hometree"
-          - "pep"
-      mod_roster:
-        versioning: true
-        store_current_id: true
-        db_type: sql
-      mod_mam:
-        db_type: sql
-      mod_vcard: 
-        db_type: sql
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: sql
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_push:
-        db_type: sql
-        include_body: false
-      mod_push_keepalive: []
-      mod_s2s_dialback: []
-      mod_stream_mgmt:
-        resume_timeout: 3
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "sqlite.localhost": 
-    sql_type: sqlite
-    sql_pool_size: 1
-    auth_method: sql
-    sm_db_type: sql
-    modules: 
-      mod_announce: 
-        db_type: sql
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: sql
-      mod_last: 
-        db_type: sql
-      mod_muc: 
-        db_type: sql
-        ram_db_type: sql
-      mod_offline: 
-        db_type: sql
-      mod_privacy: 
-        db_type: sql
-      mod_private: 
-        db_type: sql
-      mod_pubsub: 
-        db_type: sql
-        access_createnode: pubsub_createnode
-        ignore_pep_from_offline: true
-        last_item_cache: false
-        plugins: 
-          - "flat"
-          - "hometree"
-          - "pep"
-      mod_roster:
-        versioning: true
-        store_current_id: true
-        db_type: sql
-      mod_mam:
-        db_type: sql
-      mod_vcard: 
-        db_type: sql
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: sql
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_push:
-        db_type: sql
-        include_body: false
-      mod_push_keepalive: []
-      mod_s2s_dialback: []
-      mod_stream_mgmt:
-        resume_timeout: 3
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "mysql.localhost": 
-    sql_username: "@@mysql_user@@"
-    sql_type: mysql
-    sql_server: "@@mysql_server@@"
-    sql_port: @@mysql_port@@
-    sql_pool_size: 1
-    sql_password: "@@mysql_pass@@"
-    sql_database: "@@mysql_db@@"
-    auth_method: sql
-    sm_db_type: sql
-    modules: 
-      mod_announce: 
-        db_type: sql
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: sql
-      mod_last: 
-        db_type: sql
-      mod_muc: 
-        db_type: sql
-        ram_db_type: sql
-      mod_offline: 
-        db_type: sql
-      mod_privacy: 
-        db_type: sql
-      mod_private: 
-        db_type: sql
-      mod_pubsub: 
-        db_type: sql
-        access_createnode: pubsub_createnode
-        ignore_pep_from_offline: true
-        last_item_cache: false
-        plugins: 
-          - "flat"
-          - "hometree"
-          - "pep"
-      mod_roster: 
-        versioning: true
-        store_current_id: true
-        db_type: sql
-      mod_mam:
-        db_type: sql
-      mod_vcard: 
-        db_type: sql
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: sql
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_push:
-        db_type: sql
-        include_body: false
-      mod_push_keepalive: []
-      mod_s2s_dialback: []
-      mod_stream_mgmt:
-        resume_timeout: 3
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "mnesia.localhost":
-    queue_type: ram
-    auth_method: internal
-    modules: 
-      mod_announce: 
-        db_type: internal
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: internal
-      mod_last: 
-        db_type: internal
-      mod_muc: 
-        db_type: internal
-      mod_offline: 
-        db_type: internal
-      mod_privacy: 
-        db_type: internal
-      mod_private: 
-        db_type: internal
-      mod_pubsub: 
-        access_createnode: pubsub_createnode
-        ignore_pep_from_offline: true
-        last_item_cache: false
-        plugins: 
-          - "flat"
-          - "hometree"
-          - "pep"
-      mod_roster: 
-        versioning: true
-        store_current_id: true
-        db_type: internal
-      mod_mam:
-        db_type: internal
-      mod_vcard: 
-        db_type: internal
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: internal
-      mod_client_state:
-        queue_presence: true
-        queue_chat_states: true
-        queue_pep: true
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_push:
-        include_body: false
-      mod_push_keepalive: []
-      mod_s2s_dialback: []
-      mod_stream_mgmt:
-        resume_timeout: 3
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "redis.localhost":
-    queue_type: ram
-    auth_method: internal
-    sm_db_type: redis
-    modules: 
-      mod_announce: 
-        db_type: internal
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: internal
-      mod_last: 
-        db_type: internal
-      mod_muc: 
-        db_type: internal
-      mod_offline: 
-        db_type: internal
-      mod_privacy: 
-        db_type: internal
-      mod_private: 
-        db_type: internal
-      mod_pubsub: 
-        access_createnode: pubsub_createnode
-        ignore_pep_from_offline: true
-        last_item_cache: false
-        plugins: 
-          - "flat"
-          - "hometree"
-          - "pep"
-      mod_roster: 
-        versioning: true
-        store_current_id: true
-        db_type: internal
-      mod_mam:
-        db_type: internal
-      mod_vcard: 
-        db_type: internal
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: redis
-      mod_client_state:
-        queue_presence: true
-        queue_chat_states: true
-        queue_pep: true
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_push:
-        include_body: false
-      mod_push_keepalive: []
-      mod_s2s_dialback: []
-      mod_stream_mgmt:
-        resume_timeout: 3
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "riak.localhost":
-    queue_type: ram
-    auth_method: riak
-    sm_db_type: riak
-    modules: 
-      mod_announce: 
-        db_type: riak
-        access: local
-      mod_blocking: []
-      mod_caps: 
-        db_type: riak
-      mod_last: 
-        db_type: riak
-      mod_muc: 
-        db_type: riak
-      mod_offline: 
-        db_type: riak
-      mod_privacy: 
-        db_type: riak
-      mod_private: 
-        db_type: riak
-      mod_roster: 
-        versioning: true
-        store_current_id: true
-        db_type: riak
-      mod_vcard: 
-        db_type: riak
-      mod_vcard_xupdate: []
-      mod_carboncopy:
-        ram_db_type: riak
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_s2s_dialback: []
-      mod_legacy_auth: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "localhost": 
-    auth_method: [internal, anonymous]
-  "ldap.localhost":
-    queue_type: ram
-    ldap_servers: 
-      - "localhost"
-    ldap_rootdn: "cn=admin,dc=localhost"
-    ldap_port: 1389
-    ldap_password: "password"
-    ldap_base: "ou=users,dc=localhost"
-    auth_method: ldap
-    modules: 
-      mod_vcard:
-        db_type: ldap
-      mod_roster: [] # mod_roster is required by mod_shared_roster
-      mod_shared_roster_ldap:
-        ldap_auth_check: off
-        ldap_base: "dc=localhost"
-        ldap_rfilter: "(objectClass=posixGroup)"
-        ldap_gfilter: "(&(objectClass=posixGroup)(cn=%g))"
-        ldap_memberattr: "memberUid"
-        ldap_ufilter: "(uid=%u)"
-        ldap_userdesc: "cn"
-      mod_adhoc: []
-      mod_configure: []
-      mod_disco: []
-      mod_ping: []
-      mod_proxy65: []
-      mod_register: 
-        welcome_message: 
-          subject: "Welcome!"
-          body: "Hi.
-Welcome to this XMPP server."
-      mod_stats: []
-      mod_time: []
-      mod_version: []
-  "extauth.localhost":
-    queue_type: ram
-    extauth_program: "python extauth.py"
-    auth_method: external
-hosts: 
-  - "localhost"
-  - "mnesia.localhost"
-  - "redis.localhost"
-  - "mysql.localhost"
-  - "pgsql.localhost"
-  - "extauth.localhost"
-  - "ldap.localhost"
-  - "riak.localhost"
-  - "sqlite.localhost"
-access: 
-  announce: 
-    admin: allow
-  c2s: 
-    blocked: deny
-    all: allow
-  c2s_shaper: 
-    admin: none
-    all: normal
-  configure: 
-    admin: allow
-  local: 
-    local: allow
-  max_user_offline_messages: 
-    all: infinity
-  max_user_sessions: 
-    all: 10
-  muc: 
-    all: allow
-  muc_admin: 
-    admin: allow
-  muc_create: 
-    local: allow
-  pubsub_createnode: 
-    local: allow
-  register: 
-    all: allow
-  s2s_shaper: 
-    all: fast
-acl: 
-  local: 
+include_config_file:
+  - macros.yml
+  - ejabberd.extauth.yml
+  - ejabberd.ldap.yml
+  - ejabberd.mnesia.yml
+  - ejabberd.mysql.yml
+  - ejabberd.pgsql.yml
+  - ejabberd.redis.yml
+  - ejabberd.riak.yml
+  - ejabberd.sqlite.yml
+
+host_config:
+  pgsql.localhost: PGSQL_CONFIG
+  sqlite.localhost: SQLITE_CONFIG
+  mysql.localhost: MYSQL_CONFIG
+  mnesia.localhost: MNESIA_CONFIG
+  redis.localhost: REDIS_CONFIG
+  riak.localhost: RIAK_CONFIG
+  ldap.localhost: LDAP_CONFIG
+  extauth.localhost: EXTAUTH_CONFIG
+  localhost:
+    auth_method:
+      - internal
+      - anonymous
+
+hosts:
+  - localhost
+  - mnesia.localhost
+  - redis.localhost
+  - mysql.localhost
+  - pgsql.localhost
+  - extauth.localhost
+  - ldap.localhost
+  - riak.localhost
+  - sqlite.localhost
+
+shaper_rules:
+  c2s_shaper:
+    none: admin
+    normal: all
+  max_user_offline_messages:
+    infinity: all
+  max_user_sessions:
+    10: all
+  s2s_shaper:
+    fast: all
+
+access_rules:
+  announce:
+    allow: admin
+  c2s:
+    deny: blocked
+    allow: all
+  configure:
+    allow: admin
+  local:
+    allow: local
+  muc:
+    allow: all
+  muc_admin:
+    allow: admin
+  muc_create:
+    allow: local
+  pubsub_createnode:
+    allow: local
+  register:
+    allow: all
+
+acl:
+  local:
     user_regexp: ""
-define_macro: 
-  CERTFILE: "cert.pem"
-  CAFILE: "ca.pem"
-language: "en"
-listen: 
-  - 
-    port: @@c2s_port@@
+language: en
+listen:
+  -
+    port: C2S_PORT
     module: ejabberd_c2s
     max_stanza_size: 65536
-    certfile: CERTFILE
-    cafile: CAFILE
     zlib: true
     starttls: true
     tls_verify: true
     shaper: c2s_shaper
     access: c2s
-  - 
-    port: @@s2s_port@@
+  -
+    port: S2S_PORT
     module: ejabberd_s2s_in
-  - 
-    port: @@web_port@@
+  -
+    port: WEB_PORT
     module: ejabberd_http
-    captcha: true
     request_handlers:
       "/api": mod_http_api
       "/upload": mod_http_upload
-  - 
-    port: @@component_port@@
+      "/captcha": ejabberd_captcha
+  -
+    port: COMPONENT_PORT
     module: ejabberd_service
-    password: >-
-      @@password@@
-loglevel: @@loglevel@@
+    password: PASSWORD
+loglevel: LOGLEVEL
 max_fsm_queue: 1000
 queue_type: file
-modules: 
+modules:
   mod_adhoc: []
+  mod_announce: []
   mod_configure: []
   mod_disco: []
   mod_ping: []
   mod_proxy65: []
   mod_muc: []
   mod_muc_admin: []
-  mod_register: 
-    welcome_message: 
+  mod_carboncopy: []
+  mod_mam: []
+  mod_last: []
+  mod_register:
+    welcome_message:
       subject: "Welcome!"
       body: "Hi.
 Welcome to this XMPP server."
@@ -511,21 +123,23 @@ Welcome to this XMPP server."
   mod_time: []
   mod_version: []
   mod_http_upload:
-    docroot: "@@priv_dir@@"
-    put_url: "http://upload.@HOST@:@@web_port@@/upload"
-    get_url: "http://upload.@HOST@:@@web_port@@/upload"
+    docroot: PRIV_DIR
+    put_url: PUT_URL
+    get_url: GET_URL
     max_size: 10000
 registration_timeout: infinity
 route_subdomains: s2s
-domain_certfile: CERTFILE
 s2s_use_starttls: false
-s2s_cafile: CAFILE
-outgoing_s2s_port: @@s2s_port@@
-shaper: 
+ca_file: CAFILE
+c2s_cafile: CAFILE
+outgoing_s2s_port: S2S_PORT
+shaper:
   fast: 50000
   normal: 10000
+certfiles:
+  - CERTFILE
 
-new_sql_schema: @@new_schema@@
+new_sql_schema: NEW_SCHEMA
 
 api_permissions:
   "public commands":
diff --git a/test/ejabberd_SUITE_data/macros.yml b/test/ejabberd_SUITE_data/macros.yml
new file mode 100644 (file)
index 0000000..9ba6a56
--- /dev/null
@@ -0,0 +1,24 @@
+define_macro:
+  CERTFILE: cert.pem
+  CAFILE: ca.pem
+  C2S_PORT: @@c2s_port@@
+  S2S_PORT: @@s2s_port@@
+  WEB_PORT: @@web_port@@
+  COMPONENT_PORT: @@component_port@@
+  PASSWORD: >-
+    @@password@@
+  LOGLEVEL: @@loglevel@@
+  PRIV_DIR: "@@priv_dir@@"
+  PUT_URL: "http://upload.@HOST@:@@web_port@@/upload"
+  GET_URL: "http://upload.@HOST@:@@web_port@@/upload"
+  NEW_SCHEMA: @@new_schema@@
+  MYSQL_USER: "@@mysql_user@@"
+  MYSQL_SERVER: "@@mysql_server@@"
+  MYSQL_PORT: @@mysql_port@@
+  MYSQL_PASS: "@@mysql_pass@@"
+  MYSQL_DB: "@@mysql_db@@"
+  PGSQL_USER: "@@pgsql_user@@"
+  PGSQL_SERVER: "@@pgsql_server@@"
+  PGSQL_PORT: @@pgsql_port@@
+  PGSQL_PASS: "@@pgsql_pass@@"
+  PGSQL_DB: "@@pgsql_db@@"
index 8ac5a7b89e5746a41ee8f127710167ab9cdd8ca9..7ad53dda32b0533410571fdbce5237a49e439a0c 100644 (file)
@@ -71,7 +71,7 @@ init([LDIFFile]) ->
             case load_ldif(LDIFFile) of
                 {ok, Tree} ->
                     ?INFO_MSG("LDIF tree loaded, "
-                              "ready to accept connections", []),
+                              "ready to accept connections at ~B", [1389]),
                     {_Pid, MRef} =
                         spawn_monitor(
                           fun() -> accept(ListenSocket, Tree) end
index 69afacd2efd3e7fae4d3482fd660fec736ccfefb..75229becb10d7fd50889e7336e3bc6e4f2315407 100644 (file)
@@ -387,9 +387,9 @@ mucsub_from_muc_master(Config) ->
 
 mucsub_from_muc_slave(Config) ->
     Server = ?config(server, Config),
-    gen_mod:update_module_opts(Server, mod_mam, [{user_mucsub_from_muc_archive, true}]),
+    gen_mod:update_module(Server, mod_mam, #{user_mucsub_from_muc_archive => true}),
     Config2 = mucsub_slave(Config),
-    gen_mod:update_module_opts(Server, mod_mam, [{user_mucsub_from_muc_archive, false}]),
+    gen_mod:update_module(Server, mod_mam, #{user_mucsub_from_muc_archive => false}),
     Config2.
 
 mucsub_from_muc_non_persistent_master(Config) ->
index e744e3fc91f4e4af130f7ff847c10b468831c040..2eb52eb19075a8613231a6b8456fd817958c1631 100644 (file)
@@ -468,7 +468,7 @@ history_master(Config) ->
     MyNick = ?config(nick, Config),
     MyNickJID = jid:replace_resource(Room, MyNick),
     PeerNickJID = peer_muc_jid(Config),
-    Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20),
+    Size = mod_muc_opt:history_size(iolist_to_binary(ServerHost)),
     ok = join_new(Config),
     ct:comment("Putting ~p+1 messages in the history", [Size]),
     %% Only Size messages will be stored
@@ -496,7 +496,7 @@ history_slave(Config) ->
     PeerNick = ?config(peer_nick, Config),
     PeerNickJID = jid:replace_resource(Room, PeerNick),
     ServerHost = ?config(server_host, Config),
-    Size = gen_mod:get_module_opt(ServerHost, mod_muc, history_size, 20),
+    Size = mod_muc_opt:history_size(iolist_to_binary(ServerHost)),
     ct:comment("Waiting for 'join' command from the master"),
     join = get_event(Config),
     {History, _, _} = join(Config),
@@ -1785,7 +1785,7 @@ master_join(Config) ->
     wait_for_slave(Config),
     #muc_user{items = [#muc_item{jid = PeerJID,
                                 role = participant,
-                                affiliation = none}]} = 
+                                affiliation = none}]} =
        recv_muc_presence(Config, PeerNickJID, available),
     ok.
 
index 97d9ee58b392271a978cfd04e94f6184165d3f6a..a3a7aad904da87f66610a696dfbe7272389e0347 100644 (file)
@@ -189,11 +189,11 @@ from_mam_master(Config) ->
 
 from_mam_slave(Config) ->
     Server = ?config(server, Config),
-    gen_mod:update_module_opts(Server, mod_offline, [{use_mam_for_storage, true}]),
+    gen_mod:update_module(Server, mod_offline, #{use_mam_for_storage => true}),
     ok = mam_tests:set_default(Config, always),
     C2 = lists:keystore(mam_enabled, 1, Config, {mam_enabled, true}),
     C3 = send_all_slave(C2),
-    gen_mod:update_module_opts(Server, mod_offline, [{use_mam_for_storage, false}]),
+    gen_mod:update_module(Server, mod_offline, #{use_mam_for_storage => false}),
     C4 = lists:keydelete(mam_enabled, 1, C3),
     mam_tests:clean(C4).
 
@@ -234,8 +234,8 @@ mucsub_mam_master(Config) ->
 
 mucsub_mam_slave(Config) ->
     Server = ?config(server, Config),
-    gen_mod:update_module_opts(Server, mod_offline, [{use_mam_for_storage, true}]),
-    gen_mod:update_module_opts(Server, mod_mam, [{user_mucsub_from_muc_archive, true}]),
+    gen_mod:update_module(Server, mod_offline, #{use_mam_for_storage => true}),
+    gen_mod:update_module(Server, mod_mam, #{user_mucsub_from_muc_archive => true}),
 
     Room = suite:muc_room_jid(Config),
     MyJID = my_jid(Config),
@@ -268,8 +268,8 @@ mucsub_mam_slave(Config) ->
     ]}, #iq{type = result}),
     suite:put_event(Config, ready),
     mam_tests:clean(clean(disconnect(Config))),
-    gen_mod:update_module_opts(Server, mod_offline, [{use_mam_for_storage, false}]),
-    gen_mod:update_module_opts(Server, mod_mam, [{user_mucsub_from_muc_archive, false}]).
+    gen_mod:update_module(Server, mod_offline, #{use_mam_for_storage => false}),
+    gen_mod:update_module(Server, mod_mam, #{user_mucsub_from_muc_archive => false}).
 
 send_all_master(Config) ->
     wait_for_slave(Config),
index b466bd02a653c123dcaa7b68445c3934b535bef2..7e5fa3eba9661f221ae2c3b284e7e27e36114927 100644 (file)
@@ -38,7 +38,8 @@ init_config(Config) ->
     PrivDir = proplists:get_value(priv_dir, Config),
     [_, _|Tail] = lists:reverse(filename:split(DataDir)),
     BaseDir = filename:join(lists:reverse(Tail)),
-    ConfigPathTpl = filename:join([DataDir, "ejabberd.yml"]),
+    MacrosPathTpl = filename:join([DataDir, "macros.yml"]),
+    ConfigPath = filename:join([DataDir, "ejabberd.yml"]),
     LogPath = filename:join([PrivDir, "ejabberd.log"]),
     SASLPath = filename:join([PrivDir, "sasl.log"]),
     MnesiaDir = filename:join([PrivDir, "mnesia"]),
@@ -50,46 +51,32 @@ init_config(Config) ->
     {ok, _} = file:copy(SelfSignedCertFile,
                        filename:join([CWD, "self-signed-cert.pem"])),
     {ok, _} = file:copy(CAFile, filename:join([CWD, "ca.pem"])),
-    {ok, CfgContentTpl} = file:read_file(ConfigPathTpl),
+    {ok, MacrosContentTpl} = file:read_file(MacrosPathTpl),
     Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>,
-    CfgContent = process_config_tpl(CfgContentTpl, [
-                                                    {c2s_port, 5222},
-                                                    {loglevel, 4},
-                                                    {new_schema, false},
-                                                    {s2s_port, 5269},
-                                                   {component_port, 5270},
-                                                    {web_port, 5280},
-                                                   {password, Password},
-                                                    {mysql_server, <<"localhost">>},
-                                                    {mysql_port, 3306},
-                                                    {mysql_db, <<"ejabberd_test">>},
-                                                    {mysql_user, <<"ejabberd_test">>},
-                                                    {mysql_pass, <<"ejabberd_test">>},
-                                                    {pgsql_server, <<"localhost">>},
-                                                    {pgsql_port, 5432},
-                                                    {pgsql_db, <<"ejabberd_test">>},
-                                                    {pgsql_user, <<"ejabberd_test">>},
-                                                    {pgsql_pass, <<"ejabberd_test">>},
-                                                   {priv_dir, PrivDir}
-                                                  ]),
-    HostTypes = re:split(CfgContent, "(\\s*- \"(.*)\\.localhost\")",
-                        [group, {return, binary}]),
-    Types = [binary_to_list(Type) || [_, _, Type] <- HostTypes],
-    Backends = get_config_backends(Types),
-    HostTypes = re:split(CfgContent, "(\\s*- \"(.*)\\.localhost\")",
-                          [group, {return, binary}]),
-    CfgContent2 = lists:foldl(fun([Pre, Frag, Type], Acc) ->
-                                     case lists:member(binary_to_list(Type), Backends) of
-                                         true ->
-                                             <<Acc/binary, Pre/binary, Frag/binary>>;
-                                         _ ->
-                                             <<Acc/binary, Pre/binary>>
-                                     end;
-                                ([Rest], Acc) ->
-                                     <<Acc/binary, Rest/binary>>
-                             end, <<>>, HostTypes),
-    ConfigPath = filename:join([CWD, "ejabberd.yml"]),
-    ok = file:write_file(ConfigPath, CfgContent2),
+    Backends = get_config_backends(),
+    MacrosContent = process_config_tpl(
+                     MacrosContentTpl,
+                     [{c2s_port, 5222},
+                      {loglevel, 4},
+                      {new_schema, false},
+                      {s2s_port, 5269},
+                      {component_port, 5270},
+                      {web_port, 5280},
+                      {password, Password},
+                      {mysql_server, <<"localhost">>},
+                      {mysql_port, 3306},
+                      {mysql_db, <<"ejabberd_test">>},
+                      {mysql_user, <<"ejabberd_test">>},
+                      {mysql_pass, <<"ejabberd_test">>},
+                      {pgsql_server, <<"localhost">>},
+                      {pgsql_port, 5432},
+                      {pgsql_db, <<"ejabberd_test">>},
+                      {pgsql_user, <<"ejabberd_test">>},
+                      {pgsql_pass, <<"ejabberd_test">>},
+                      {priv_dir, PrivDir}]),
+    MacrosPath = filename:join([CWD, "macros.yml"]),
+    ok = file:write_file(MacrosPath, MacrosContent),
+    copy_backend_configs(DataDir, CWD, Backends),
     setup_ejabberd_lib_path(Config),
     case application:load(sasl) of
        ok -> ok;
@@ -141,6 +128,29 @@ init_config(Config) ->
      {backends, Backends}
      |Config].
 
+copy_backend_configs(DataDir, CWD, Backends) ->
+    Files = filelib:wildcard(filename:join([DataDir, "ejabberd.*.yml"])),
+    lists:foreach(
+      fun(Src) ->
+             File = filename:basename(Src),
+             case string:tokens(File, ".") of
+                 ["ejabberd", SBackend, "yml"] ->
+                     Backend = list_to_atom(SBackend),
+                     Macro = list_to_atom(string:to_upper(SBackend) ++ "_CONFIG"),
+                     Dst = filename:join([CWD, File]),
+                     case lists:member(Backend, Backends) of
+                         true ->
+                             {ok, _} = file:copy(Src, Dst);
+                         false ->
+                             ok = file:write_file(
+                                    Dst, fast_yaml:encode(
+                                           [{define_macro, [{Macro, []}]}]))
+                     end;
+                 _ ->
+                     ok
+             end
+      end, Files).
+
 find_top_dir(Dir) ->
     case file:read_file_info(filename:join([Dir, ebin])) of
        {ok, #file_info{type = directory}} ->
@@ -165,29 +175,19 @@ setup_ejabberd_lib_path(Config) ->
 %% Read environment variable CT_DB=riak,mysql to limit the backends to test.
 %% You can thus limit the backend you want to test with:
 %%  CT_BACKENDS=riak,mysql rebar ct suites=ejabberd
-get_config_backends(Types) ->
+get_config_backends() ->
     EnvBackends = case os:getenv("CT_BACKENDS") of
-                     false  -> Types;
+                     false  -> ?BACKENDS;
                      String ->
                          Backends0 = string:tokens(String, ","),
-                         lists:map(fun(Backend) -> string:strip(Backend, both, $ ) end, Backends0)
+                         lists:map(
+                           fun(Backend) ->
+                                   list_to_atom(string:strip(Backend, both, $ ))
+                           end, Backends0)
                  end,
     application:load(ejabberd),
-    EnabledBackends = lists:map(fun(V) when is_atom(V) ->
-                                       atom_to_list(V);
-                                  (V) ->
-                                       V
-                               end,
-                              application:get_env(ejabberd, enabled_backends, Types)),
-    lists:foldl(fun(Backend, Backends) ->
-                       case lists:member(Backend, EnabledBackends) of
-                           false ->
-                               lists:delete(Backend, Backends);
-                           _ ->
-                               Backends
-                       end
-               end, EnvBackends, ["odbc", "mysql", "pgsql",
-                                  "sqlite", "riak", "redis"]).
+    EnabledBackends = application:get_env(ejabberd, enabled_backends, EnvBackends),
+    misc:intersection(EnvBackends, [mnesia, ldap, extauth|EnabledBackends]).
 
 process_config_tpl(Content, []) ->
     Content;
@@ -489,6 +489,8 @@ wait_auth_SASL_result(Config, ShouldFail) ->
                              set_opt(csi, true, ConfigAcc);
                         (#rosterver_feature{}, ConfigAcc) ->
                              set_opt(rosterver, true, ConfigAcc);
+                        (#compression{methods = Ms}, ConfigAcc) ->
+                             set_opt(compression, Ms, ConfigAcc);
                         (_, ConfigAcc) ->
                              ConfigAcc
                      end, Config2, Fs)
index b48932848c086de5b2cd09be9f0030f303b3725b..2f2abc6620441d10d7a42183e02442262bb67e7c 100644 (file)
 -define(S2S_VHOST, <<"s2s.localhost">>).
 -define(UPLOAD_VHOST, <<"upload.localhost">>).
 
+-define(BACKENDS, [mnesia, redis, mysql, pgsql, sqlite, ldap, extauth, riak]).
+
 insert(Val, N, Tuple) ->
     L = tuple_to_list(Tuple),
     {H, T} = lists:split(N-1, L),
index c3d69cb74e554dc23be08b97895fa8e5b198b180..4dca7f0784868e2e102de8619d51d47ecb58a27e 100755 (executable)
@@ -10,7 +10,7 @@
                module :: module(),
                file :: filename:filename()}).
 
-main([Dir]) ->
+main(Paths) ->
     State =
        fold_beams(
          fun(File0, Tree, Acc0) ->
@@ -49,11 +49,11 @@ main([Dir]) ->
                                    Acc
                            end
                    end, Acc1, Tree)
-         end, #state{}, Dir),
+         end, #state{}, Paths),
     report_orphaned_funs(State),
     RunDeps = build_deps(State#state.run_hooks, State#state.hooked_funs),
     RunFoldDeps = build_deps(State#state.run_fold_hooks, State#state.hooked_funs),
-    emit_module(RunDeps, RunFoldDeps, State#state.specs, Dir, hooks_type_test).
+    emit_module(RunDeps, RunFoldDeps, State#state.specs, hooks_type_test).
 
 analyze_run_hook(Form, State) ->
     [Hook|Tail] = erl_syntax:application_arguments(Form),
@@ -245,11 +245,16 @@ integer_value(Form, State) ->
            0
     end.
 
-emit_module(RunDeps, RunFoldDeps, Specs, _Dir, Module) ->
+emit_module(RunDeps, RunFoldDeps, Specs, Module) ->
     File = filename:join(["src", Module]) ++ ".erl",
     try
        {ok, Fd} = file:open(File, [write]),
-       write(Fd, "-module(~s).~n~n", [Module]),
+       write(Fd,
+             "%% Generated automatically~n"
+             "%% DO NOT EDIT: run `make hooks` instead~n~n", []),
+       write(Fd, "-module(~s).~n", [Module]),
+       write(Fd, "-compile(nowarn_unused_vars).~n", []),
+       write(Fd, "-dialyzer(no_return).~n~n", []),
        emit_export(Fd, RunDeps, "run hooks"),
        emit_export(Fd, RunFoldDeps, "run_fold hooks"),
        emit_run_hooks(Fd, RunDeps, Specs),
@@ -263,20 +268,17 @@ emit_module(RunDeps, RunFoldDeps, Specs, _Dir, Module) ->
 emit_run_hooks(Fd, Deps, Specs) ->
     DepsList = lists:sort(dict:to_list(Deps)),
     lists:foreach(
-      fun({{Hook, Arity, {File, LineNo}}, []}) ->
-             Args = lists:duplicate(Arity, "_"),
-             write(Fd, "%% called at ~s:~p~n", [File, LineNo]),
-             write(Fd, "~s(~s) -> ok.~n~n", [Hook, string:join(Args, ", ")]);
-        ({{Hook, Arity, {File, LineNo}}, Funs}) ->
+      fun({{Hook, Arity, {File, LineNo}}, Funs}) ->
              emit_specs(Fd, Funs, Specs),
              write(Fd, "%% called at ~s:~p~n", [File, LineNo]),
              Args = string:join(
                       [[N] || N <- lists:sublist(lists:seq($A, $Z), Arity)],
                       ", "),
              write(Fd, "~s(~s) ->~n    ", [Hook, Args]),
-             Calls = [io_lib:format("~s:~s(~s)", [Mod, Fun, Args])
+             Calls = [io_lib:format("_ = ~s:~s(~s)", [Mod, Fun, Args])
                       || {{Mod, Fun, _}, _Seq, _} <- lists:keysort(2, Funs)],
-             write(Fd, "~s.~n~n", [string:join(Calls, ",\n    ")])
+             write(Fd, "~s.~n~n",
+                   [string:join(Calls ++ ["ok"], ",\n    ")])
       end, DepsList).
 
 emit_run_fold_hooks(Fd, Deps, Specs) ->
@@ -332,16 +334,38 @@ emit_specs(Fd, Funs, Specs) ->
              end
       end, lists:keysort(2, Funs)).
 
-fold_beams(Fun, State, Dir) ->
-    filelib:fold_files(
-      Dir, ".+\.beam\$", false,
-      fun(File, Acc) ->
-             AbsCode = get_code_from_beam(File),
-             lists:foldl(
-               fun(Form, Acc1) ->
-                       Fun(File, Form, Acc1)
-               end, Acc, AbsCode)
-      end, State).
+fold_beams(Fun, State, Paths) ->
+    Paths1 = fold_paths(Paths),
+    Total = length(Paths1),
+    {_, State1} =
+       lists:foldl(
+         fun(File, {I, Acc}) ->
+                 io:format("Progress: ~B% (~B/~B)\r",
+                           [round(I*100/Total), I, Total]),
+                 AbsCode = get_code_from_beam(File),
+                 Acc2 = lists:foldl(
+                          fun(Form, Acc1) ->
+                                  Fun(File, Form, Acc1)
+                          end, Acc, AbsCode),
+                 {I+1, Acc2}
+         end, {0, State}, Paths1),
+    State1.
+
+fold_paths(Paths) ->
+    lists:flatmap(
+      fun(Path) ->
+             case filelib:is_dir(Path) of
+                 true ->
+                     lists:reverse(
+                       filelib:fold_files(
+                         Path, ".+\.beam\$", false,
+                         fun(File, Acc) ->
+                                 [File|Acc]
+                         end, []));
+                 false ->
+                     [Path]
+             end
+      end, Paths).
 
 get_code_from_beam(File) ->
     try
diff --git a/tools/opt_types.sh b/tools/opt_types.sh
new file mode 100755 (executable)
index 0000000..17c229b
--- /dev/null
@@ -0,0 +1,597 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+%%! -pa ebin
+
+-compile([nowarn_unused_function]).
+-record(state, {g_opts = #{} :: map(),
+               m_opts = #{} :: map(),
+               globals = [] :: [atom()],
+               defaults = #{} :: map(),
+               mod_defaults = #{} :: map(),
+               specs = #{} :: map(),
+               mod_specs = #{} :: map()}).
+
+main(Paths) ->
+    State = fold_beams(
+             fun(File, Form, StateAcc) ->
+                     append(Form, File, StateAcc)
+             end, #state{}, Paths),
+    emit_modules(map_to_specs(State#state.m_opts,
+                             State#state.mod_defaults,
+                             State#state.mod_specs)),
+    emit_config(map_to_specs(State#state.g_opts,
+                            State#state.defaults,
+                            State#state.specs),
+               State#state.globals).
+
+emit_config(Specs, Globals) ->
+    Mod = "ejabberd_option",
+    File = filename:join("src", Mod ++ ".erl"),
+    case file:open(File, [write]) of
+       {ok, Fd} ->
+           emit_header(Fd, Mod, Specs, Globals),
+           emit_funs(Fd, Mod, Specs, Globals);
+       {error, Reason} ->
+           err("Failed to open file ~s for writing: ~s",
+               [File, file:format_error(Reason)])
+    end.
+
+emit_modules(Specs) ->
+    M = lists:foldl(
+         fun({{Mod, Opt}, Spec}, Acc) ->
+                 Opts = maps:get(Mod, Acc, []),
+                 Opts1 = [{Opt, Spec}|Opts],
+                 maps:put(Mod, Opts1, Acc)
+         end, #{}, Specs),
+    maps:fold(
+      fun(Mod, OptSpecs, _) ->
+             ModS = atom_to_list(Mod) ++ "_opt",
+             File = filename:join("src", ModS ++ ".erl"),
+             case file:open(File, [write]) of
+                 {ok, Fd} ->
+                     OptSpecs1 = lists:reverse(OptSpecs),
+                     emit_header(Fd, ModS, OptSpecs1),
+                     emit_funs(Fd, Mod, OptSpecs1);
+                 {error, Reason} ->
+                     err("Failed to open file ~s for writing: ~s",
+                         [File, file:format_error(Reason)])
+             end
+      end, ok, M).
+
+emit_header(Fd, Mod, Specs, Globals) ->
+    log(Fd, comment(), []),
+    log(Fd, "-module(~s).~n", [Mod]),
+    lists:foreach(
+      fun({{_, Opt}, _}) ->
+             case lists:member(Opt, Globals) of
+                 true ->
+                     log(Fd, "-export([~s/0]).", [Opt]);
+                 false ->
+                     log(Fd, "-export([~s/0, ~s/1]).", [Opt, Opt])
+             end
+      end, Specs),
+    log(Fd, "", []).
+
+emit_header(Fd, Mod, Specs) ->
+    log(Fd, comment(), []),
+    log(Fd, "-module(~s).~n", [Mod]),
+    lists:foreach(
+      fun({Opt, _}) ->
+             log(Fd, "-export([~s/1]).", [Opt])
+      end, Specs),
+    log(Fd, "", []).
+
+emit_funs(Fd, _Mod, Specs, Globals) ->
+    lists:foreach(
+      fun({{_, Opt}, Type}) ->
+             SType = t_to_string(Type),
+             case lists:member(Opt, Globals) of
+                 true ->
+                     log(Fd,
+                         "-spec ~s() -> ~s.~n"
+                         "~s() ->~n"
+                         "    ejabberd_config:get_option({~s, global}).~n",
+                         [Opt, SType, Opt, Opt]);
+                 false ->
+                     log(Fd,
+                         "-spec ~s() -> ~s.~n"
+                         "~s() ->~n"
+                         "    ~s(global).~n"
+                         "-spec ~s(global | binary()) -> ~s.~n"
+                         "~s(Host) ->~n"
+                         "    ejabberd_config:get_option({~s, Host}).~n",
+                         [Opt, SType, Opt, Opt, Opt, SType, Opt, Opt])
+             end
+      end, Specs).
+
+emit_funs(Fd, Mod, Specs) ->
+    lists:foreach(
+      fun({Opt, Type}) ->
+             log(Fd,
+                 "-spec ~s(gen_mod:opts() | global | binary()) -> ~s.~n"
+                 "~s(Opts) when is_map(Opts) ->~n"
+                 "    gen_mod:get_opt(~s, Opts);~n"
+                 "~s(Host) ->~n"
+                 "    gen_mod:get_module_opt(Host, ~s, ~s).~n",
+                 [Opt, t_to_string(Type), Opt, Opt, Opt, Mod, Opt])
+      end, Specs).
+
+append({globals, Form}, _File, State) ->
+    [Clause] = erl_syntax:function_clauses(Form),
+    Body = lists:last(erl_syntax:clause_body(Clause)),
+    Gs = lists:map(fun erl_syntax:atom_value/1,
+                  erl_syntax:list_elements(Body)),
+    Globals = State#state.globals ++ Gs,
+    State#state{globals = Globals};
+append({Index, Form}, File, State) when Index == #state.defaults;
+                                       Index == #state.mod_defaults ->
+    Mod = module(File),
+    [Clause] = erl_syntax:function_clauses(Form),
+    Body = lists:last(erl_syntax:clause_body(Clause)),
+    case erl_syntax:is_proper_list(Body) of
+       true ->
+           Opts = lists:foldl(
+                    fun(E, M) ->
+                            try
+                                [E1, E2|_] = erl_syntax:tuple_elements(E),
+                                Name = erl_syntax:atom_value(E1),
+                                Val = erl_syntax:concrete(E2),
+                                maps:put({Mod, Name}, Val, M)
+                            catch _:_ ->
+                                    M
+                            end
+                    end, element(Index, State), erl_syntax:list_elements(Body)),
+           setelement(Index, State, Opts);
+       false ->
+           warn("~s: improper list", [format_file(File, Body)]),
+           State
+    end;
+append({Index, Form}, File, State) when Index == #state.specs;
+                                       Index == #state.mod_specs ->
+    Specs = element(Index, State),
+    Mod = module(File),
+    try
+       {type, _, 'fun', Form1} = Form,
+       {type, _, list, Form2} = lists:last(Form1),
+       Tuples = case Form2 of
+                    [{type, _, union, Form3}] -> Form3;
+                    _ -> Form2
+                end,
+       Specs1 = lists:foldl(
+                  fun({type, _, tuple, [{atom, _, Atom}, Form5]}, Acc) ->
+                          maps:put({Mod, Atom}, Form5, Acc);
+                     (_, Acc) ->
+                          Acc
+                  end, Specs, Tuples),
+       setelement(Index, State, Specs1)
+    catch _:_ ->
+           warn("~s: unsupported type spec", [format_file(File, Form)]),
+           State
+    end;
+append({Type, Form}, File, State) when Type == opt_type; Type == mod_opt_type ->
+    Clauses = erl_syntax:function_clauses(Form),
+    Mod = module(File),
+    lists:foldl(
+      fun(Clause, StateAcc) ->
+              [Arg] = erl_syntax:clause_patterns(Clause),
+             Body = lists:last(erl_syntax:clause_body(Clause)),
+              case erl_syntax:type(Arg) of
+                  atom ->
+                      Name = erl_syntax:atom_value(Arg),
+                     case Type of
+                         opt_type ->
+                             GOpts = StateAcc#state.g_opts,
+                             State#state{
+                               g_opts = append_body({Mod, Name}, Body, GOpts)};
+                         mod_opt_type ->
+                             MOpts = StateAcc#state.m_opts,
+                             State#state{
+                               m_opts = append_body({Mod, Name}, Body, MOpts)}
+                     end;
+                 T ->
+                     warn("~s: unexpected option name: ~s",
+                          [format_file(File, Arg), T]),
+                     StateAcc
+              end
+      end, State, Clauses).
+
+append_body(Name, Body, Map) ->
+    maps:put(Name, Body, Map).
+
+map_to_specs(Map, Defaults, Specs) ->
+    lists:keysort(
+      1, maps:fold(
+          fun({Mod, Opt} = Key, Val, Acc) ->
+                  S1 = type_with_default(Key, Val, Defaults),
+                  S2 = case t_is_any(S1) of
+                           true ->
+                               try maps:get(Key, Specs)
+                               catch _:{badkey, _} ->
+                                       warn("Cannot derive type for ~s->~s", [Mod, Opt]),
+                                       S1
+                               end;
+                           false ->
+                               S1
+                       end,
+                  [{Key, S2}|Acc]
+          end, [], Map)).
+
+type_with_default({Mod, _} = Key, Val, Defaults) ->
+    S = try spec(Mod, Val)
+       catch throw:unknown -> erl_types:t_any()
+       end,
+    case t_is_any(S) of
+       true ->
+           S;
+       false ->
+           try maps:get(Key, Defaults) of
+               T ->
+                   erl_types:t_sup(
+                     [S, erl_types:t_from_term(T)])
+           catch _:{badkey, _} ->
+                   S
+           end
+    end.
+
+spec(Mod, Form) ->
+    case erl_syntax:type(Form) of
+       application ->
+           case erl_syntax_lib:analyze_application(Form) of
+               {M, {Fun, Arity}} when M == econf;
+                                      M == yconf ->
+                   Args = erl_syntax:application_arguments(Form),
+                   spec(Fun, Arity, Args, Mod);
+               _ ->
+                   t_unknown(Mod)
+           end;
+       _ ->
+           t_unknown(Mod)
+    end.
+
+spec(pos_int, 0, _, _) ->
+    erl_types:t_pos_integer();
+spec(pos_int, 1, [Inf], _) ->
+    erl_types:t_sup(
+      erl_types:t_pos_integer(),
+      erl_types:t_atom(erl_syntax:atom_value(Inf)));
+spec(non_neg_int, 0, _, _) ->
+    erl_types:t_non_neg_integer();
+spec(non_neg_int, 1, [Inf], _) ->
+    erl_types:t_sup(
+      erl_types:t_non_neg_integer(),
+      erl_types:t_atom(erl_syntax:atom_value(Inf)));
+spec(int, 0, _, _) ->
+    erl_types:t_integer();
+spec(int, 2, [Min, Max], _) ->
+    erl_types:t_from_range(
+      erl_syntax:integer_value(Min),
+      erl_syntax:integer_value(Max));
+spec(number, 1, _, _) ->
+    erl_types:t_number();
+spec(octal, 0, _, _) ->
+    erl_types:t_non_neg_integer();
+spec(binary, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_binary();
+spec(enum, 1, [L], _) ->
+    try
+       Els = erl_syntax:list_elements(L),
+       Atoms = lists:map(
+                 fun(A) ->
+                         erl_types:t_atom(
+                           erl_syntax:atom_value(A))
+                 end, Els),
+       erl_types:t_sup(Atoms)
+    catch _:_ ->
+           erl_types:t_binary()
+    end;
+spec(bool, 0, _, _) ->
+    erl_types:t_boolean();
+spec(atom, 0, _, _) ->
+    erl_types:t_atom();
+spec(string, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_string();
+spec(any, 0, _, Mod) ->
+    t_unknown(Mod);
+spec(url, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_binary();
+spec(file, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_binary();
+spec(directory, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_binary();
+spec(ip, 0, _, _) ->
+    t_remote(inet, ip_address);
+spec(ipv4, 0, _, _) ->
+    t_remote(inet, ip4_address);
+spec(ipv6, 0, _, _) ->
+    t_remote(inet, ip6_address);
+spec(ip_mask, 0, _, _) ->
+    erl_types:t_sup(
+      erl_types:t_tuple(
+       [t_remote(inet, ip4_address), erl_types:t_from_range(0, 32)]),
+      erl_types:t_tuple(
+       [t_remote(inet, ip6_address), erl_types:t_from_range(0, 128)]));
+spec(port, 0, _, _) ->
+    erl_types:t_from_range(1, 65535);
+spec(re, 0, _, _) ->
+    t_remote(re, mp);
+spec(glob, 0, _, _) ->
+    t_remote(re, mp);
+spec(path, 0, _, _) ->
+    erl_types:t_binary();
+spec(binary_sep, 1, _, _) ->
+    erl_types:t_list(erl_types:t_binary());
+spec(beam, A, _, _) when A == 0; A == 1 ->
+    erl_types:t_module();
+spec(timeout, 1, _, _) ->
+    erl_types:t_pos_integer();
+spec(timeout, 2, [_, Inf], _) ->
+    erl_types:t_sup(
+      erl_types:t_pos_integer(),
+      erl_types:t_atom(erl_syntax:atom_value(Inf)));
+spec(non_empty, 1, [Form], Mod) ->
+    S = spec(Mod, Form),
+    case erl_types:t_is_list(S) of
+       true ->
+           erl_types:t_nonempty_list(
+             erl_types:t_list_elements(S));
+       false ->
+           S
+    end;
+spec(unique, 1, [Form], Mod) ->
+    spec(Mod, Form);
+spec(acl, 0, _, _) ->
+    t_remote(acl, acl);
+spec(shaper, 0, _, _) ->
+    erl_types:t_sup(
+      [erl_types:t_atom(),
+       erl_types:t_list(t_remote(ejabberd_shaper, shaper_rule))]);
+spec(url_or_file, 0, _, _) ->
+    erl_types:t_tuple(
+      [erl_types:t_sup([erl_types:t_atom(file),
+                       erl_types:t_atom(url)]),
+       erl_types:t_binary()]);
+spec(lang, 0, _, _) ->
+    erl_types:t_binary();
+spec(pem, 0, _, _) ->
+    erl_types:t_binary();
+spec(jid, 0, _, _) ->
+    t_remote(jid, jid);
+spec(domain, 0, _, _) ->
+    erl_types:t_binary();
+spec(db_type, 1, _, _) ->
+    erl_types:t_atom();
+spec(queue_type, 0, _, _) ->
+    erl_types:t_sup([erl_types:t_atom(ram),
+                    erl_types:t_atom(file)]);
+spec(ldap_filter, 0, _, _) ->
+    erl_types:t_binary();
+spec(sip_uri, 0, _, _) ->
+    t_remote(esip, uri);
+spec(Fun, A, [Form|_], Mod) when (A == 1 orelse A == 2) andalso
+                                (Fun == list orelse Fun == list_or_single) ->
+    erl_types:t_list(spec(Mod, Form));
+spec(map, A, [F1, F2|OForm], Mod) when A == 2; A == 3 ->
+    T1 = spec(Mod, F1),
+    T2 = spec(Mod, F2),
+    case options_return_type(OForm) of
+       map ->
+           erl_types:t_map([], T1, T2);
+       dict ->
+           t_remote(dict, dict);
+       _ ->
+           erl_types:t_list(erl_types:t_tuple([T1, T2]))
+    end;
+spec(either, 2, [F1, F2], Mod) ->
+    Spec1 = case erl_syntax:type(F1) of
+               atom -> erl_types:t_atom(erl_syntax:atom_value(F1));
+               _ -> spec(Mod, F1)
+           end,
+    Spec2 = spec(Mod, F2),
+    erl_types:t_sup([Spec1, Spec2]);
+spec(and_then, 2, [_, F], Mod) ->
+    spec(Mod, F);
+spec(well_known, 2, [Form, _], Mod) ->
+    case erl_syntax:atom_value(Form) of
+       queue_type -> spec(queue_type, 0, [], Mod);
+       db_type -> erl_types:t_atom();
+       ram_db_type -> erl_types:t_atom();
+       cache_life_time -> spec(pos_int, 1, [erl_syntax:atom(infinity)], Mod);
+       cache_size -> spec(pos_int, 1, [erl_syntax:atom(infinity)], Mod);
+       use_cache -> spec(bool, 0, [], Mod);
+       cache_missed -> spec(bool, 0, [], Mod);
+       host -> erl_types:t_binary();
+       hosts -> erl_types:t_list(erl_types:t_binary())
+    end;
+spec(options, A, [Form|OForm], Mod) when A == 1; A == 2 ->
+    case erl_syntax:type(Form) of
+       map_expr ->
+           Fs = erl_syntax:map_expr_fields(Form),
+           Required = options_required(OForm),
+           {Els, {DefK, DefV}} =
+               lists:mapfoldl(
+                 fun(F, Acc) ->
+                         Name = erl_syntax:map_field_assoc_name(F),
+                         Val = erl_syntax:map_field_assoc_value(F),
+                         OptType = spec(Mod, Val),
+                         case erl_syntax:atom_value(Name) of
+                             '_' ->
+                                 {[], {erl_types:t_atom(), OptType}};
+                             Atom ->
+                                 Mand = case lists:member(Atom, Required) of
+                                            true -> mandatory;
+                                            false -> optional
+                                        end,
+                                 {[{erl_types:t_atom(Atom), Mand, OptType}], Acc}
+                         end
+                 end, {erl_types:t_none(), erl_types:t_none()}, Fs),
+           case options_return_type(OForm) of
+               map ->
+                   erl_types:t_map(lists:flatten(Els), DefK, DefV);
+               dict ->
+                   t_remote(dict, dict);
+               _ ->
+                   erl_types:t_list(
+                     erl_types:t_sup(
+                       [erl_types:t_tuple([DefK, DefV])|
+                        lists:map(
+                          fun({K, _, V}) ->
+                                  erl_types:t_tuple([K, V])
+                          end, lists:flatten(Els))]))
+           end;
+       _ ->
+           t_unknown(Mod)
+    end;
+spec(_, _, _, Mod) ->
+    t_unknown(Mod).
+
+t_from_form(Spec) ->
+    {T, _} = erl_types:t_from_form(
+               Spec, sets:new(), {type, {mod, foo, 1}}, dict:new(),
+               erl_types:var_table__new(), erl_types:cache__new()),
+    T.
+
+t_remote(Mod, Type) ->
+    D = maps:from_list([{{opaque, Type, []},
+                         {{Mod, 1, 2, []}, type}}]),
+    [T] = erl_types:t_opaque_from_records(D),
+    T.
+
+t_unknown(_Mod) ->
+    throw(unknown).
+
+t_is_any(T) ->
+    T == erl_types:t_any().
+
+t_to_string(T) ->
+    case erl_types:is_erl_type(T) of
+       true -> erl_types:t_to_string(T);
+       false -> erl_types:t_form_to_string(T)
+    end.
+
+options_return_type([]) ->
+    list;
+options_return_type([Form]) ->
+    Opts = erl_syntax:concrete(Form),
+    proplists:get_value(return, Opts, list).
+
+options_required([]) ->
+    [];
+options_required([Form]) ->
+    Opts = erl_syntax:concrete(Form),
+    proplists:get_value(required, Opts, []).
+
+format_file(Path, Form) ->
+    filename:rootname(filename:basename(Path)) ++ ".erl:" ++
+       integer_to_list(erl_syntax:get_pos(Form)).
+
+module(Path) ->
+    list_to_atom(filename:rootname(filename:basename(Path))).
+
+fold_beams(Fun, State, Paths) ->
+    Paths1 = fold_paths(Paths),
+    Total = length(Paths1),
+    {_, State1} =
+       lists:foldl(
+         fun(File, {I, Acc}) ->
+                 io:format("Progress: ~B% (~B/~B)\r",
+                           [round(I*100/Total), I, Total]),
+                 AbsCode = get_code_from_beam(File),
+                 Acc2 = case is_behaviour(AbsCode, ejabberd_config) of
+                            true ->
+                                fold_opt(File, Fun, Acc, AbsCode);
+                            false ->
+                                fold_mod_opt(File, Fun, Acc, AbsCode)
+                        end,
+                 {I+1, Acc2}
+         end, {0, State}, Paths1),
+    State1.
+
+fold_opt(File, Fun, Acc, AbsCode) ->
+    lists:foldl(
+      fun(Form, Acc1) ->
+             case erl_syntax_lib:analyze_form(Form) of
+                 {function, {opt_type, 1}} ->
+                     Fun(File, {opt_type, Form}, Acc1);
+                 {function, {globals, 0}} ->
+                     Fun(File, {globals, Form}, Acc1);
+                 {function, {options, 0}} ->
+                     Fun(File, {#state.defaults, Form}, Acc1);
+                 {attribute, {spec, {spec, {{options, 0}, Spec}}}} ->
+                     Fun(File, {#state.specs, hd(Spec)}, Acc1);
+                 _ ->
+                     Acc1
+             end
+      end, Acc, AbsCode).
+
+fold_mod_opt(File, Fun, Acc, AbsCode) ->
+    lists:foldl(
+      fun(Form, Acc1) ->
+             case erl_syntax_lib:analyze_form(Form) of
+                 {function, {mod_opt_type, 1}} ->
+                     Fun(File, {mod_opt_type, Form}, Acc1);
+                 {function, {mod_options, 1}} ->
+                     Fun(File, {#state.mod_defaults, Form}, Acc1);
+                 {attribute, {spec, {spec, {{mod_options, 1}, Spec}}}} ->
+                     Fun(File, {#state.mod_specs, hd(Spec)}, Acc1);
+                 _ ->
+                     Acc1
+             end
+      end, Acc, AbsCode).
+
+fold_paths(Paths) ->
+    lists:flatmap(
+      fun(Path) ->
+             case filelib:is_dir(Path) of
+                 true ->
+                     lists:reverse(
+                       filelib:fold_files(
+                         Path, ".+\.beam\$", false,
+                         fun(File, Acc) ->
+                                 [File|Acc]
+                         end, []));
+                 false ->
+                     [Path]
+             end
+      end, Paths).
+
+is_behaviour(AbsCode, Mod) ->
+    lists:any(
+      fun(Form) ->
+             case erl_syntax_lib:analyze_form(Form) of
+                 {attribute, {Attr, {_, Mod}}}
+                   when Attr == behaviour orelse Attr == behavior ->
+                     true;
+                 _ ->
+                     false
+             end
+      end, AbsCode).
+
+get_code_from_beam(File) ->
+    try
+        {ok, {_, List}} = beam_lib:chunks(File, [abstract_code]),
+        {_, {raw_abstract_v1, Forms}} = lists:keyfind(abstract_code, 1, List),
+        Forms
+    catch _:{badmatch, _} ->
+            err("no abstract code found in ~s", [File])
+    end.
+
+comment() ->
+    "%% Generated automatically~n"
+    "%% DO NOT EDIT: run `make options` instead~n".
+
+log(Format, Args) ->
+    log(standard_io, Format, Args).
+
+log(Fd, Format, Args) ->
+    case io:format(Fd, Format ++ "~n", Args) of
+       ok -> ok;
+       {error, Reason} ->
+           err("Failed to write to file: ~s", [file:format_error(Reason)])
+    end.
+
+warn(Format, Args) ->
+    io:format(standard_error, "Warning: " ++ Format ++ "~n", Args).
+
+err(Format, Args) ->
+    io:format(standard_error, "Error: " ++ Format ++ "~n", Args),
+    halt(1).
index f19bcfdbdffefebbb05df3f98a3651c5e5695d6e..21b06a0bcde678eaba3bd8dee1637878a1537725 100644 (file)
@@ -33,7 +33,7 @@
 -record(attr_stats, {count = 0, vals = #{}}).
 
 archive_analyze(Host, Table, EHost) ->
-    case ejabberd_sql:sql_query(Host, <<"select username, peer, kind, xml from ", Table/binary>>) of
+    case ejabberd_sql:sql_query(Host, [<<"select username, peer, kind, xml from ", Table/binary>>]) of
        {selected, _, Res} ->
            lists:foldl(
                fun([U, P, K, X], Stats) ->
@@ -76,7 +76,7 @@ gen_code(File, Rules, Ver) when Ver < 64 ->
                end, Id + 1, Text),
            {lists:keystore(Ns, 1, Acc, {Ns, NsC ++ [{El, encode_id(Id), AttrsE, TextE}]}), Id5}
        end, {[], 5}, Rules),
-    {ok, Dev} = file:open(File, write),
+    {ok, Dev} = file:open(File, [write]),
     Mod = filename:basename(File, ".erl"),
     io:format(Dev, "-module(~s).~n-export([encode/3, decode/3]).~n~n", [Mod]),
     RulesS = iolist_to_binary(io_lib:format("~p", [Rules])),