Full Code of processone/ejabberd for AI

master 181465ea3e87 cached
592 files
7.5 MB
2.0M tokens
780 symbols
1 requests
Download .txt
Showing preview only (7,971K chars total). Download the full file or copy to clipboard to get everything.
Repository: processone/ejabberd
Branch: master
Commit: 181465ea3e87
Files: 592
Total size: 7.5 MB

Directory structure:
gitextract_hsr6yf71/

├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   └── prepare-container.sh
├── .dockerignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE
│   ├── actions/
│   │   ├── manage-database/
│   │   │   └── action.yml
│   │   └── manage-ejabberd/
│   │       └── action.yml
│   ├── container/
│   │   ├── Dockerfile
│   │   ├── ejabberd-container-install.bat
│   │   ├── ejabberd.yml.example
│   │   └── ejabberdctl.template
│   ├── lock.yml
│   └── workflows/
│       ├── ci.yml
│       ├── container.yml
│       ├── installers.yml
│       ├── runtime.yml
│       └── weekly.yml
├── .gitignore
├── .shellcheckrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── relive.sh
│   └── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── COMPILE.md
├── CONTAINER.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── COPYING
├── Makefile.in
├── README.md
├── SECURITY.md
├── _checkouts/
│   └── configure_deps/
│       ├── rebar.config
│       └── src/
│           ├── configure_deps.app.src
│           ├── configure_deps.erl
│           └── configure_deps_prv.erl
├── autogen.sh
├── config/
│   ├── ejabberd.exs
│   └── runtime.exs
├── configure.ac
├── configure.bat
├── cover.spec
├── ejabberd.doap
├── ejabberd.init.template
├── ejabberd.service.template
├── ejabberd.yml.example
├── ejabberdctl.cfg.example
├── ejabberdctl.template
├── elvis.config
├── erlang_ls.config
├── include/
│   ├── ELDAPv3.hrl
│   ├── bosh.hrl
│   ├── ejabberd_auth.hrl
│   ├── ejabberd_commands.hrl
│   ├── ejabberd_ctl.hrl
│   ├── ejabberd_db_serialize.hrl
│   ├── ejabberd_http.hrl
│   ├── ejabberd_oauth.hrl
│   ├── ejabberd_router.hrl
│   ├── ejabberd_sm.hrl
│   ├── ejabberd_sql.hrl
│   ├── ejabberd_sql_pt.hrl
│   ├── ejabberd_web_admin.hrl
│   ├── eldap.hrl
│   ├── http_bind.hrl
│   ├── logger.hrl
│   ├── mod_announce.hrl
│   ├── mod_antispam.hrl
│   ├── mod_caps.hrl
│   ├── mod_invites.hrl
│   ├── mod_last.hrl
│   ├── mod_mam.hrl
│   ├── mod_matrix_gw.hrl
│   ├── mod_muc.hrl
│   ├── mod_muc_room.hrl
│   ├── mod_offline.hrl
│   ├── mod_privacy.hrl
│   ├── mod_private.hrl
│   ├── mod_proxy65.hrl
│   ├── mod_push.hrl
│   ├── mod_roster.hrl
│   ├── mod_shared_roster.hrl
│   ├── mod_vcard.hrl
│   ├── mqtt.hrl
│   ├── pubsub.hrl
│   └── translate.hrl
├── inetrc
├── install-sh
├── lib/
│   ├── ejabberd/
│   │   ├── config/
│   │   │   ├── attr.ex
│   │   │   ├── config.ex
│   │   │   ├── ejabberd_hook.ex
│   │   │   ├── ejabberd_module.ex
│   │   │   ├── logger/
│   │   │   │   └── ejabberd_logger.ex
│   │   │   ├── opts_formatter.ex
│   │   │   ├── store.ex
│   │   │   └── validator/
│   │   │       ├── validation.ex
│   │   │       ├── validator_attrs.ex
│   │   │       ├── validator_dependencies.ex
│   │   │       └── validator_utility.ex
│   │   ├── config_util.ex
│   │   ├── hooks.ex
│   │   └── logger.ex
│   ├── ejabberd_auth_example.ex
│   ├── mix/
│   │   └── tasks/
│   │       └── deps.tree.ex
│   └── mod_example.ex
├── m4/
│   ├── ax_lib_sqlite3.m4
│   └── erlang-extra.m4
├── man/
│   └── ejabberd.yml.5
├── mix.exs
├── package.json
├── plugins/
│   ├── configure_deps.erl
│   ├── deps_erl_opts.erl
│   ├── override_deps_versions2.erl
│   └── override_opts.erl
├── priv/
│   ├── css/
│   │   ├── admin.css
│   │   ├── bosh.css
│   │   ├── muc.css
│   │   ├── oauth.css
│   │   └── register.css
│   ├── js/
│   │   ├── admin.js
│   │   └── muc.js
│   ├── lua/
│   │   └── redis_sm.lua
│   ├── mod_invites/
│   │   ├── apps.html
│   │   ├── apps.json
│   │   ├── base.html
│   │   ├── base_min.html
│   │   ├── client.html
│   │   ├── copyright
│   │   ├── invite.html
│   │   ├── invite_invalid.html
│   │   ├── register.html
│   │   ├── register_error.html
│   │   ├── register_success.html
│   │   ├── roster.html
│   │   └── static/
│   │       ├── invite.css
│   │       └── invite.js
│   └── msgs/
│       ├── ar.msg
│       ├── bg.msg
│       ├── ca.msg
│       ├── cs.msg
│       ├── de.msg
│       ├── el.msg
│       ├── eo.msg
│       ├── es.msg
│       ├── fr.msg
│       ├── gl.msg
│       ├── he.msg
│       ├── hu.msg
│       ├── id.msg
│       ├── it.msg
│       ├── ja.msg
│       ├── nl.msg
│       ├── no.msg
│       ├── pl.msg
│       ├── pt-br.msg
│       ├── pt.msg
│       ├── ru.msg
│       ├── sk.msg
│       ├── sq.msg
│       ├── sv.msg
│       ├── ta.msg
│       ├── th.msg
│       ├── tr.msg
│       ├── uk.msg
│       ├── vi.msg
│       ├── wa.msg
│       └── zh.msg
├── rebar
├── rebar.config
├── rebar.config.script
├── rebar3
├── rel/
│   ├── files/
│   │   ├── erl
│   │   └── install_upgrade.escript
│   ├── relive.config
│   ├── relive.escript
│   ├── reltool.config.script
│   ├── setup-dev.sh
│   ├── setup-relive.sh
│   ├── sys.config
│   └── vm.args
├── sql/
│   ├── lite.new.sql
│   ├── lite.sql
│   ├── mssql.new.sql
│   ├── mssql.sql
│   ├── mysql.new.sql
│   ├── mysql.old-to-new.sql
│   ├── mysql.sql
│   ├── pg.new.sql
│   └── pg.sql
├── src/
│   ├── ELDAPv3.asn1db
│   ├── ELDAPv3.erl
│   ├── acl.erl
│   ├── econf.erl
│   ├── ejabberd.app.src.script
│   ├── ejabberd.erl
│   ├── ejabberd_access_permissions.erl
│   ├── ejabberd_acme.erl
│   ├── ejabberd_admin.erl
│   ├── ejabberd_app.erl
│   ├── ejabberd_auth.erl
│   ├── ejabberd_auth_anonymous.erl
│   ├── ejabberd_auth_external.erl
│   ├── ejabberd_auth_jwt.erl
│   ├── ejabberd_auth_ldap.erl
│   ├── ejabberd_auth_mnesia.erl
│   ├── ejabberd_auth_pam.erl
│   ├── ejabberd_auth_sql.erl
│   ├── ejabberd_backend_sup.erl
│   ├── ejabberd_batch.erl
│   ├── ejabberd_bosh.erl
│   ├── ejabberd_c2s.erl
│   ├── ejabberd_c2s_config.erl
│   ├── ejabberd_captcha.erl
│   ├── ejabberd_cluster.erl
│   ├── ejabberd_cluster_mnesia.erl
│   ├── ejabberd_commands.erl
│   ├── ejabberd_commands_doc.erl
│   ├── ejabberd_config.erl
│   ├── ejabberd_config_transformer.erl
│   ├── ejabberd_ctl.erl
│   ├── ejabberd_db_serialize.erl
│   ├── ejabberd_db_sup.erl
│   ├── ejabberd_doc.erl
│   ├── ejabberd_hooks.erl
│   ├── ejabberd_http.erl
│   ├── ejabberd_http_ws.erl
│   ├── ejabberd_iq.erl
│   ├── ejabberd_listener.erl
│   ├── ejabberd_local.erl
│   ├── ejabberd_logger.erl
│   ├── ejabberd_mnesia.erl
│   ├── ejabberd_oauth.erl
│   ├── ejabberd_oauth_mnesia.erl
│   ├── ejabberd_oauth_rest.erl
│   ├── ejabberd_oauth_sql.erl
│   ├── ejabberd_old_config.erl
│   ├── ejabberd_option.erl
│   ├── ejabberd_options.erl
│   ├── ejabberd_options_doc.erl
│   ├── ejabberd_piefxis.erl
│   ├── ejabberd_pkix.erl
│   ├── ejabberd_redis.erl
│   ├── ejabberd_redis_sup.erl
│   ├── ejabberd_regexp.erl
│   ├── ejabberd_router.erl
│   ├── ejabberd_router_mnesia.erl
│   ├── ejabberd_router_multicast.erl
│   ├── ejabberd_router_redis.erl
│   ├── ejabberd_router_sql.erl
│   ├── ejabberd_s2s.erl
│   ├── ejabberd_s2s_in.erl
│   ├── ejabberd_s2s_out.erl
│   ├── ejabberd_service.erl
│   ├── ejabberd_shaper.erl
│   ├── ejabberd_sip.erl
│   ├── ejabberd_sm.erl
│   ├── ejabberd_sm_mnesia.erl
│   ├── ejabberd_sm_redis.erl
│   ├── ejabberd_sm_sql.erl
│   ├── ejabberd_sql.erl
│   ├── ejabberd_sql_pt.erl
│   ├── ejabberd_sql_schema.erl
│   ├── ejabberd_sql_sup.erl
│   ├── ejabberd_stun.erl
│   ├── ejabberd_sup.erl
│   ├── ejabberd_system_monitor.erl
│   ├── ejabberd_systemd.erl
│   ├── ejabberd_tmp_sup.erl
│   ├── ejabberd_update.erl
│   ├── ejabberd_web.erl
│   ├── ejabberd_web_admin.erl
│   ├── ejabberd_websocket.erl
│   ├── ejabberd_websocket_codec.erl
│   ├── ejabberd_xmlrpc.erl
│   ├── ejd2sql.erl
│   ├── eldap.erl
│   ├── eldap_filter.erl
│   ├── eldap_filter_yecc.yrl
│   ├── eldap_pool.erl
│   ├── eldap_utils.erl
│   ├── ext_mod.erl
│   ├── extauth.erl
│   ├── extauth_sup.erl
│   ├── gen_iq_handler.erl
│   ├── gen_mod.erl
│   ├── gen_pubsub_node.erl
│   ├── gen_pubsub_nodetree.erl
│   ├── jd2ejd.erl
│   ├── misc.erl
│   ├── mod_adhoc.erl
│   ├── mod_adhoc_api.erl
│   ├── mod_adhoc_api_opt.erl
│   ├── mod_adhoc_opt.erl
│   ├── mod_admin_extra.erl
│   ├── mod_admin_update_sql.erl
│   ├── mod_announce.erl
│   ├── mod_announce_mnesia.erl
│   ├── mod_announce_opt.erl
│   ├── mod_announce_sql.erl
│   ├── mod_antispam.erl
│   ├── mod_antispam_dump.erl
│   ├── mod_antispam_files.erl
│   ├── mod_antispam_filter.erl
│   ├── mod_antispam_opt.erl
│   ├── mod_antispam_rtbl.erl
│   ├── mod_auth_fast.erl
│   ├── mod_auth_fast_mnesia.erl
│   ├── mod_auth_fast_opt.erl
│   ├── mod_avatar.erl
│   ├── mod_avatar_opt.erl
│   ├── mod_block_strangers.erl
│   ├── mod_block_strangers_opt.erl
│   ├── mod_blocking.erl
│   ├── mod_bosh.erl
│   ├── mod_bosh_mnesia.erl
│   ├── mod_bosh_opt.erl
│   ├── mod_bosh_redis.erl
│   ├── mod_bosh_sql.erl
│   ├── mod_caps.erl
│   ├── mod_caps_mnesia.erl
│   ├── mod_caps_opt.erl
│   ├── mod_caps_sql.erl
│   ├── mod_carboncopy.erl
│   ├── mod_client_state.erl
│   ├── mod_client_state_opt.erl
│   ├── mod_configure.erl
│   ├── mod_configure_opt.erl
│   ├── mod_conversejs.erl
│   ├── mod_conversejs_opt.erl
│   ├── mod_delegation.erl
│   ├── mod_delegation_opt.erl
│   ├── mod_disco.erl
│   ├── mod_disco_opt.erl
│   ├── mod_fail2ban.erl
│   ├── mod_fail2ban_opt.erl
│   ├── mod_host_meta.erl
│   ├── mod_host_meta_opt.erl
│   ├── mod_http_api.erl
│   ├── mod_http_api_opt.erl
│   ├── mod_http_fileserver.erl
│   ├── mod_http_fileserver_opt.erl
│   ├── mod_http_upload.erl
│   ├── mod_http_upload_opt.erl
│   ├── mod_http_upload_quota.erl
│   ├── mod_http_upload_quota_opt.erl
│   ├── mod_invites.erl
│   ├── mod_invites_http.erl
│   ├── mod_invites_http_erlylib.erl
│   ├── mod_invites_mnesia.erl
│   ├── mod_invites_opt.erl
│   ├── mod_invites_register.erl
│   ├── mod_invites_sql.erl
│   ├── mod_jidprep.erl
│   ├── mod_jidprep_opt.erl
│   ├── mod_last.erl
│   ├── mod_last_mnesia.erl
│   ├── mod_last_opt.erl
│   ├── mod_last_sql.erl
│   ├── mod_legacy_auth.erl
│   ├── mod_mam.erl
│   ├── mod_mam_mnesia.erl
│   ├── mod_mam_opt.erl
│   ├── mod_mam_sql.erl
│   ├── mod_matrix_gw.erl
│   ├── mod_matrix_gw_opt.erl
│   ├── mod_matrix_gw_room.erl
│   ├── mod_matrix_gw_s2s.erl
│   ├── mod_matrix_gw_sup.erl
│   ├── mod_metrics.erl
│   ├── mod_metrics_opt.erl
│   ├── mod_mix.erl
│   ├── mod_mix_mnesia.erl
│   ├── mod_mix_opt.erl
│   ├── mod_mix_pam.erl
│   ├── mod_mix_pam_mnesia.erl
│   ├── mod_mix_pam_opt.erl
│   ├── mod_mix_pam_sql.erl
│   ├── mod_mix_sql.erl
│   ├── mod_mqtt.erl
│   ├── mod_mqtt_bridge.erl
│   ├── mod_mqtt_bridge_opt.erl
│   ├── mod_mqtt_bridge_session.erl
│   ├── mod_mqtt_mnesia.erl
│   ├── mod_mqtt_opt.erl
│   ├── mod_mqtt_session.erl
│   ├── mod_mqtt_sql.erl
│   ├── mod_mqtt_ws.erl
│   ├── mod_muc.erl
│   ├── mod_muc_admin.erl
│   ├── mod_muc_admin_opt.erl
│   ├── mod_muc_log.erl
│   ├── mod_muc_log_opt.erl
│   ├── mod_muc_mnesia.erl
│   ├── mod_muc_opt.erl
│   ├── mod_muc_room.erl
│   ├── mod_muc_rtbl.erl
│   ├── mod_muc_rtbl_opt.erl
│   ├── mod_muc_sql.erl
│   ├── mod_muc_sup.erl
│   ├── mod_multicast.erl
│   ├── mod_multicast_opt.erl
│   ├── mod_offline.erl
│   ├── mod_offline_mnesia.erl
│   ├── mod_offline_opt.erl
│   ├── mod_offline_sql.erl
│   ├── mod_ping.erl
│   ├── mod_ping_opt.erl
│   ├── mod_pres_counter.erl
│   ├── mod_pres_counter_opt.erl
│   ├── mod_privacy.erl
│   ├── mod_privacy_mnesia.erl
│   ├── mod_privacy_opt.erl
│   ├── mod_privacy_sql.erl
│   ├── mod_private.erl
│   ├── mod_private_mnesia.erl
│   ├── mod_private_opt.erl
│   ├── mod_private_sql.erl
│   ├── mod_privilege.erl
│   ├── mod_privilege_opt.erl
│   ├── mod_providers.erl
│   ├── mod_providers_opt.erl
│   ├── mod_proxy65.erl
│   ├── mod_proxy65_lib.erl
│   ├── mod_proxy65_mnesia.erl
│   ├── mod_proxy65_opt.erl
│   ├── mod_proxy65_redis.erl
│   ├── mod_proxy65_service.erl
│   ├── mod_proxy65_sql.erl
│   ├── mod_proxy65_stream.erl
│   ├── mod_pubsub.erl
│   ├── mod_pubsub_mnesia.erl
│   ├── mod_pubsub_opt.erl
│   ├── mod_pubsub_serverinfo.erl
│   ├── mod_pubsub_serverinfo_opt.erl
│   ├── mod_pubsub_sql.erl
│   ├── mod_push.erl
│   ├── mod_push_keepalive.erl
│   ├── mod_push_keepalive_opt.erl
│   ├── mod_push_mnesia.erl
│   ├── mod_push_opt.erl
│   ├── mod_push_sql.erl
│   ├── mod_register.erl
│   ├── mod_register_opt.erl
│   ├── mod_register_web.erl
│   ├── mod_roster.erl
│   ├── mod_roster_mnesia.erl
│   ├── mod_roster_opt.erl
│   ├── mod_roster_sql.erl
│   ├── mod_s2s_bidi.erl
│   ├── mod_s2s_dialback.erl
│   ├── mod_s2s_dialback_opt.erl
│   ├── mod_scram_upgrade.erl
│   ├── mod_scram_upgrade_opt.erl
│   ├── mod_service_log.erl
│   ├── mod_service_log_opt.erl
│   ├── mod_shared_roster.erl
│   ├── mod_shared_roster_ldap.erl
│   ├── mod_shared_roster_ldap_opt.erl
│   ├── mod_shared_roster_mnesia.erl
│   ├── mod_shared_roster_opt.erl
│   ├── mod_shared_roster_sql.erl
│   ├── mod_sic.erl
│   ├── mod_sip.erl
│   ├── mod_sip_opt.erl
│   ├── mod_sip_proxy.erl
│   ├── mod_sip_registrar.erl
│   ├── mod_stats.erl
│   ├── mod_stream_mgmt.erl
│   ├── mod_stream_mgmt_opt.erl
│   ├── mod_stun_disco.erl
│   ├── mod_stun_disco_opt.erl
│   ├── mod_time.erl
│   ├── mod_vcard.erl
│   ├── mod_vcard_ldap.erl
│   ├── mod_vcard_ldap_opt.erl
│   ├── mod_vcard_mnesia.erl
│   ├── mod_vcard_mnesia_opt.erl
│   ├── mod_vcard_opt.erl
│   ├── mod_vcard_sql.erl
│   ├── mod_vcard_xupdate.erl
│   ├── mod_vcard_xupdate_opt.erl
│   ├── mod_version.erl
│   ├── mod_version_opt.erl
│   ├── mqtt_codec.erl
│   ├── node_flat.erl
│   ├── node_flat_sql.erl
│   ├── node_pep.erl
│   ├── node_pep_sql.erl
│   ├── nodetree_tree.erl
│   ├── nodetree_tree_sql.erl
│   ├── nodetree_virtual.erl
│   ├── prosody2ejabberd.erl
│   ├── proxy_protocol.erl
│   ├── pubsub_db_sql.erl
│   ├── pubsub_index.erl
│   ├── pubsub_migrate.erl
│   ├── pubsub_subscription.erl
│   ├── pubsub_subscription_sql.erl
│   ├── rest.erl
│   ├── str.erl
│   ├── translate.erl
│   ├── win32_dns.erl
│   └── xml_compress.erl
├── test/
│   ├── README
│   ├── announce_tests.erl
│   ├── antispam_tests.erl
│   ├── carbons_tests.erl
│   ├── commands_tests.erl
│   ├── configtest_tests.erl
│   ├── csi_tests.erl
│   ├── docker/
│   │   ├── README.md
│   │   ├── db/
│   │   │   └── mssql/
│   │   │       └── initdb/
│   │   │           └── initdb_mssql.sql
│   │   └── docker-compose.yml
│   ├── ejabberd_SUITE.erl
│   ├── ejabberd_SUITE_data/
│   │   ├── ca.key
│   │   ├── ca.pem
│   │   ├── cert.pem
│   │   ├── configtest.yml
│   │   ├── ejabberd.extauth.yml
│   │   ├── ejabberd.ldap.yml
│   │   ├── ejabberd.ldif
│   │   ├── ejabberd.mnesia.yml
│   │   ├── ejabberd.mssql.yml
│   │   ├── ejabberd.mysql.yml
│   │   ├── ejabberd.pgsql.yml
│   │   ├── ejabberd.redis.yml
│   │   ├── ejabberd.sqlite.yml
│   │   ├── ejabberd.yml
│   │   ├── extauth.py
│   │   ├── gencerts.sh
│   │   ├── macros.yml
│   │   ├── openssl.cnf
│   │   ├── self-signed-cert.pem
│   │   ├── spam_domains.txt
│   │   ├── spam_jids.txt
│   │   ├── spam_urls.txt
│   │   ├── sql_sort.pl
│   │   └── whitelist_domains.txt
│   ├── ejabberd_test_options.erl
│   ├── elixir-config/
│   │   ├── attr_test.exs
│   │   ├── config_test.exs
│   │   ├── ejabberd_logger.exs
│   │   ├── shared/
│   │   │   ├── ejabberd.exs
│   │   │   ├── ejabberd_different_from_default.exs
│   │   │   └── ejabberd_for_validation.exs
│   │   └── validation_test.exs
│   ├── example_tests.erl
│   ├── invites_tests.erl
│   ├── jidprep_tests.erl
│   ├── json_test.erl
│   ├── ldap_srv.erl
│   ├── mam_tests.erl
│   ├── mod_configtest.erl
│   ├── muc_tests.erl
│   ├── offline_tests.erl
│   ├── privacy_tests.erl
│   ├── private_tests.erl
│   ├── proxy65_tests.erl
│   ├── pubsub_tests.erl
│   ├── push_tests.erl
│   ├── replaced_tests.erl
│   ├── roster_tests.erl
│   ├── sm_tests.erl
│   ├── stundisco_tests.erl
│   ├── suite.erl
│   ├── suite.hrl
│   ├── upload_tests.erl
│   ├── vcard_tests.erl
│   └── webadmin_tests.erl
├── tools/
│   ├── captcha-ng.sh
│   ├── captcha.sh
│   ├── check_xep_versions.sh
│   ├── dl_invites_page_deps.sh
│   ├── ejabberdctl.bc
│   ├── emacs-indent.sh
│   ├── extract-erlydtl-templates.sh
│   ├── extract-tr.sh
│   ├── generate-doap.sh
│   ├── hook_deps.sh
│   ├── jhbtest.pl
│   ├── make-binaries
│   ├── make-installers
│   ├── make-packages
│   ├── opt_types.sh
│   ├── prepare-tr.sh
│   ├── rebar3-format.sh
│   ├── update-deps-releases.pl
│   └── xml_compress_gen.erl
└── vars.config.in

================================================
FILE CONTENTS
================================================

================================================
FILE: .devcontainer/Dockerfile
================================================
FROM ghcr.io/processone/devcontainer:latest


================================================
FILE: .devcontainer/devcontainer.json
================================================
{
	"name": "ejabberd",
	"build": {"dockerfile": "Dockerfile"},
	"extensions": ["erlang-ls.erlang-ls"],
	"postCreateCommand": ".devcontainer/prepare-container.sh",
	"remoteUser": "vscode"
}


================================================
FILE: .devcontainer/prepare-container.sh
================================================
echo "export PATH=/workspaces/ejabberd/_build/relive:$PATH" >>$HOME/.bashrc
echo "COOKIE" >$HOME/.erlang.cookie
chmod 400 $HOME/.erlang.cookie


================================================
FILE: .dockerignore
================================================
.git
.win32
.examples
*.swp
*~
\#*#
.#*
.edts
*.dump
/Makefile
/config.log
/config.status
/config/releases.exs
/configure
/aclocal.m4
/*.cache
/deps/
/.deps-update/
/ebin/
/ejabberd.init
/ejabberd.service
/ejabberdctl
/ejabberdctl.example
/rel/ejabberd/
/rel/overlays/
/src/eldap_filter_yecc.erl
/vars.config
/dialyzer/
/test/*.beam
/test/*.ctc
/logs/
/priv/bin/captcha*sh
/priv/sql
/rel/ejabberd
/_build
/database/
/.rebar
/rebar.lock
/log/
Mnesia.nonode@nohost/
# Binaries created with tools/make-{binaries,installers,packages}:
/ejabberd_*.deb
/ejabberd-*.rpm
/ejabberd-*.run
/ejabberd-*.tar.gz
/.github/container/Dockerfile


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
assignees: ''

---

Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better.

## Environment

- ejabberd version: 18.09
- Erlang version: `erl +V`
- OS: Linux (Debian)
- Installed from: source | distro package | official deb/rpm | official binary installer | other

## Configuration (only if needed): grep -Ev '^$|^\s*#' ejabberd.yml

```yaml
loglevel: 4
...
```

## Errors from error.log/crash.log

No errors

## Bug description

Please, give us a precise description (what does not work, what is expected, etc.)


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: Kind:Feature
assignees: ''

---

Before creating a ticket, please consider if this should fit the [discussion forum](https://github.com/processone/ejabberd/discussions) better.

**Is your feature request related to a problem? Please describe.**

A clear and concise description of what the problem is. Ex. I'm always frustrated when...

**Describe the solution you'd like**

A clear and concise description of what you want to happen.

**Describe alternatives you've considered**

A clear and concise description of any alternative solutions or features you've considered.

**Additional context**

Add any other context or screenshots about the feature request here.


================================================
FILE: .github/PULL_REQUEST_TEMPLATE
================================================
We are open to contributions for ejabberd, as GitHub pull requests (PR).
Here are a few points to consider before submitting your PR. (You can
remove the whole text after reading.)

1. Does this PR address an issue? Please reference it in the PR
   description.

2. Have you properly described the proposed change?

3. Please make sure the change is atomic and does only touch the needed
   modules. If you have other changes/fixes to provide, please submit
   them as separate PRs.

4. If your change or new feature involves storage backends, did you make
   sure your change works with all backends?

5. Do you provide tests? How can we check the behavior of the code?

6. Did you consider documentation changes in the
   processone/docs.ejabberd.im repository?


================================================
FILE: .github/actions/manage-database/action.yml
================================================
name: 'Manage Database'

inputs:
  for:
    default: ""
    description: 'One or more databases to manage:
                  mysql, pgsql, redis, mssql'

  do:
    default: ""
    description: 'One or more tasks to do:
                  install, start, user, create, drop, dump'

  dump-suffix:
    default: ""
    description: 'Suffix to append to the dump file name'

  mssql-schema:
    default: ""
    description: 'SQL schema for the MSSQL database:
                  singlehost or multihost'

runs:
  using: "composite"
  steps:

    ############################################################# Install #####

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'install')
      shell: sh
      run: |
        sudo sed -i 's/yes/no/g' /etc/initramfs-tools/update-initramfs.conf
        sudo rm -f /var/lib/man-db/auto-update || echo ok
        sudo apt-get -q update
        sudo apt-get -q -y install postgresql

    - if: contains(inputs.for, 'redis') && contains(inputs.do, 'install')
      shell: sh
      run: |
        sudo sed -i 's/yes/no/g' /etc/initramfs-tools/update-initramfs.conf
        sudo rm -f /var/lib/man-db/auto-update || echo ok
        sudo apt-get -q update
        sudo apt-get -q -y install redis-server

    - if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
      uses: awalsh128/cache-apt-pkgs-action@latest
      with:
        packages: tdsodbc

    - if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
      shell: sh
      run: |
        docker run -d -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=ejabberd_Test1" \
                   -v $(pwd)/test/docker/db/mssql/initdb/initdb_mssql.sql:/initdb_mssql.sql:ro \
                   -v $(pwd)/sql/mssql.sql:/mssql.sql:ro \
                   -v $(pwd)/sql/mssql.new.sql:/mssql.new.sql:ro \
                   -p 1433:1433 --name ejabberd-mssql \
                   "mcr.microsoft.com/mssql/server:2019-latest"
        sleep 10
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
               -C -U SA -P ejabberd_Test1 -S localhost -i /initdb_mssql.sql

    - if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
          && inputs.mssql-schema == 'singlehost'
      shell: sh
      run: |
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
               -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test \
               -i /mssql.sql

    - if: contains(inputs.for, 'mssql') && contains(inputs.do, 'install')
          && inputs.mssql-schema == 'multihost'
      shell: sh
      run: |
        docker exec ejabberd-mssql /opt/mssql-tools18/bin/sqlcmd \
               -C -U SA -P ejabberd_Test1 -S localhost -d ejabberd_test \
               -i /mssql.new.sql

    ############################################################### Start #####

    - if: contains(inputs.for, 'mysql') && contains(inputs.do, 'start')
      shell: sh
      run: |
        sudo systemctl start mysql.service

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'start')
      shell: sh
      run: |
        sudo systemctl start postgresql.service
        pg_isready

    ################################################################ User #####

    - if: contains(inputs.for, 'mysql') && contains(inputs.do, 'user')
      shell: sh
      run: |
        mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost'
                                 IDENTIFIED BY 'ejabberd_test';"

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'user')
      shell: sh
      run: |
        sudo -u postgres psql -c "CREATE USER ejabberd_test
                                  WITH PASSWORD 'ejabberd_test';"

    ################################################################ Dump #####

    - if: contains(inputs.for, 'mysql') && contains(inputs.do, 'dump')
      shell: sh
      run: |
        sudo mysqldump -u root -proot ejabberd_test \
             > mysql-${{ inputs.dump-suffix }}.sql

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'dump')
      shell: sh
      run: |
        sudo -u postgres pg_dump ejabberd_test \
             > pgsql-${{ inputs.dump-suffix }}.sql

    ################################################################ Drop #####

    - if: contains(inputs.for, 'mysql') && contains(inputs.do, 'drop')
      shell: sh
      run: |
        mysql -u root -proot -e "DROP DATABASE ejabberd_test;"

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'drop')
      shell: sh
      run: |
        sudo -u postgres psql -c "DROP DATABASE ejabberd_test;"

    ############################################################## Create #####

    - if: contains(inputs.for, 'mysql') && contains(inputs.do, 'create')
      shell: sh
      run: |
        mysql -u root -proot -e "CREATE DATABASE ejabberd_test;"
        mysql -u root -proot -e "GRANT ALL ON ejabberd_test.*
                                 TO 'ejabberd_test'@'localhost';"

    - if: contains(inputs.for, 'pgsql') && contains(inputs.do, 'create')
      shell: sh
      run: |
        sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;"
        sudo -u postgres psql -c "GRANT ALL PRIVILEGES
                                  ON DATABASE ejabberd_test TO ejabberd_test;"
        sudo -u postgres psql -c "GRANT ALL ON SCHEMA public TO ejabberd_test;"
        sudo -u postgres psql -c "ALTER DATABASE ejabberd_test
                                  OWNER TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                TABLES IN SCHEMA public
                                                TO ejabberd_test;"
        sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL
                                                SEQUENCES IN SCHEMA public
                                                TO ejabberd_test;"


================================================
FILE: .github/actions/manage-ejabberd/action.yml
================================================
name: 'Manage ejabberd'

inputs:
  for:
    default: ""
    description: 'Release method, one of:
                  prod, dev, install, run, deb'
  do:
    default: ""
    description: 'One or more tasks to perform:
                  deploy, no_acme, no_tls,
                  start, register, update_sql, stop,
                  check, logs'
  username:
    default: "user1"
    description: 'Username part of the account JID'
  host:
    default: "localhost"
    description: 'Host part of the account JID'
  tool:
    default: "rebar3"
    description: 'Build tool to use:
                  rebar or rebar3 (only relevant to make rebar2)'
  rel_name_vsn:
    default: ""
    description: 'Base name of installer files'
  configure:
    default: ""
    description: 'Options to append to ./configure'

runs:
  using: "composite"
  steps:

    - name: Path Definitions
      id: path
      shell: bash
      run: |
        case ${{ inputs.for }} in
          'prod')
             BASE="_build/prod/rel/ejabberd"
             echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
             echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
             echo "ectl=$BASE/bin/ejabberdctl" >> $GITHUB_OUTPUT
             ;;
          'dev')
             BASE="_build/dev/rel/ejabberd"
             echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
             echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
             echo "ectl=$BASE/bin/ejabberdctl" >> $GITHUB_OUTPUT
             ;;
          'install')
             BASE="/tmp/ejabberd"
             echo "conf=$BASE/etc/ejabberd" >> $GITHUB_OUTPUT
             echo "logs=$BASE/var/log/ejabberd" >> $GITHUB_OUTPUT
             echo "ectl=$BASE/sbin/ejabberdctl" >> $GITHUB_OUTPUT
             ;;
          'deb')
             BASE="/opt/ejabberd"
             echo "base=$BASE" >> $GITHUB_OUTPUT
             echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
             echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
             echo "ectl=sudo /opt/${{ inputs.rel_name_vsn }}/bin/ejabberdctl" >> $GITHUB_OUTPUT
             ;;
          'run')
             BASE="$HOME/opt/ejabberd"
             echo "base=$BASE" >> $GITHUB_OUTPUT
             echo "conf=$BASE/conf" >> $GITHUB_OUTPUT
             echo "logs=$BASE/logs" >> $GITHUB_OUTPUT
             echo "ectl=$HOME/opt/${{ inputs.rel_name_vsn }}/bin/ejabberdctl" >> $GITHUB_OUTPUT
             ;;
        esac

    ############################################################# Compile #####

    - if: contains(inputs.do, 'compile')
      shell: sh
      run: |
        TOOL=${{ inputs.tool }}
        [ "${TOOL%3}" = "rebar" ] && TOOL="./$TOOL"
        ./autogen.sh
        ./configure --with-rebar=$TOOL \
                    --prefix=/tmp/ejabberd \
                    --enable-all ${{ inputs.configure }}
        sed -i 's|, syntax_tools||g' src/ejabberd.app.src.script
        REBAR_PROFILE=test make

    ############################################################## Deploy #####

    - if: contains(inputs.do, 'deploy') &&
          inputs.for == 'prod' && inputs.tool == 'rebar'
      shell: sh
      run: |
        mkdir -p _build/prod && ln -s `pwd`/rel/ _build/prod/rel

    - if: contains(inputs.do, 'deploy') &&
          inputs.for == 'dev' && inputs.tool == 'rebar'
      shell: sh
      run: |
        mkdir -p _build/dev && ln -s `pwd`/rel/ _build/dev/rel

    - if: contains(inputs.do, 'deploy') &&
          (inputs.for == 'prod' ||
           inputs.for == 'dev' ||
           inputs.for == 'install')
      shell: sh
      run: |
        make ${{ inputs.for }}

    - if: contains(inputs.do, 'deploy') &&
          inputs.for == 'deb'
      shell: sh
      run: |
        sudo dpkg -i $(ls -1 *.deb)

    - if: contains(inputs.do, 'deploy') &&
          inputs.for == 'run'
      shell: sh
      run: |
        ./$(ls -1 *.run)

    ################################################################ ACME #####

    - if: contains(inputs.do, 'no_acme')
      shell: sh
      run: |
        sed -i 's/loglevel/acme:\n  auto: false\nloglevel/g' \
            ${{ steps.path.outputs.conf }}/ejabberd.yml

    ################################################################# TLS #####

    - if: contains(inputs.do, 'no_tls') &&
          inputs.for == 'dev'
      shell: sh
      run: |
        sed -i 's/starttls_required: true/starttls_required: false/g' \
            ${{ steps.path.outputs.conf }}/ejabberd.yml

    ############################################################### Start #####

    - if: contains(inputs.do, 'start')
      shell: sh
      run: |
        ${{ steps.path.outputs.ectl }} start
        ${{ steps.path.outputs.ectl }} started

    ############################################################ Register #####

    - if: contains(inputs.do, 'register')
      shell: sh
      run: |
        ${{ steps.path.outputs.ectl }} \
            register ${{ inputs.username }} ${{ inputs.host }} s0mePass
        ${{ steps.path.outputs.ectl }} \
            registered_users ${{ inputs.host }} >> registered.log
        grep -q '${{ inputs.username }}' registered.log

    ########################################################### UpdateSQL #####

    - if: contains(inputs.do, 'update_sql')
      shell: sh
      run: |
        ${{ steps.path.outputs.ectl }} \
            update_sql

    ################################################################ Stop #####

    - if: contains(inputs.do, 'stop')
      shell: sh
      run: |
        ${{ steps.path.outputs.ectl }} stop
        ${{ steps.path.outputs.ectl }} stopped

    ############################################################### Check #####

    - if: contains(inputs.do, 'check')
      shell: sh
      run: |
        grep -q 'is started' ${{ steps.path.outputs.logs }}/ejabberd.log
        grep -q 'is stopped' ${{ steps.path.outputs.logs }}/ejabberd.log
        test $(find ${{ steps.path.outputs.logs }}/ -empty -name error.log)

    ################################################################ Logs #####

    - if: contains(inputs.do, 'logs') || failure()
      shell: sh
      run: |
        SUDO=sudo
        [ "${{ inputs.for }}" = "deb" ] || SUDO=""
        echo "::group::View ejabberd.log"
        $SUDO cat ${{ steps.path.outputs.logs }}/ejabberd.log
        echo "::endgroup::"
        echo "::group::View error.log"
        $SUDO cat ${{ steps.path.outputs.logs }}/error.log
        echo "::endgroup::"


================================================
FILE: .github/container/Dockerfile
================================================
#' Define default build variables
ARG OTP_VSN='28.4.1.0'
ARG ELIXIR_VSN='1.19.5'
ARG UID='9000'
ARG USER='ejabberd'
ARG HOME="opt/$USER"
ARG BUILD_DIR="/$USER"
ARG VERSION='master'

################################################################################
#' Compile ejabberdapi
FROM docker.io/golang:1.25-alpine AS api
RUN go install -v \
    github.com/processone/ejabberd-api/cmd/ejabberd@master \
    && mv bin/ejabberd bin/ejabberdapi

################################################################################
#' build and install ejabberd directly from source
FROM docker.io/erlang:${OTP_VSN}-alpine AS ejabberd

RUN apk -U add --no-cache \
        ca-certificates \
        autoconf \
        automake \
        bash \
        build-base \
        curl \
        expat-dev \
        file \
        gd-dev \
        git \
        jpeg-dev \
        libpng-dev \
        libwebp-dev \
        linux-pam-dev \
        npm \
        openssl-dev \
        sqlite-dev \
        yaml-dev \
        zlib-dev

ARG ELIXIR_VSN
RUN wget -O - https://github.com/elixir-lang/elixir/archive/v$ELIXIR_VSN.tar.gz \
        | tar -xzf -

WORKDIR /elixir-$ELIXIR_VSN
ENV ERL_FLAGS="+JPperf true"
RUN make install clean

RUN mix local.hex --force \
    && mix local.rebar --force

ARG BUILD_DIR
COPY / $BUILD_DIR/

WORKDIR $BUILD_DIR

RUN mv .github/container/ejabberdctl.template . \
    && mv .github/container/ejabberd.yml.example . \
    && ./autogen.sh \
    && ./configure --with-rebar=mix --enable-all \
    && make deps \
    && make prod

WORKDIR /rootfs
ARG VERSION
ARG HOME
RUN mkdir -p $HOME $HOME-$VERSION \
    && cp -r $BUILD_DIR/_build/prod/rel/ejabberd/* $HOME-$VERSION \
    && mv $HOME-$VERSION/conf $HOME/conf

RUN cp -p $BUILD_DIR/tools/captcha*.sh $HOME-$VERSION/lib

RUN find "$HOME-$VERSION/bin" -name 'ejabberd' -delete \
    && find "$HOME-$VERSION/releases" -name 'COOKIE' -delete

RUN wget -O "$HOME/conf/cacert.pem" 'https://curl.se/ca/cacert.pem'

#' Prepare ejabberd for runtime
RUN apk -U add --no-cache \
        git \
        libcap \
        openssl

RUN mkdir -p usr/local/bin $HOME/conf $HOME/database $HOME/logs $HOME/upload

COPY --from=api /go/bin/ejabberdapi usr/local/bin/

RUN if [ ! -d $HOME/.ejabberd-modules ]; \
    then \
        if [ -d $BUILD_DIR/.ejabberd-modules ]; \
        then cp -r $BUILD_DIR/.ejabberd-modules $HOME; \
        else git clone https://github.com/processone/ejabberd-contrib --depth 1 \
                $HOME/.ejabberd-modules/sources/ejabberd-contrib; \
        fi \
    fi

RUN export PEM=$HOME/conf/server.pem \
    && openssl req -x509 \
            -batch \
            -nodes \
            -newkey rsa:4096 \
            -keyout $PEM \
            -out $PEM \
            -days 3650 \
            -subj "/CN=localhost"

RUN sed -i 's|^#CTL_OVER_HTTP=|CTL_OVER_HTTP=../|' "$HOME/conf/ejabberdctl.cfg"

RUN home_root_dir=$(echo $HOME | sed 's|\(.*\)/.*|\1 |') \
    && setcap 'cap_net_bind_service=+ep' $(find $home_root_dir -name beam.smp) \
    && echo -e \
        "#!/bin/sh \
        \n[ -z \$ERLANG_NODE_ARG ] && export ERLANG_NODE_ARG=ejabberd@localhost \
        \nexport EMA=\"\$EJABBERD_MACRO_ADMIN\" \
        \nexport HOST=\"\${EJABBERD_MACRO_HOST:-localhost}\" \
        \nif [ -n \"\$EMA\" ] \
        \nthen \
        \n    if [ \"\$EMA\" != \"\${EMA%%@*}\" ] \
        \n    then \
        \n        export USERNAME=\"\${EMA%%@*}\" \
        \n        export HOST=\"\${EMA##*@}\" \
        \n    else \
        \n        export USERNAME=\"\$EMA\" \
        \n        export SHOW_WARNING=\"true\" \
        \n    fi \
        \nelif [ -n \"\$REGISTER_ADMIN_PASSWORD\" ] \
        \nthen \
        \n    export USERNAME=\"admin\" \
        \nelse \
        \n    export USERNAME=\"\$(od -A n -N 8 -t x8 /dev/urandom)\" \
        \nfi \
        \nexport EJABBERD_MACRO_ADMIN=\"\$USERNAME@\$HOST\" \
        \n[ -n \"\$SHOW_WARNING\" ] && echo \"WARNING: The EJABBERD_MACRO_ADMIN environment variable was set to '\$EMA', but it should include the host... I'll overwrite it to become '\$EJABBERD_MACRO_ADMIN'.\" \
        \n[ -n \"\$CTL_ON_CREATE\" ] && export SEPARATOR=\";\" \
        \n[ -n \"\$REGISTER_ADMIN_PASSWORD\" ] && export CTL_ON_CREATE=\"register \${EJABBERD_MACRO_ADMIN%%@*} \${EJABBERD_MACRO_ADMIN##*@} \$REGISTER_ADMIN_PASSWORD \$SEPARATOR \$CTL_ON_CREATE\" \
        \nexport CONFIG_DIR=/$HOME/conf \
        \nexport LOGS_DIR=/$HOME/logs \
        \nexport SPOOL_DIR=/$HOME/database \
        \nexec /$(find $home_root_dir -name ejabberdctl) \"\$@\"" \
            > usr/local/bin/ejabberdctl \
    && chmod +x usr/local/bin/* \
    && scanelf --needed --nobanner --format '%n#p' --recursive $home_root_dir \
        | tr ',' '\n' \
        | sort -u \
        | awk 'system("[ -e $home_root_dir" $1 " ]") == 0 { next } { print "so:" $1 }' \
        | sed -e "s|so:libc.so|so:libc.musl-$(uname -m).so.1|" \
            > /tmp/runDeps

ARG UID
RUN chown -R $UID:$UID $HOME

RUN cp /rootfs/$HOME-$VERSION/lib/captcha*.sh usr/local/bin/
RUN mkdir $HOME/sql \
    && find /rootfs/$HOME-$VERSION/lib/ -name *.sql -exec cp {} $HOME/sql \; -exec cp {} $HOME/database \;

################################################################################
#' Remove erlang/OTP & rebar3
FROM docker.io/erlang:${OTP_VSN}-alpine AS runtime
RUN apk del .erlang-rundeps \
    && rm -f $(which rebar3) \
    && find /usr -type d -name 'erlang' -exec rm -rf {} + \
    && find /usr -type l -exec test ! -e {} \; -delete

#' Update alpine, finalize runtime environment
COPY --from=ejabberd /tmp/runDeps /tmp/runDeps
RUN apk -U upgrade --available --no-cache \
    && apk add --no-cache \
        $(cat /tmp/runDeps) \
        so:libcap.so.2 \
        so:libtdsodbc.so \
        curl \
        tini \
    && rm /tmp/runDeps

ARG USER
ARG UID
ARG HOME
RUN addgroup $USER -g $UID \
    && adduser -s /sbin/nologin -D -u $UID -h /$HOME -G $USER $USER

RUN ln -fs /usr/local/bin/ /opt/ejabberd/bin
RUN rm -rf /home \
    && ln -fs /opt /home

################################################################################
#' Build together production image
FROM scratch
ARG USER
ARG HOME

COPY --from=runtime / /
COPY --from=ejabberd /rootfs /

HEALTHCHECK \
    --interval=1m \
    --timeout=5s \
    --start-period=5s \
    --retries=10 \
    CMD ejabberdctl status

WORKDIR /$HOME
USER $USER
VOLUME ["/$HOME"]
EXPOSE 1880 1883 4369-4399 5210 5222 5269 5280 5443 5478 7777 50000-50099

ENTRYPOINT ["/sbin/tini","--","ejabberdctl"]
CMD ["foreground"]


================================================
FILE: .github/container/ejabberd-container-install.bat
================================================
@echo off

::
::   ejabberd container installer for Windows
::   -------------------------------------
::                                    v0.4
::
:: This batch script downloads an ejabberd container image
:: and setups a docker container to run ejabberd.

::
:: 1. Download and install Docker:
::
::    If you use Windows 10, download Docker Desktop from:
::      https://www.docker.com/
::
::    If you use Windows 7 or 8, download Docker Toolbox from:
::      https://github.com/docker/toolbox/releases
::    After installation, run Docker Quickstart Installer
::

::
:: 2. Edit those options:

:: Directory where your ejabberd deployment files will be installed
:: (configuration, database, logs, ...)
::
:: In Windows 10 you can configure the path:

set INSTALL_DIR_WINDOWS10=C:\ejabberd

:: In older Windows, not configurable, it will be installed in:
:: C:\Users\%USERNAME%\ejabberd

:: Please enter the desired ejabberd domain name.
:: The domain is the visible attribute that is added to the username
:: to form the Jabber Identifier (for example: user@example.net).
:: This computer must be known on the network with this address name.
:: You can later add more in conf/ejabberd.yml

set HOST=localhost

:: Please enter the administrator username for the current
:: ejabberd installation. A Jabber account with this username
:: will be created and granted administrative privileges.
:: Don't use blankspaces in the username.

set USER=admin

:: Please provide a password for that new administrator account

set PASSWORD=

:: By default this downloads 'latest' ejabberd version,
:: but you can set a specific version, for example '22.05'
:: or the bleeding edge 'master'. See available tags in
:: https://github.com/processone/ejabberd/pkgs/container/ejabberd

set VERSION=latest

:: This tells docker what ports ejabberd will use.
:: You can later configure them in conf/ejabberd.yml

set PORTS=5180 5222 5269 5443

::
:: 3. Now save this script and run it.
::

::
:: 4. When installation is completed:
::
:: If using Windows 10, open Docker Desktop and you can:
::
:: - (>) START the ejabberd container
:: - Enter WebAdmin: click the ([->]) OPEN IN BROWSER button
:: - To try ejabberdctl, click the (>_) CLI button, then: ejabberdctl
:: - ([]) STOP the ejabberd container
::
:: If using an old Windows, open Kitematic and you can:
::
:: - START the ejabberd container
:: - Open your configuration, logs, ... in Settings > Volumes
:: - Enter WebAdmin in Settings > Hostname/Ports > click on the 5180 port
:: - Try ejabberdctl in EXEC, then: ejabberdctl
:: - STOP the ejabberd container
::
:: You can delete the container and create it again running this script,
:: the configuration and database are maintained.
::

::===============================================================
:: Check Windows version
::
::===============================================================

set INSTALL_DIR_DOCKER=c/Users/%USERNAME%/ejabberd

for /f "tokens=4-5 delims=. " %%i in ('ver') do set WVERSION=%%i.%%j
if "%wversion%" == "10.0" (
  echo === Preparing paths to install in Windows 10...
  set INSTALL_DIR=%INSTALL_DIR_WINDOWS10%
  set VC=-v %INSTALL_DIR_WINDOWS10%\conf:/opt/ejabberd/conf
  set VD=-v %INSTALL_DIR_WINDOWS10%\database:/opt/ejabberd/database
  set VL=-v %INSTALL_DIR_WINDOWS10%\logs:/opt/ejabberd/logs
  set VM=-v %INSTALL_DIR_WINDOWS10%\ejabberd-modules:/opt/ejabberd/.ejabberd-modules
  set DOCKERDOWNLOAD="First download and install Docker Desktop from https://www.docker.com/"
) else (
  echo === Preparing paths to install in Windows older than 10...
  set INSTALL_DIR=C:\Users\%USERNAME%\ejabberd
  set VC=-v "/%INSTALL_DIR_DOCKER%/conf:/opt/ejabberd/conf"
  set VD=-v "/%INSTALL_DIR_DOCKER%/database:/opt/ejabberd/database"
  set VL=-v "/%INSTALL_DIR_DOCKER%/logs:/opt/ejabberd/logs"
  set VM=-v "/%INSTALL_DIR_DOCKER%/ejabberd-modules:/opt/ejabberd/.ejabberd-modules"
  set DOCKERDOWNLOAD="First download and install Docker Toolbox from https://github.com/docker/toolbox/releases"
)
set VOLUMES=%VC% %VD% %VL% %VM%

::===============================================================
:: Check docker is installed
::
::===============================================================

docker version >NUL
if %ERRORLEVEL% NEQ 0 (
  echo.
  echo === ERROR: It seems docker is not installed!!!
  echo.
  echo %DOCKERDOWNLOAD%
  echo === Then try to run this script again.
  echo.
  pause
  exit 1
)

::===============================================================
:: Check install options are correctly set
::
::===============================================================

if [%PASSWORD%]==[] (
  echo.
  echo === ERROR: PASSWORD not set!!!
  echo.
  echo === Please edit this script and set the PASSWORD.
  echo === Then try to run this script again.
  echo.
  pause
  exit 1
)

::===============================================================
:: Download Docker image
::
::===============================================================

set IMAGE=ghcr.io/processone/ejabberd:%VERSION%

echo.
echo === Checking if the '%IMAGE%' container image was already downloaded...
docker image history %IMAGE% >NUL
if %ERRORLEVEL% NEQ 0 (
  echo === The '%IMAGE%' container image was not downloaded yet.
  echo.
  echo === Downloading the '%IMAGE%' container image, please wait...
  docker pull %IMAGE%
) else (
  echo === The '%IMAGE%' container image was already downloaded.
)

::===============================================================
:: Create preliminary container
::
::===============================================================

echo.
echo === Checking if the 'ejabberd' container already exists...
docker container logs ejabberd
if %ERRORLEVEL% EQU 0 (
  echo.
  echo === The 'ejabberd' container already exists.
  echo === Nothing to do, so installation finishes now.
  echo === You can go to Docker Desktop and start the 'ejabberd' container.
  echo.
  pause
  exit 1
) else (
  echo === The 'ejabberd' container doesn't yet exist,
  echo === so let's continue the installation process.
)

echo.
if exist %INSTALL_DIR% (
  echo === The INSTALL_DIR %INSTALL_DIR% already exists.
  echo === No need to create the preliminary 'ejabberd-pre' image.
) else (
  echo === The INSTALL_DIR %INSTALL_DIR% doesn't exist.
  echo === Let's create the preliminary 'ejabberd-pre' image.
  CALL :create-ejabberd-pre
)

::===============================================================
:: Create final container
::
::===============================================================

echo.
echo === Creating the final 'ejabberd' container using %IMAGE% image...

setlocal EnableDelayedExpansion
set PS=
for %%a in (%PORTS%) do (
  set PS=!PS! -p %%a:%%a
)

docker create --name ejabberd --hostname localhost %PS% %VOLUMES% %IMAGE%

echo.
echo === Installation completed.
echo.
pause

EXIT /B %ERRORLEVEL%

::===============================================================
:: Function to create preliminary container
::
::===============================================================

:create-ejabberd-pre

echo.
echo === Creating a preliminary 'ejabberd-pre' container using %IMAGE% image...
docker create --name ejabberd-pre --hostname localhost %IMAGE%

echo.
echo === Now 'ejabberd-pre' will be started.
docker container start ejabberd-pre

echo.
echo === Waiting ejabberd to be running...
set /A timeout = 10
set status=4
goto :while

:statusstart
docker exec -it ejabberd-pre ejabberdctl status
goto :statusend

:while
if %status% GTR 0 (
   echo.
   timeout /t 1 /nobreak >NUL
   set /A timeout = timeout - 1
   if %timeout% EQU 0 (
      set status=-1
   ) else (
      goto :statusstart
      :statusend
      set status=%ERRORLEVEL%
   )
   goto :while
)

echo.
echo === Setting a few options...
docker exec -it ejabberd-pre sed -i "s!- localhost!- %HOST%!g" conf/ejabberd.yml
docker exec -it ejabberd-pre sed -i "s!^acl:!acl:\n  admin:\n    user:\n      - \"%USER%@%HOST%\"!g" conf/ejabberd.yml
docker exec -it ejabberd-pre sed -i "s!5280!5180!g" conf/ejabberd.yml
docker exec -it ejabberd-pre sed -i "s!/admin!/!g" conf/ejabberd.yml
docker exec -it ejabberd-pre ejabberdctl reload_config

echo.
echo === Registering the administrator account...
docker exec -it ejabberd-pre ejabberdctl register %USER% %HOST% %PASSWORD%
docker exec -it ejabberd-pre ejabberdctl stop

echo.
echo === Copying conf, database, logs...
mkdir %INSTALL_DIR%
mkdir %INSTALL_DIR%\conf
mkdir %INSTALL_DIR%\database
mkdir %INSTALL_DIR%\logs
mkdir %INSTALL_DIR%\ejabberd-modules
docker cp ejabberd-pre:/opt/ejabberd/conf/ %INSTALL_DIR%
docker cp ejabberd-pre:/opt/ejabberd/database/ %INSTALL_DIR%
docker cp ejabberd-pre:/opt/ejabberd/logs/ %INSTALL_DIR%

echo.
echo === Deleting the preliminary 'ejabberd-pre' container...
docker stop ejabberd-pre
docker rm ejabberd-pre

EXIT /B 0


================================================
FILE: .github/container/ejabberd.yml.example
================================================
###
###              ejabberd configuration file
###
### The parameters used in this configuration file are explained at
###
###       https://docs.ejabberd.im/admin/configuration
###
### The configuration file is written in YAML.
### *******************************************************
### *******           !!! WARNING !!!               *******
### *******     YAML IS INDENTATION SENSITIVE       *******
### ******* MAKE SURE YOU INDENT SECTIONS CORRECTLY *******
### *******************************************************
### Refer to http://en.wikipedia.org/wiki/YAML for the brief description.
###

define_macro:
  HOST: localhost
  ## ADMIN: ... # set by /usr/local/bin/ejabberdctl
  PORT_C2S: 5222
  PORT_C2S_TLS: 5223
  PORT_S2S: 5269
  PORT_HTTP_TLS: 5443
  PORT_HTTP: 5280
  PORT_BROWSER: 1880
  PORT_STUN: 5478
  PORT_TURN_MIN: 50000
  PORT_TURN_MAX: 50099
  PORT_MQTT: 1883
  PORT_PROXY65: 7777
  STARTTLS_REQUIRED: true

hosts:
  - HOST

loglevel: info

## If you already have certificates, list them here
# certfiles:
#  - /etc/letsencrypt/live/domain.tld/fullchain.pem
#  - /etc/letsencrypt/live/domain.tld/privkey.pem

ca_file: /opt/ejabberd/conf/cacert.pem
certfiles:
  - /opt/ejabberd/conf/server.pem

listen:
  -
    port: PORT_C2S
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    starttls_required: STARTTLS_REQUIRED
  -
    port: PORT_C2S_TLS
    ip: "::"
    module: ejabberd_c2s
    max_stanza_size: 262144
    shaper: c2s_shaper
    access: c2s
    tls: true
  -
    port: PORT_S2S
    ip: "::"
    module: ejabberd_s2s_in
    max_stanza_size: 524288
    shaper: s2s_shaper
  -
    port: PORT_HTTP_TLS
    ip: "::"
    module: ejabberd_http
    tls: true
    request_handlers:
      /admin: ejabberd_web_admin
      /api: mod_http_api
      /bosh: mod_bosh
      /captcha: ejabberd_captcha
      /upload: mod_http_upload
      /websocket: ejabberd_http_ws
  -
    port: PORT_HTTP
    ip: "::"
    module: ejabberd_http
    request_handlers:
      /admin: ejabberd_web_admin
      /.well-known/acme-challenge: ejabberd_acme
  -
    port: PORT_BROWSER
    ip: "::"
    module: ejabberd_http
    request_handlers:
      /: ejabberd_web_admin
  -
    port: "unix:../sockets/ctl_over_http.sock"
    module: ejabberd_http
    unix_socket:
      mode: '0600'
    request_handlers:
      /ctl: ejabberd_ctl
  -
    port: PORT_STUN
    ip: "::"
    transport: udp
    module: ejabberd_stun
    use_turn: true
    turn_min_port: PORT_TURN_MIN
    turn_max_port: PORT_TURN_MAX
    ## The server's public IPv4 address:
    # turn_ipv4_address: "203.0.113.3"
    ## The server's public IPv6 address:
    # turn_ipv6_address: "2001:db8::3"
  -
    port: PORT_MQTT
    ip: "::"
    module: mod_mqtt
    backlog: 1000

s2s_use_starttls: optional

acl:
  local:
    user_regexp: ""
  loopback:
    ip:
      - 127.0.0.0/8
      - ::1/128
  admin:
    user:
      - ADMIN

access_rules:
  local:
    allow: local
  c2s:
    deny: blocked
    allow: all
  announce:
    allow: admin
  configure:
    allow: admin
  muc_create:
    allow: local
  pubsub_createnode:
    allow: local
  trusted_network:
    allow: loopback

api_permissions:
  "console commands":
    from: ejabberd_ctl
    who: all
    what: "*"
  "webadmin commands":
    from: ejabberd_web_admin
    who: admin
    what: "*"
  "adhoc commands":
    from: mod_adhoc_api
    who: admin
    what: "*"
  "http access":
    from: mod_http_api
    who:
      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
    what:
      - status
      - connected_users_number

shaper:
  normal:
    rate: 3000
    burst_size: 20000
  fast: 100000

shaper_rules:
  max_user_sessions: 10
  max_user_offline_messages:
    5000: admin
    100: all
  c2s_shaper:
    none: admin
    normal: all
  s2s_shaper: fast

modules:
  mod_adhoc: {}
  mod_adhoc_api: {}
  mod_admin_extra: {}
  mod_announce:
    access: announce
  mod_avatar: {}
  mod_blocking: {}
  mod_bosh: {}
  mod_caps: {}
  mod_carboncopy: {}
  mod_client_state: {}
  mod_configure: {}
  mod_disco: {}
  mod_fail2ban: {}
  mod_http_api: {}
  mod_http_upload:
    put_url: https://@HOST_URL_ENCODE@:5443/upload
    custom_headers:
      "Access-Control-Allow-Origin": "https://@HOST@"
      "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
      "Access-Control-Allow-Headers": "Content-Type"
  mod_last: {}
  mod_mam:
    ## Mnesia is limited to 2GB, better to use an SQL backend
    ## For small servers SQLite is a good fit and is very easy
    ## to configure. Uncomment this when you have SQL configured:
    ## db_type: sql
    assume_mam_usage: true
    default: always
  mod_mqtt: {}
  mod_muc:
    access:
      - allow
    access_admin:
      - allow: admin
    access_create: muc_create
    access_persistent: muc_create
    access_mam:
      - allow
    default_room_options:
      mam: true
  mod_muc_admin: {}
  mod_offline:
    access_max_user_messages: max_user_offline_messages
  mod_ping: {}
  mod_privacy: {}
  mod_private: {}
  mod_proxy65:
    access: local
    max_connections: 5
    port: PORT_PROXY65
  mod_pubsub:
    access_createnode: pubsub_createnode
    plugins:
      - flat
      - pep
    force_node_config:
      ## Avoid buggy clients to make their bookmarks public
      storage:bookmarks:
        access_model: whitelist
  mod_push: {}
  mod_push_keepalive: {}
  mod_register:
    ## Only accept registration requests from the "trusted"
    ## network (see access_rules section above).
    ## Think twice before enabling registration from any
    ## address. See the Jabber SPAM Manifesto for details:
    ## https://github.com/ge0rg/jabber-spam-fighting-manifesto
    ip_access: trusted_network
  mod_roster:
    versioning: true
  mod_s2s_bidi: {}
  mod_s2s_dialback: {}
  mod_shared_roster: {}
  mod_stream_mgmt:
    resend_on_timeout: if_offline
  mod_stun_disco: {}
  mod_vcard: {}
  mod_vcard_xupdate: {}
  mod_version:
    show_os: false

### Local Variables:
### mode: yaml
### End:
### vim: set filetype=yaml tabstop=8


================================================
FILE: .github/container/ejabberdctl.template
================================================
#!/bin/sh

# define default configuration
POLL=true
ERL_MAX_PORTS=32000
ERL_PROCESSES=250000
ERL_MAX_ETS_TABLES=1400
FIREWALL_WINDOW=""
INET_DIST_INTERFACE=""
ERLANG_NODE=ejabberd@localhost

# define default environment variables
[ -z "$SCRIPT" ] && SCRIPT=$0
SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)"
# shellcheck disable=SC2034
ERTS_VSN="{{erts_vsn}}"
ERL="{{erl}}"
EPMD="{{epmd}}"
IEX="{{iexpath}}"
COOKIE_FILE="$HOME"/.erlang.cookie
[ -n "$ERLANG_COOKIE" ] && [ ! -f "$COOKIE_FILE" ] && echo "$ERLANG_COOKIE" > "$COOKIE_FILE" && chmod 400 "$COOKIE_FILE"

# check the proper system user is used
case $(id -un) in
    "$INSTALLUSER")
        EXEC_CMD="as_current_user"
        ;;
    root)
        if [ -n "$INSTALLUSER" ] ; then
            EXEC_CMD="as_install_user"
        else
            EXEC_CMD="as_current_user"
            echo "WARNING: It is not recommended to run ejabberd as root" >&2
        fi
        ;;
    *)
        if [ -n "$INSTALLUSER" ] ; then
            echo "ERROR: This command can only be run by root or the user $INSTALLUSER" >&2
            exit 7
        else
            EXEC_CMD="as_current_user"
        fi
        ;;
esac

# parse command line parameters
while [ $# -gt 0 ]; do
    case $1 in
        -n|--node) ERLANG_NODE_ARG=$2; shift 2;;
        -s|--spool) SPOOL_DIR=$2; shift 2;;
        -l|--logs) LOGS_DIR=$2; shift 2;;
        -f|--config) EJABBERD_CONFIG_PATH=$2; shift 2;;
        -c|--ctl-config) EJABBERDCTL_CONFIG_PATH=$2; shift 2;;
        -d|--config-dir) CONFIG_DIR=$2; shift 2;;
        -t|--no-timeout) NO_TIMEOUT="--no-timeout"; shift;;
        *) break;;
    esac
done

# define ejabberd variables if not already defined from the command line
: "${CONFIG_DIR:="{{config_dir}}"}"
: "${LOGS_DIR:="{{logs_dir}}"}"
: "${EJABBERD_CONFIG_PATH:="$CONFIG_DIR/ejabberd.yml"}"
: "${EJABBERDCTL_CONFIG_PATH:="$CONFIG_DIR/ejabberdctl.cfg"}"
# Allows passing extra Erlang command-line arguments in vm.args file
: "${VMARGS:="$CONFIG_DIR/vm.args"}"
# shellcheck source=ejabberdctl.cfg.example
[ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH"
[ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG"
[ "$ERLANG_NODE" = "${ERLANG_NODE%@*}" ] && ERLANG_NODE="$ERLANG_NODE@$(hostname -s)"
[ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s"
: "${SPOOL_DIR:="{{spool_dir}}"}"
: "${EJABBERD_LOG_PATH:="$LOGS_DIR/ejabberd.log"}"

# backward support for old mnesia spool dir path
: "${SPOOL_DIR_OLD:="$SPOOL_DIR/$ERLANG_NODE"}"
[ -r "$SPOOL_DIR_OLD/schema.DAT" ] && [ ! -r "$SPOOL_DIR/schema.DAT" ] && SPOOL_DIR="$SPOOL_DIR_OLD"

# define erl parameters
ERLANG_OPTS="+K $POLL +P $ERL_PROCESSES $ERL_OPTIONS"
if [ -n "$FIREWALL_WINDOW" ] ; then
    ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_listen_min ${FIREWALL_WINDOW%-*} inet_dist_listen_max ${FIREWALL_WINDOW#*-}"
fi
if [ -n "$INET_DIST_INTERFACE" ] ; then
    INET_DIST_INTERFACE2=$("$ERL" $ERLANG_OPTS -noshell -eval 'case inet:parse_address("'$INET_DIST_INTERFACE'") of {ok,IP} -> io:format("~p",[IP]); _ -> ok end.' -s erlang halt)
    if [ -n "$INET_DIST_INTERFACE2" ] ; then
        if [ "$(echo "$INET_DIST_INTERFACE2" | grep -o "," | wc -l)" -eq 7 ] ; then
            INET_DIST_INTERFACE2="$INET_DIST_INTERFACE2 -proto_dist inet6_tcp"
        fi
        ERLANG_OPTS="$ERLANG_OPTS -kernel inet_dist_use_interface $INET_DIST_INTERFACE2"
    fi
fi
[ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -erl_epmd_port $ERL_DIST_PORT -start_epmd false"
# if vm.args file exists in config directory, pass it to Erlang VM
[ -f "$VMARGS" ] && ERLANG_OPTS="$ERLANG_OPTS -args_file $VMARGS"
ERL_LIBS='{{libdir}}'
ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump
ERL_INETRC="$CONFIG_DIR"/inetrc

# define ejabberd parameters
EJABBERD_OPTS="\
$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]\{1,\}\).*/ \1/;s/:[ \t]*\(infinity\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\
$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\
$(sed '/^log_burst_limit_count/!d;s/:[ \t]*\([0-9]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\
$(sed '/^log_burst_limit_window_time/!d;s/:[ \t]*\([0-9]*[a-z]*\).*/ \1 /;s/^/ /' "$EJABBERD_CONFIG_PATH")\
$EJABBERD_OPTS"
[ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS"
EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd"

# export global variables
export EJABBERD_CONFIG_PATH
export EJABBERD_LOG_PATH
export EJABBERD_PID_PATH
export ERL_CRASH_DUMP
export ERL_EPMD_ADDRESS
export ERL_DIST_PORT
export ERL_INETRC
export ERL_MAX_PORTS
export ERL_MAX_ETS_TABLES
export CONTRIB_MODULES_PATH
export CONTRIB_MODULES_CONF_DIR
export ERL_LIBS
export SCRIPT_DIR

# Only required for Erlang/OTP 25:
export ERL_FLAGS="$ERL_FLAGS -enable-feature maybe_expr"

set_dist_client()
{
    [ -n "$ERL_DIST_PORT" ] && ERLANG_OPTS="$ERLANG_OPTS -dist_listen false"
}

# run command either directly or via su $INSTALLUSER
run_cmd()
{
    case $EXEC_CMD in
        as_install_user) su -s /bin/sh -c '"$0" "$@"' "$INSTALLUSER" -- "$@" ;;
        as_current_user) "$@" ;;
    esac
}
exec_cmd()
{
    case $EXEC_CMD,$(uname -s) in
        as_install_user,OpenBSD)
            su -s /bin/sh "$INSTALLUSER" -c 'exec "$0" "$@"' "$@"
            ;;
        as_install_user,NetBSD)
            su "$INSTALLUSER" -c 'exec "$0" "$@"' -- "$@"
            ;;
        as_install_user,*)
            su -s /bin/sh -c 'exec "$0" "$@"' "$INSTALLUSER" -- "$@"
            ;;
        as_current_user,*)
            "$@"
            ;;
    esac
}
run_erl()
{
    NODE=$1; shift
    run_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@"
}
exec_erl()
{
    NODE=$1; shift
    exec_cmd "$ERL" ${S:--}name "$NODE" $ERLANG_OPTS "$@"
}
exec_iex()
{
    NODE=$1; shift
    exec_cmd "$IEX" -${S:--}name "$NODE" --erl "$ERLANG_OPTS" "$@"
}

# usage
debugwarning()
{
    if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
        echo "--------------------------------------------------------------------"
        echo ""
        echo "IMPORTANT: we will attempt to attach an INTERACTIVE shell"
        echo "to an already running ejabberd node."
        echo "If an ERROR is printed, it means the connection was not successful."
        echo "You can interact with the ejabberd node if you know how to use it."
        echo "Please be extremely cautious with your actions,"
        echo "and exit immediately if you are not completely sure."
        echo ""
        echo "To exit and detach this shell from ejabberd, press:"
        echo "  control+g and then q"
        echo ""
        #vt100 echo "Please do NOT use control+c in this debug shell !"
        #vt100 echo ""
        echo "--------------------------------------------------------------------"
        echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
        echo "  EJABBERD_BYPASS_WARNINGS=true"
        echo "Press return to continue"
        read -r _
        echo ""
    fi
}

livewarning()
{
    if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then
        echo "--------------------------------------------------------------------"
        echo ""
        echo "IMPORTANT: ejabberd is going to start in LIVE (interactive) mode."
        echo "All log messages will be shown in the command shell."
        echo "You can interact with the ejabberd node if you know how to use it."
        echo "Please be extremely cautious with your actions,"
        echo "and exit immediately if you are not completely sure."
        echo ""
        echo "To stop ejabberd gracefully:"
        echo "  ejabberd:stop()."
        echo "To quit erlang immediately, press:"
        echo "  control+g and then q"
        echo ""
        echo "--------------------------------------------------------------------"
        echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:"
        echo "  EJABBERD_BYPASS_WARNINGS=true"
        echo "Press return to continue"
        read -r _
        echo ""
    fi
}

check_etop_result()
{
    result=$?
    if [ $result -eq 1 ] ; then
        echo ""
        echo "It seems there was some problem running 'ejabberdctl etop'."
        echo "Is the error message something like this?"
        echo "  Failed to load module 'etop' because it cannot be found..."
        echo "Then probably ejabberd was compiled with development tools disabled."
        echo "To use 'etop', recompile ejabberd with: ./configure --enable-tools"
        echo ""
        exit $result
    fi
}

check_iex_result()
{
    result=$?
    if [ $result -eq 127 ] ; then
        echo ""
        echo "It seems there was some problem finding 'iex' binary from Elixir."
        echo "Probably ejabberd was compiled with Rebar3 and Elixir disabled, like:"
        echo "  ./configure"
        echo "which is equivalent to:"
        echo "  ./configure --with-rebar=rebar3 --disable-elixir"
        echo "To use 'iex', recompile ejabberd enabling Elixir or using Mix:"
        echo "  ./configure --enable-elixir"
        echo "  ./configure --with-rebar=mix"
        echo ""
        exit $result
    fi
}

help()
{
    echo ""
    echo "Commands to start an ejabberd node:"
    echo "  start            Start in server mode"
    echo "  foreground       Start in server mode (attached)"
    echo "  foreground-quiet Start in server mode (attached), show only critical messages"
    echo "  live             Start in interactive mode, with Erlang shell"
    echo "  iexlive          Start in interactive mode, with Elixir shell"
    echo ""
    echo "Commands to interact with a running ejabberd node:"
    echo "  debug            Attach an interactive Erlang shell to a running node"
    echo "  iexdebug         Attach an interactive Elixir shell to a running node"
    echo "  etop             Attach to a running node and start Erlang Top"
    echo "  ping             Send ping to the node, returns pong or pang"
    echo "  started|stopped  Wait for the node to fully start|stop"
    echo ""
    echo "Optional parameters when starting an ejabberd node:"
    echo "  --config-dir dir   Config ejabberd:    $CONFIG_DIR"
    echo "  --config file      Config ejabberd:    $EJABBERD_CONFIG_PATH"
    echo "  --ctl-config file  Config ejabberdctl: $EJABBERDCTL_CONFIG_PATH"
    echo "  --logs dir         Directory for logs: $LOGS_DIR"
    echo "  --spool dir        Database spool dir: $SPOOL_DIR"
    echo "  --node nodename    ejabberd node name: $ERLANG_NODE"
    echo ""
}

# dynamic node name helper
uid() {
  ERTSVERSION="$("$ERL" -version 2>&1 | sed 's|.* \([0-9]*[0-9]\).*|\1|g')"
  if [ $ERTSVERSION -lt 11 ] ; then # otp 23.0 includes erts 11.0
    # Erlang/OTP lower than 23, which doesn's support dynamic node code
    N=1
    PF=$(( $$ % 97 ))
    while
      case $# in
        0) NN="${PF}-${N}-${ERLANG_NODE}"
          ;;
        1) NN="${PF}-${N}-${1}-${ERLANG_NODE}"
          ;;
        2) NN="${PF}-${N}-${1}@${2}"
          ;;
      esac
      N=$(( N + 1 + ( $$ % 5 ) ))
      "$EPMD" -names 2>/dev/null | grep -q " ${NN%@*} "
    do :; done
    echo $NN
  else
    # Erlang/OTP 23 or higher: use native dynamic node code
    # https://www.erlang.org/patches/otp-23.0#OTP-13812
    if [ "$ERLANG_NODE" != "${ERLANG_NODE%.*}" ]; then
      echo "undefined@${ERLANG_NODE#*@}"
    else
      echo "undefined"
    fi
  fi
}

# stop epmd if there is no other running node
stop_epmd()
{
    [ -n "$ERL_DIST_PORT" ] && return
    "$EPMD" -names 2>/dev/null | grep -q name || "$EPMD" -kill >/dev/null
}

# make sure node not already running and node name unregistered
# if all ok, ensure runtime directory exists and make it current directory
check_start()
{
    ECSIMAGE_DBPATH=$HOME/database/$ERLANG_NODE
    [ ! -d "$ECSIMAGE_DBPATH" ] && ln -s $HOME/database $HOME/database/$ERLANG_NODE
    [ -n "$ERL_DIST_PORT" ] && {
        netstat -nl | grep ":5210" >/dev/null && {
            echo "Error: The file ejabberdctl.cfg has configured ERL_DIST_PORT=$ERL_DIST_PORT"
            echo "       but the port 5210 is already in use."
            echo "       Stop that program or configure a different ERL_DIST_PORT"
            echo ""
            netstat -nlp | grep ":5210"
            echo ""
        }
        return
    }
    "$EPMD" -names 2>/dev/null | grep -q " ${ERLANG_NODE%@*} " && {
        pgrep -f "$ERLANG_NODE" >/dev/null && {
            echo "ERROR: The ejabberd node '$ERLANG_NODE' is already running."
            exit 4
        }
        pgrep beam >/dev/null && {
            echo "ERROR: The ejabberd node '$ERLANG_NODE' is registered,"
            echo "       but no related beam process has been found."
            echo "Shutdown all other erlang nodes, and call 'epmd -kill'."
            exit 5
        }
        "$EPMD" -kill >/dev/null
    }
}

post_waiter_fork()
{
    (FIRST_RUN=$FIRST_RUN "$0" post_waiter)&
}

post_waiter_waiting()
{
    $0 started
    [ -n "$FIRST_RUN" ] && [ -n "$CTL_ON_CREATE" ] && (post_waiter_loop $CTL_ON_CREATE)
    [ -n "$CTL_ON_START" ] && post_waiter_loop $CTL_ON_START
}

post_waiter_loop()
{
    LIST=$@
    HEAD=${LIST%% ; *}
    TAIL=${LIST#* ; }
    HEAD2=${HEAD#\! *}
    echo ":> ejabberdctl $HEAD2"
    $0 $HEAD2
    ctlstatus=$?
    if [ $ctlstatus -ne 0 ] ; then
        if [ "$HEAD" != "$HEAD2" ] ; then
            echo ":> FAILURE in command '$HEAD2' !!! Ignoring result"
        else
            echo ":> FAILURE in command '$HEAD' !!! Stopping ejabberd..."
            $0 halt > /dev/null
            exit $ctlstatus
        fi
    fi
    [ "$HEAD" = "$TAIL" ] || post_waiter_loop $TAIL
}

# allow sync calls
wait_status()
{
    wait_status_node "$ERLANG_NODE" $1 $2 $3
}

wait_status_node()
{
    CONNECT_NODE=$1
    shift
    # args: status try delay
    # return: 0 OK, 1 KO
    timeout="$2"
    status=4
    while [ "$status" -ne "$1" ] ; do
        sleep "$3"
        timeout=$((timeout - 1))
        if [ $timeout -eq 0 ] ; then
            status="$1"
        else
            run_erl "$(uid ctl)" -hidden -noinput \
                     -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
                     -s ejabberd_ctl \
                     -extra "$CONNECT_NODE" $NO_TIMEOUT status > /dev/null
            status="$?"
        fi
    done
    [ $timeout -gt 0 ]
}

exec_other_command()
{
    exec_other_command_node $ERLANG_NODE "$@"
}

exec_other_command_node()
{
    CONNECT_NODE=$1
    shift
    if [ -z "$CTL_OVER_HTTP" ] || [ ! -S "$CTL_OVER_HTTP" ] \
        || [ ! -x "$(command -v curl)" ] || [ -z "$1" ] || [ "$1" = "help" ] \
        || [ "$1" = "mnesia_info_ctl" ]|| [ "$1" = "print_sql_schema" ] ; then
        run_erl "$(uid ctl)" -hidden -noinput \
                 -eval 'net_kernel:connect_node('"'$CONNECT_NODE'"')' \
                 -s ejabberd_ctl \
                 -extra "$CONNECT_NODE" $NO_TIMEOUT "$@"
        result=$?
        case $result in
            3) help;;
            *) :;;
        esac
        return $result
    else
        exec_ctl_over_http_socket "$@"
    fi
}

exec_ctl_over_http_socket()
{
    COMMAND=${1}
    CARGS=""
    while [ $# -gt 0 ]; do
        [ -z "$CARGS" ] && CARGS="[" || CARGS="${CARGS}, "
        CARGS="${CARGS}\"$1\""
        shift
    done
    CARGS="${CARGS}]"
    TEMPHEADERS=temp-headers.log
    curl \
        --unix-socket ${CTL_OVER_HTTP} \
        --header "Content-Type: application/json" \
        --header "Accept: application/json" \
        --data "${CARGS}" \
        --dump-header ${TEMPHEADERS} \
        --no-progress-meter \
        "http://localhost/ctl/${COMMAND}"
    result=$(sed -n 's/.*status-code: \([0-9]*\).*/\1/p' < $TEMPHEADERS)
    rm ${TEMPHEADERS}
    case $result in
        2|3) exec_other_command help ${COMMAND};;
        *) :;;
    esac
    exit $result
}

# ensure we can change current directory to SPOOL_DIR
[ -f "$SPOOL_DIR/schema.DAT" ] || FIRST_RUN=true
[ -d "$SPOOL_DIR" ] || run_cmd mkdir -p "$SPOOL_DIR"
cd "$SPOOL_DIR" || {
    echo "ERROR: can not access directory $SPOOL_DIR"
    exit 6
}

printe()
{
    printf "\n"
    printf "\e[1;40;32m==> %s\e[0m\n" "$1"
}

## Function copied from tools/make-installers
user_agrees()
{
    question="$*"

    if [ -t 0 ]
    then
        printe "$question (y/n) [n]"
        read -r response
        case "$response" in
        [Yy]|[Yy][Ee][Ss])
            return 0
            ;;
        [Nn]|[Nn][Oo]|'')
            return 1
            ;;
        *)
            echo 'Please respond with "yes" or "no".'
            user_agrees "$question"
            ;;
        esac
    else # Assume 'yes' if not running interactively.
        return 0
    fi
}

mnesia_change()
{
    ERLANG_NODE_OLD="$1"
    [ "$ERLANG_NODE_OLD" = "" ] \
        && echo "Error: Please provide the old erlang node name, for example:" \
        && echo "  ejabberdctl mnesia_change ejabberd@oldmachine" \
        && exit 1

    SPOOL_DIR_BACKUP=$SPOOL_DIR/$ERLANG_NODE_OLD-backup/
    OLDFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE_OLD.backup
    NEWFILE=$SPOOL_DIR_BACKUP/$ERLANG_NODE.backup

    printe "This changes your mnesia database from node name '$ERLANG_NODE_OLD' to '$ERLANG_NODE'"

    [ -d "$SPOOL_DIR_BACKUP" ] && printe "WARNING! A backup of old node already exists in $SPOOL_DIR_BACKUP"

    if ! user_agrees "Do you want to proceed?"
    then
        echo 'Operation aborted.'
        exit 1
    fi

    printe "Starting ejabberd with old node name $ERLANG_NODE_OLD ..."
    exec_erl "$ERLANG_NODE_OLD" $EJABBERD_OPTS -detached
    wait_status_node $ERLANG_NODE_OLD 0 30 2
    result=$?
    case $result in
        1) echo "There was a problem starting ejabberd with the old erlang node name. " \
            && echo "Check for log errors in $EJABBERD_LOG_PATH" \
            && exit $result;;
        *) :;;
    esac
    exec_other_command_node $ERLANG_NODE_OLD "status"

    printe "Making backup of old database to file $OLDFILE ..."
    mkdir $SPOOL_DIR_BACKUP
    exec_other_command_node $ERLANG_NODE_OLD backup "$OLDFILE"

    printe "Changing node name in new backup file $NEWFILE ..."
    exec_other_command_node $ERLANG_NODE_OLD mnesia_change_nodename "$ERLANG_NODE_OLD" "$ERLANG_NODE" "$OLDFILE" "$NEWFILE"

    printe "Stopping old ejabberd..."
    exec_other_command_node $ERLANG_NODE_OLD "stop"
    wait_status_node $ERLANG_NODE_OLD 3 30 2 && stop_epmd

    printe "Moving old mnesia spool files to backup subdirectory $SPOOL_DIR_BACKUP ..."
    mv $SPOOL_DIR/*.DAT $SPOOL_DIR_BACKUP
    mv $SPOOL_DIR/*.DCD $SPOOL_DIR_BACKUP
    mv $SPOOL_DIR/*.LOG $SPOOL_DIR_BACKUP

    printe "Starting ejabberd with new node name $ERLANG_NODE ..."
    exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
    wait_status 0 30 2
    exec_other_command "status"

    printe "Installing fallback of new mnesia..."
    exec_other_command install_fallback "$NEWFILE"

    printe "Stopping new ejabberd..."
    exec_other_command "stop"
    wait_status 3 30 2 && stop_epmd

    printe "Finished, now you can start ejabberd normally"
}

# main
case $1 in
    start)
        check_start
        exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -detached
        ;;
    foreground)
        check_start
        post_waiter_fork
        exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput
        ;;
    foreground-quiet)
        check_start
        exec_erl "$ERLANG_NODE" $EJABBERD_OPTS -noinput -ejabberd quiet true
        ;;
    live)
        livewarning
        check_start
        exec_erl "$ERLANG_NODE" $EJABBERD_OPTS
        ;;
    debug)
        debugwarning
        set_dist_client
        exec_erl "$(uid debug)" -hidden -remsh "$ERLANG_NODE"
        ;;
    etop)
        set_dist_client
        exec_erl "$(uid top)" -hidden -remsh "$ERLANG_NODE" \
                 -eval 'net_kernel:connect_node('"'$ERLANG_NODE'"')' \
                 -s etop \
                 -output text
        check_etop_result
        ;;
    iexdebug)
        debugwarning
        set_dist_client
        exec_iex "$(uid debug)" --remsh "$ERLANG_NODE"
        check_iex_result
        ;;
    iexlive)
        livewarning
        exec_iex "$ERLANG_NODE" --erl "$EJABBERD_OPTS"
        check_iex_result
        ;;
    ping)
        PEER=${2:-$ERLANG_NODE}
        [ "$PEER" = "${PEER%.*}" ] && PS="-s"
        set_dist_client
        exec_cmd "$ERL" ${PS:--}name "$(uid ping "$(hostname $PS)")" $ERLANG_OPTS \
                 -noinput -hidden \
                 -eval 'net_kernel:connect_node('"'$PEER'"')' \
                 -eval 'io:format("~p~n",[net_adm:ping('"'$PEER'"')])' \
                 -eval 'halt(case net_adm:ping('"'$PEER'"') of pong -> 0; pang -> 1 end).' \
                 -output text
        ;;
    started)
        set_dist_client
        wait_status 0 30 2 # wait 30x2s before timeout
        ;;
    stopped)
        set_dist_client
        wait_status 3 30 2 && stop_epmd # wait 30x2s before timeout
        ;;
    mnesia_change)
        mnesia_change $2
        ;;
    post_waiter)
        post_waiter_waiting
        ;;
    *)
        set_dist_client
        exec_other_command "$@"
        ;;
esac


================================================
FILE: .github/lock.yml
================================================
# Configuration for Lock Threads - https://github.com/dessant/lock-threads

# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 365

# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false

# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []

# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false

# Comment to post before locking. Set to `false` to disable
lockComment: >
  This thread has been automatically locked since there has not been
  any recent activity after it was closed. Please open a new issue for
  related bugs.

# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true

# Limit to only `issues` or `pulls`
# only: issues

# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
#   exemptLabels:
#     - help-wanted
#   lockLabel: outdated

# pulls:
#   daysUntilLock: 30

# Repository to extend settings from
# _extends: repo


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    paths-ignore:
    - '.devcontainer/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'
  pull_request:
    paths-ignore:
    - '.devcontainer/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'

env:
  DEV: _build/dev/rel/ejabberd

jobs:

  ############################################################### Compile #####

  compile:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['28']

    steps:

    - uses: actions/checkout@v6

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: awalsh128/cache-apt-pkgs-action@latest
      with:
        packages: libexpat1-dev libgd-dev libpam0g-dev
                  libsqlite3-dev libwebp-dev libyaml-dev

    - name: Cache rebar3
      uses: actions/cache@v5
      with:
        path: |
          ~/.cache/rebar3/
          _build/default/lib/
        key: ci-${{ matrix.otp }}-${{hashFiles('rebar.*')}}

    - name: Compile
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: compile
        tool: rebar3
        configure: --disable-elixir

    - name: Compress compiled.tar
      run: |
        tar -cvf /tmp/compiled.tar .

    - uses: actions/upload-artifact@v6
      with:
        name: compiled-${{ matrix.otp }}
        path: /tmp/compiled.tar
        retention-days: 1

  ########################################################## Static Tests #####

  static:
    needs: compile
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['28']

    steps:

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: actions/download-artifact@v7
      with:
        name: compiled-${{ matrix.otp }}
    - run: tar -xf compiled.tar

    - name: Test shell scripts
      run: |
        shellcheck ejabberd.init.template
        shellcheck -x ejabberdctl.template
        shellcheck .vscode/relive.sh
        shellcheck rel/setup-dev.sh
        shellcheck rel/setup-relive.sh
        shellcheck test/ejabberd_SUITE_data/gencerts.sh
        shellcheck tools/captcha.sh
        shellcheck tools/emacs-indent.sh
        shellcheck tools/generate-doap.sh
        shellcheck tools/prepare-tr.sh
        shellcheck tools/rebar3-format.sh

    - run: make hooks
    - run: make options
    - run: make xref
    - run: make dialyzer
    - run: make testeunit
    - run: make elvis
      if: matrix.otp > '25'

    - run: make install -s

  ######################################################### Dynamic Tests #####

  dynamic:
    needs: compile
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['28']

    steps:

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: awalsh128/cache-apt-pkgs-action@latest
      if: matrix.otp < '28'
      with:
        packages: libexpat1-dev libgd-dev libpam0g-dev
                  libsqlite3-dev libwebp-dev libyaml-dev

    - uses: actions/download-artifact@v7
      with:
        name: compiled-${{ matrix.otp }}
    - run: tar -xf compiled.tar

    - name: Check production release
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: deploy, start, stop, check

    - name: Start development release
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: deploy, no_tls, start, register
        username: user123

    - name: Run XMPP Interoperability Tests against CI server
      if: matrix.otp == '28'
      continue-on-error: true
      uses: XMPP-Interop-Testing/xmpp-interop-tests-action@v1.7.2
      with:
        domain: 'localhost'
        adminAccountUsername: 'user123'
        adminAccountPassword: 's0mePass'
        disabledSpecifications: RFC6121,XEP-0030,XEP-0045,XEP-0054,XEP-0060,
                                XEP-0080,XEP-0115,XEP-0118,XEP-0215,XEP-0347,
                                XEP-0363,XEP-0384,XEP-0421

    - name: Stop development release
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: stop, check

    - name: View production logs
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: logs

    - name: View development logs
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: logs

  ##################################################### Common Test Suite #####

  ct:
    needs: compile
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['28']
        backend: [agnostic, extauth, ldap, mnesia, mysql, pgsql, redis, sqlite]
        schema: [single, multi]
        exclude:
        - backend: agnostic
          schema: single
        - backend: extauth
          schema: single
        - backend: ldap
          schema: single
        - backend: mnesia
          schema: single
        - backend: redis
          schema: single

    steps:

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: actions/download-artifact@v7
      with:
        name: compiled-${{ matrix.otp }}
    - run: tar -xf compiled.tar

    - name: Prepare database
      uses: ./.github/actions/manage-database
      with:
        for: ${{ matrix.backend }}
        do: install, start, user, create

    - name: Setup multihost SQL schema
      if: matrix.schema == 'multi'
      run: |
        sed -i 's|multihost_schema, false|multihost_schema, true|g' \
            test/suite.erl

    - name: Run tests
      id: ct
      run: CT_BACKENDS=${{ matrix.backend }} make test

    - name: Send to coveralls
      continue-on-error: true
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        DIAGNOSTIC=1 ./rebar3 as test coveralls send

    - name: Check results
      if: always() && (steps.ct.outcome != 'skipped')
      id: ctresults
      run: |
        [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
        ln `find logs/ -name suite.log` logs/suite.log
        grep 'TEST COMPLETE' logs/suite.log
        grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
        test $(find logs/ -empty -name error.log)

    - name: View logs
      if: always()
      run: |
        echo "::group::ejabberd.log"
        find logs/ -name ejabberd.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::error.log"
        find logs/ -name error.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::exunit.log"
        find logs/ -name exunit.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::suite.log (only failures)"
        cat logs/suite.log | awk \
          'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
        echo "::endgroup::"
        echo "::group::suite.log (complete)"
        cat logs/suite.log
        echo "::endgroup::"

    - name: Upload CT logs
      if: failure()
      uses: actions/upload-artifact@v6
      with:
        name: ct-logs-${{ matrix.otp }}-${{ matrix.backend }}-${{ matrix.schema }}
        path: _build/test/logs
        retention-days: 14

  ############################################################# Coveralls #####

  cover:
    needs: ct
    runs-on: ubuntu-24.04-arm
    steps:
    - name: Finish parallel upload to coveralls
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        curl -v -k https://coveralls.io/webhook \
             --header "Content-Type: application/json" \
             --data '{"repo_name":"$GITHUB_REPOSITORY",
                      "repo_token":"$GITHUB_TOKEN",
                      "payload":{"build_num":$GITHUB_RUN_ID,
                                 "status":"done"}}'

  ################################################################ Schema #####

  schema:
    needs: compile
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['28']

    steps:

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: actions/download-artifact@v7
      with:
        name: compiled-${{ matrix.otp }}
    - run: tar -xf compiled.tar

    ###################################### multi-changed ##

    - name: Prepare databases
      uses: ./.github/actions/manage-database
      with:
        for: mysql, pgsql
        do: install, start, user, create

    - name: Prepare configuration
      run: |
        CT_BACKENDS=mysql,pgsql ./rebar3 ct \
            --suite=test/ejabberd_SUITE --group=configtest_single
        make dev
        cp test/ejabberd_SUITE_data/ejabberd.yml ${{ env.DEV }}/conf/
        cp _build/test/logs/last/*.yml ${{ env.DEV }}/database/
        echo "define_macro: [CONFIGTEST_CONFIG: {modules: {mod_muc: {}}}]" \
             > ${{ env.DEV }}/database/configtest.yml
        cp _build/test/logs/last/*.pem ${{ env.DEV }}/conf/
        cp _build/test/logs/last/*.pem ${{ env.DEV }}/database/

    - name: Run ejabberd
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: start, update_sql, stop, check, logs
        username: user2

    - name: Dump multihost databases
      uses: ./.github/actions/manage-database
      with:
        for: mysql, pgsql
        do: dump
        dump-suffix: multi-changed

    ######################################### multi-auto ##

    - name: Prepare databases
      uses: ./.github/actions/manage-database
      with:
        for: mysql, pgsql
        do: drop, create

    - name: Configure multihost schema
      run: |
        sed -i 's|MULTIHOST_SCHEMA|true|g' ${{ env.DEV }}/conf/ejabberd.yml

    - name: Run ejabberd for multihost
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: start, stop, check, logs
        username: user2

    - name: Dump multihost databases
      uses: ./.github/actions/manage-database
      with:
        for: mysql, pgsql
        do: dump
        dump-suffix: multi-auto

    ############################################ compare ##

    - name: View SQL schemas (mysql)
      run: |
        perl test/ejabberd_SUITE_data/sql_sort.pl \
             <mysql-multi-auto.sql >mysql-multi2.sql
        perl test/ejabberd_SUITE_data/sql_sort.pl \
             <mysql-multi-changed.sql >mysql-changed2.sql
        echo "::group::differences multi-auto > multi-changed"
        diff -u mysql-multi2.sql mysql-changed2.sql || echo ok
        echo "::endgroup::"
        echo "::group::multi-auto.sql"
        cat mysql-multi-auto.sql
        echo "::endgroup::"
        echo "::group::multi-changed.sql"
        cat mysql-multi-changed.sql
        echo "::endgroup::"

    - name: View SQL schemas (pgsql)
      run: |
        perl test/ejabberd_SUITE_data/sql_sort.pl \
             <pgsql-multi-auto.sql >pgsql-multi2.sql
        perl test/ejabberd_SUITE_data/sql_sort.pl \
             <pgsql-multi-changed.sql >pgsql-changed2.sql
        echo "::group::differences (multi-auto > multi-changed)"
        diff -u pgsql-multi2.sql pgsql-changed2.sql || echo ok
        echo "::endgroup::"
        echo "::group::multi-auto.sql"
        cat pgsql-multi-auto.sql
        echo "::endgroup::"
        echo "::group::multi-changed.sql"
        cat pgsql-multi-changed.sql
        echo "::endgroup::"

    - name: View ejabberd logs
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: logs


================================================
FILE: .github/workflows/container.yml
================================================
name: Container

on:
  push:
    paths-ignore:
    - '.devcontainer/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:

  build:
    runs-on: ubuntu-24.04${{ matrix.suffix }}
    strategy:
      matrix:
        platform: [amd64, arm64]
        include:
          - platform: amd64
            suffix:
          - platform: arm64
            suffix: -arm
    permissions:
      packages: write
    steps:
      - name: Check out repository code
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Checkout ejabberd-contrib
        uses: actions/checkout@v6
        with:
          repository: processone/ejabberd-contrib
          path: .ejabberd-modules/sources/ejabberd-contrib

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Get git describe
        id: gitdescribe
        run: echo "ver=$(git describe --tags)" >> $GITHUB_OUTPUT

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: suffix=--${{ matrix.platform }}
          labels: |
            org.opencontainers.image.revision=${{ steps.gitdescribe.outputs.ver }}
            org.opencontainers.image.licenses=GPL-2.0
            org.opencontainers.image.vendor=ProcessOne

      - uses: docker/setup-buildx-action@v3

      - uses: docker/build-push-action@v6
        with:
          build-args: |
            VERSION=${{ steps.gitdescribe.outputs.ver }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          context: .
          file: .github/container/Dockerfile
          labels: ${{ steps.meta.outputs.labels }}
          platforms: linux/${{ matrix.platform }}
          push: true
          tags: ${{ steps.meta.outputs.tags }}

  merge:
    needs: [build]
    runs-on: ubuntu-24.04
    outputs:
      image-uri: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ steps.merge.outputs.digest }}
    steps:
      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/metadata-action@v5
        id: meta-amd
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: suffix=--amd64
      - uses: docker/metadata-action@v5
        id: meta-arm
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          flavor: suffix=--arm64
      - uses: docker/metadata-action@v5
        id: meta-result
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - uses: int128/docker-manifest-create-action@v2
        id: merge
        with:
          index-annotations: ${{ steps.metadata.outputs.labels }}
          tags: ${{ steps.meta-result.outputs.tags }}
          sources: |
            ${{ steps.meta-amd.outputs.tags }}
            ${{ steps.meta-arm.outputs.tags }}
      #- uses: dataaxiom/ghcr-cleanup-action@v1
      #  with:
      #    dry-run: true
      #    delete-tags: '*--a??64'
      #    delete-untagged: true
      #    delete-ghost-images: true
      #    delete-partial-images: true
      #    delete-orphaned-images: true


================================================
FILE: .github/workflows/installers.yml
================================================
name: Installers

on:
  push:
    paths-ignore:
    - '.devcontainer/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'
  pull_request:
    paths-ignore:
    - '.devcontainer/**'
    - 'lib/**'
    - 'man/**'
    - 'priv/**'
    - '**.md'

jobs:
  binaries:
    runs-on: ubuntu-22.04${{ matrix.suffix }}
    strategy:
      matrix:
        platform: [amd64, arm64]
        include:
          - platform: amd64
            suffix:
          - platform: arm64
            suffix: -arm
    steps:
      - name: Check out repository code
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
      - name: Cache build directory
        uses: actions/cache@v5
        with:
          path: ~/build/
          key: installers-ubuntu-22.04-${{runner.arch}}-${{hashFiles('tools/make-binaries')}}
      - name: Install prerequisites
        run: |
          sudo apt-get -qq update
          sudo apt-get -qq install makeself
          # https://github.com/crosstool-ng/crosstool-ng/blob/master/testing/docker/ubuntu21.10/Dockerfile
          sudo apt-get -qq install build-essential autoconf bison flex gawk
          sudo apt-get -qq install help2man libncurses5-dev libtool libtool-bin
          sudo apt-get -qq install python3-dev texinfo unzip
      - name: Install FPM
        run: |
          gem install --no-document --user-install fpm
          echo $HOME/.local/share/gem/ruby/*/bin >> $GITHUB_PATH
      - name: Build binary archives
        run: CHECK_DEPS=false tools/make-binaries
      - name: Build DEB and RPM packages
        run: tools/make-packages
      - name: Build installers
        run: tools/make-installers

      - run: echo "rel_name_vsn=$(ls *.run | sed 's/-1-linux.*//g')" >> $GITHUB_OUTPUT
        id: vsn
      - name: Test RUN
        uses: ./.github/actions/manage-ejabberd
        with:
          for: run
          do: deploy, no_acme, start, register, stop, check, logs
          username: user1
          host: $(hostname --fqdn)
          rel_name_vsn: ${{ steps.vsn.outputs.rel_name_vsn }}
      - name: Test DEB
        uses: ./.github/actions/manage-ejabberd
        with:
          for: deb
          do: deploy, register, stop, logs
          username: user1
          host: $(hostname --fqdn)
          rel_name_vsn: ${{ steps.vsn.outputs.rel_name_vsn }}

      - name: Collect packages
        run: |
          mkdir ejabberd-packages
          mv ejabberd_*.deb ejabberd-*.rpm ejabberd-*.run ejabberd-packages
      - name: Upload packages
        uses: actions/upload-artifact@v6
        with:
          name: ejabberd-packages-${{ matrix.platform }}
          #
          # Appending the wildcard character ("*") is a trick to make
          # "ejabberd-packages" the root directory of the uploaded ZIP file:
          #
          # https://github.com/actions/upload-artifact#upload-using-multiple-paths-and-exclusions
          #
          path: ejabberd-packages*
          retention-days: 14

  release:
    name: Release
    needs: [binaries]
    runs-on: ubuntu-22.04
    if: github.ref_type == 'tag'
    steps:
      - name: Download packages
        uses: actions/download-artifact@v7
        with:
          merge-multiple: true
      - name: Draft Release
        uses: softprops/action-gh-release@v2
        with:
          draft: true
          files: ejabberd-packages/*


================================================
FILE: .github/workflows/runtime.yml
================================================
name: Runtime

on:
  push:
    paths:
    - '*'
    - '!*.md'
    - '.github/workflows/runtime.yml'
    - 'checkouts/**'
    - 'config/**'
    - 'lib/**'
    - 'm4/**'
    - 'plugins/**'
    - 'rel/**'
  pull_request:
    paths:
    - '*'
    - '!*.md'
    - '.github/workflows/runtime.yml'
    - 'checkouts/**'
    - 'config/**'
    - 'lib/**'
    - 'm4/**'
    - 'plugins/**'
    - 'rel/**'

jobs:

  ################################################################ Rebars #####

  rebars:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['25', '26', '27', '28', '29']
        rebar: ['rebar', 'rebar3']
        exclude:
        - otp: '27'
          rebar: 'rebar'
        - otp: '28'
          rebar: 'rebar'
        - otp: '29'
          rebar: 'rebar'
    container:
      image: public.ecr.aws/docker/library/erlang:${{ matrix.otp }}

    steps:

    - uses: actions/checkout@v6

    - name: Prepare libraries
      run: |
        apt-get -qq update
        apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
                              libsqlite3-dev libwebp-dev libyaml-dev

    - name: Cache rebar3
      if: matrix.rebar == 'rebar3'
      uses: actions/cache@v5
      with:
        path: |
          ~/.cache/rebar3/
          _build/default/lib/
        key: runtime-${{ matrix.otp }}-${{matrix.rebar}}-${{hashFiles('rebar.*')}}

    - name: Compile
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: compile
        tool: ${{ matrix.rebar }}
        configure: --disable-elixir

    - run: make hooks
    - run: make options
    - run: make xref
    - run: make dialyzer
    - run: make elvis
      if: matrix.otp > '25' && matrix.rebar == 'rebar3'

    - name: Production
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: deploy, start, register, stop, check, logs
        tool: ${{ matrix.rebar }}
        username: user1

    - name: Development
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: deploy, start, register, stop, check, logs
        tool: ${{ matrix.rebar }}
        username: user2

    - name: Installed
      uses: ./.github/actions/manage-ejabberd
      with:
        for: install
        do: deploy, start, register, stop, check, logs
        tool: ${{ matrix.rebar }}
        username: user3

  ####################################################### Rebar3 + Elixir #####

  rebar3-elixir:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        elixir: ['1.14', '1.18', '1.19']
    container:
      image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }}

    steps:

    - uses: actions/checkout@v6

    - name: Prepare libraries
      run: |
        apt-get -qq update
        apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
                              libsqlite3-dev libwebp-dev libyaml-dev

    - name: Enable Module.Example and an Elixir dependency
      run: |
        sed -i "s|^modules:|modules:\n  'Ejabberd.Module.Example': {}|g" \
                ejabberd.yml.example
        cat ejabberd.yml.example
        sed -i 's|^{deps, \[\(.*\)|{deps, [{decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}},\n \1|g' rebar.config
        cat rebar.config

    - name: Cache hex.pm
      uses: actions/cache@v5
      with:
        path: |
          ~/.cache/rebar3/
        key: runtime-${{matrix.elixir}}-${{hashFiles('rebar.*')}}

    - name: Install Hex and Rebar3 manually on older Elixir
      if: matrix.elixir < '1.15'
      run: |
        mix local.hex --force
        mix local.rebar --force

    - name: Compile
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: compile
        tool: ./rebar3

    - name: Test scripts, deps, eunit
      run: |
        make scripts deps
        ./rebar3 eunit --verbose

    - run: make hooks
    - run: make options
    - run: make xref
    #- run: make dialyzer
    - run: make elvis
      if: matrix.otp > '25'

    - name: Production
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: deploy, start, register, stop, check, logs
        username: user1

    - name: Release
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: deploy, start, register, stop, check, logs
        username: user2

    - name: Installed
      uses: ./.github/actions/manage-ejabberd
      with:
        for: install
        do: deploy, start, register, stop, check, logs
        username: user3

  ################################################################### Mix #####

  mix:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        elixir: ['1.14', '1.18', '1.19']
    container:
      image: public.ecr.aws/docker/library/elixir:${{ matrix.elixir }}

    steps:

    - uses: actions/checkout@v6

    - name: Prepare libraries
      run: |
        apt-get -qq update
        apt-get -q -y install libexpat1-dev libgd-dev libpam0g-dev \
                              libsqlite3-dev libwebp-dev libyaml-dev

    - name: Remove Elixir matchers
      run: |
        echo "::remove-matcher owner=elixir-mixCompileWarning::"
        echo "::remove-matcher owner=elixir-credoOutputDefault::"
        echo "::remove-matcher owner=elixir-mixCompileError::"
        echo "::remove-matcher owner=elixir-mixTestFailure::"
        echo "::remove-matcher owner=elixir-dialyzerOutputDefault::"

    - name: Enable Module.Example and an Elixir dependency
      run: |
        sed -i "s|^modules:|modules:\n  'Ejabberd.Module.Example': {}|g" \
                ejabberd.yml.example
        cat ejabberd.yml.example
        sed -i 's|^{deps, \(.*\)|{deps, \1\n  {decimal, ".*", {git, "https://github.com/ericmj/decimal", {branch, "main"}}}, |g' rebar.config
        cat rebar.config

    - name: Cache hex.pm
      uses: actions/cache@v5
      with:
        path: |
          ~/.hex/
        key: runtime-${{matrix.elixir}}-${{hashFiles('mix.*')}}

    - name: Install Hex and Rebar3 manually on older Elixir
      if: matrix.elixir < '1.15'
      run: |
        mix local.hex --force
        mix local.rebar --force

    - name: Compile
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: compile
        tool: mix

    - run: make hooks
    - run: make options
    - run: make xref
    - run: make dialyzer
    - run: make elvis
      if: matrix.otp > '25'

    - run: make edoc

    - name: Production
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: deploy, start, register, stop, check, logs
        username: user1

    - name: Development
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: deploy, start, register, stop, check, logs
        username: user2

    - name: Installed
      uses: ./.github/actions/manage-ejabberd
      with:
        for: install
        do: deploy, start, register, stop, check, logs
        username: user3


================================================
FILE: .github/workflows/weekly.yml
================================================
name: Weekly

on:
  schedule:
    - cron: '30 9 * * 0'

jobs:

  ############################################################### Compile #####

  test:
    runs-on: ubuntu-24.04-arm
    strategy:
      matrix:
        otp: ['25', '26', '27', '28', '29.0-rc1']

    steps:

    - uses: actions/checkout@v6

    - uses: erlef/setup-beam@v1
      with:
        otp-version: ${{ matrix.otp }}

    - uses: awalsh128/cache-apt-pkgs-action@latest
      with:
        packages: libexpat1-dev libgd-dev libpam0g-dev
                  libsqlite3-dev libwebp-dev libyaml-dev

    - name: Compile
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: compile
        tool: rebar3
        configure: --disable-elixir --disable-mssql

  ########################################################## Static Tests #####

    - name: Test shell scripts
      run: |
        shellcheck ejabberd.init.template
        shellcheck -x ejabberdctl.template
        shellcheck .vscode/relive.sh
        shellcheck rel/setup-dev.sh
        shellcheck rel/setup-relive.sh
        shellcheck test/ejabberd_SUITE_data/gencerts.sh
        shellcheck tools/captcha.sh
        shellcheck tools/emacs-indent.sh
        shellcheck tools/generate-doap.sh
        shellcheck tools/prepare-tr.sh
        shellcheck tools/rebar3-format.sh

    - run: make hooks
    - run: make options
    - run: make xref
    - run: make dialyzer
    - run: make testeunit
    - run: make elvis
      if: matrix.otp > '25'

    - run: make install -s

  ######################################################### Dynamic Tests #####

    - name: Check production release
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: deploy, start, stop, check

    - name: Start development release
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: deploy, no_tls, start, register
        username: user123

    - name: Stop development release
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: stop, check

    - name: View production logs
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: prod
        do: logs

    - name: View development logs
      if: always()
      uses: ./.github/actions/manage-ejabberd
      with:
        for: dev
        do: logs

  ##################################################### Common Test Suite #####

    - name: Prepare database
      uses: ./.github/actions/manage-database
      with:
        for: mysql, pgsql, redis
        do: install, start, user, create

    - name: Setup multihost SQL schema
      run: |
        sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl

    - name: Run tests
      id: ct
      run: make test

    - name: Send to coveralls
      continue-on-error: true
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        DIAGNOSTIC=1 ./rebar3 as test coveralls send

    - name: Check results
      if: always() && (steps.ct.outcome != 'skipped')
      id: ctresults
      run: |
        [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true
        ln `find logs/ -name suite.log` logs/suite.log
        grep 'TEST COMPLETE' logs/suite.log
        grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log
        test $(find logs/ -empty -name error.log)

    - name: View logs
      if: always()
      run: |
        echo "::group::ejabberd.log"
        find logs/ -name ejabberd.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::error.log"
        find logs/ -name error.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::exunit.log"
        find logs/ -name exunit.log -exec cat '{}' ';'
        echo "::endgroup::"
        echo "::group::suite.log (only failures)"
        cat logs/suite.log | awk \
          'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}'
        echo "::endgroup::"
        echo "::group::suite.log (complete)"
        cat logs/suite.log
        echo "::endgroup::"

    - name: Upload CT logs
      if: failure()
      uses: actions/upload-artifact@v6
      with:
        name: ct-logs-${{ matrix.otp }}
        path: _build/test/logs
        retention-days: 14

  ############################################################# Coveralls #####

  cover:
    needs: test
    if: always()
    runs-on: ubuntu-24.04-arm
    steps:
    - name: Finish parallel upload to coveralls
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        curl -v -k https://coveralls.io/webhook \
             --header "Content-Type: application/json" \
             --data '{"repo_name":"$GITHUB_REPOSITORY",
                      "repo_token":"$GITHUB_TOKEN",
                      "payload":{"build_num":$GITHUB_RUN_ID,
                                 "status":"done"}}'


================================================
FILE: .gitignore
================================================
#
# You can add personal rules in your file .git/info/exclude
*.swp
*~
\#*#
.#*
.edts
.tool-versions
*.dump
/Makefile
/doc
/config.log
/config.status
/config/releases.exs
/configure
/aclocal.m4
/*.cache
/deps/
/.deps-update/
/.ejabberd-modules/
/ebin/
/ejabberd.init
/ejabberd.service
/ejabberdctl
/ejabberdctl.example
/rel/ejabberd/
/rel/overlays/
/src/eldap_filter_yecc.erl
/vars.config
/dialyzer/
/test/*.beam
/test/*.ctc
/logs/
/priv/bin/captcha*sh
/priv/sql
/rel/ejabberd
/recompile.log
/_build
/database/
/.rebar
/log/
Mnesia.nonode@nohost/
/TAGS
/tags
# Binaries created with tools/make-{binaries,installers,packages}:
/ejabberd_*.deb
/ejabberd-*.rpm
/ejabberd-*.run
/ejabberd-*.tar.gz
/priv/mod_invites/static/bootstrap/
/priv/mod_invites/static/jquery/
/node_modules/


================================================
FILE: .shellcheckrc
================================================
disable=SC2016,SC2086,SC2089,SC2090
external-sources=true
source=ejabberdctl.cfg.example
shell=sh


================================================
FILE: .vscode/extensions.json
================================================
{
        "recommendations": [
                "erlang-ls.erlang-ls"
        ]
}

================================================
FILE: .vscode/launch.json
================================================
{
        "version": "0.2.0",
        "configurations": [
                {
                        "name": "Relive (Vim)",
                        "type": "erlang",
                        "request": "launch",
                        "runinterminal": [
                                "./rebar3", "shell",
                                "--apps", "ejabberd",
                                "--config", "rel/relive.config",
                                "--script", "rel/relive.escript",
                                "--name", "ejabberd@localhost",
                                "--setcookie", "COOKIE"
                        ],
                        "projectnode": "ejabberd@localhost",
                        "cookie": "COOKIE",
                        "timeout": 900,
                        "cwd": "."
                },
                {
                        "name": "Relive (VSCode)",
                        "type": "erlang",
                        "request": "launch",
                        "runinterminal": [
                                ".vscode/relive.sh"
                        ],
                        "projectnode": "ejabberd@localhost",
                        "cookie": "COOKIE",
                        "timeout": 300,
                        "cwd": "${workspaceRoot}"
                },
                {
                        "name": "Relive (alternate)",
                        "type": "erlang",
                        "request": "launch",
                        "runinterminal": [
                                "./rebar3", "shell",
                                "--apps", "ejabberd",
                                "--config", "rel/relive.config",
                                "--script", "rel/relive.escript",
                                "--name", "ejabberd@localhost",
                                "--setcookie", "COOKIE"
                        ],
                        "projectnode": "ejabberd@localhost",
                        "cookie": "COOKIE",
                        "timeout": 300,
                        "cwd": "${workspaceRoot}"
                },
                {
                        "name": "Attach",
                        "type": "erlang",
                        "request": "attach",
                        "runinterminal": [
                                "./rebar3", "shell",
                                "--sname", "clean@localhost",
                                "--setcookie", "COOKIE",
                                "--start-clean"
                        ],
                        "projectnode": "ejabberd@localhost",
                        "cookie": "COOKIE",
                        "timeout": 300,
                        "cwd": "${workspaceRoot}"
                }
        ]
}


================================================
FILE: .vscode/relive.sh
================================================
[ ! -f Makefile ] \
    && ./autogen.sh \
    && ./configure --with-rebar=rebar3 \
    && make

make relive


================================================
FILE: .vscode/settings.json
================================================
{
    "editor.tabSize": 8,
    "remote.portsAttributes": {
        "1883": {"label": "MQTT", "onAutoForward": "silent"},
        "4369": {"label": "EPMD", "onAutoForward": "silent"},
        "5222": {"label": "XMPP C2S", "onAutoForward": "silent"},
        "5223": {"label": "XMPP C2S (legacy)", "onAutoForward": "silent"},
        "5269": {"label": "XMPP S2S", "onAutoForward": "silent"},
        "5280": {"label": "HTTP", "onAutoForward": "silent"},
        "5443": {"label": "HTTPS", "onAutoForward": "silent"},
        "7777": {"label": "XMPP SOCKS5 (proxy65)", "onAutoForward": "silent"}
    }
}


================================================
FILE: CHANGELOG.md
================================================
## Version 26.02

- Fixes issue with adding hats data in presences send by group chats ([#4516](https://github.com/processone/ejabberd/issues/4516))
- Removes `mod_muc_occupantid` modules, and integrates its functionality directly into `mod_muc` ([#4521](https://github.com/processone/ejabberd/issues/4521))
- Fixes issue with reset occupant-id values after restart of ejabberd ([#4521](https://github.com/processone/ejabberd/issues/4521))
- Improves handling of mediated group chat invitations in `mod_block_stranger` ([#4523](https://github.com/processone/ejabberd/issues/4523))
- Properly install `mod_invites` templates in `make install` call ([#4514](https://github.com/processone/ejabberd/issues/4514))
- Better errors in `mod_invites` ([#4515](https://github.com/processone/ejabberd/issues/4515))
- Accessibility improvements in `mod_invites` ([#4524](https://github.com/processone/ejabberd/issues/4524))
- Improves handling of request with invalid url encoded values in request handled by `ejabberd_http`
- Improves handling of invalid responses to disco queries in `mod_pubsub_serverinfo`
- Fixes conversion of MUC room configs from ejabberd older than 21.12
- Fixes to autologin in WebAdmin

## Version 26.01

#### Compile and Start

- Remove dependencies, macros and code for Erlang/OTP older than 25
- Require Elixir 1.14 or higher, that's the lowest we can test automatically
- `ejabberdctl`: Support NetBSD and OpenBSD `su` ([#4320](https://github.com/processone/ejabberd/issues/4320))
- `ejabberdctl.template`: Show meaningful error when `ERL_DIST_PORT` is in use
- `ejabberd_app`: Print address and port where listens for erlang node connections
- `Makefile.in`: Add `make relivectl` similar to `relive` but using `ejabberdctl`

#### Databases

- Add db_serialize support in mnesia modules
- Add db serialization to `mod_muc_sql`
- New database export/import infrastructure
- Add commands for new database export/import
- Apply timestamp pass in `?SQL_INSERT` queries
- Update p1_mysql to bring fix for timestamp decoding
- Extend timestamp type handling in sql macros
- Revert changes to conversion of pgsql `int` types

#### Installer and Container

- `make-binaries`: Bump Erlang/OTP 28.3.1 and Elixir 1.19.5
- `Dockerfile`: Bump Erlang/OTP 28.3.1 and Elixir 1.19.5
- `Dockerfile`: Expose also port 7777 for SOCKS5
- `Dockerfile`: Configure TURN ports and expose 5478 50000-50099
- `Dockerfile`: Try to fix error with recent `freetds` Alpine package
- Container: Setup new macro `STARTTLS_REQUIRED` to allow easy disabling

#### MUC

- Add `muc_online_rooms_count` API command
- Set `enable_hats` room option `true` by default
- Allow vcard queries even when IQ queries are disabled ([#4489](https://github.com/processone/ejabberd/issues/4489))
- Announce `stable-id` feature from XEP-0045 1.31, supported since long ago
- Fix `preload_rooms` in case of SQL database ([#4476](https://github.com/processone/ejabberd/issues/4476))
- Run new hooks: `registering_nickmuc` and `registered_nickmuc` ([#4478](https://github.com/processone/ejabberd/issues/4478))
- When deleting account, unregister account's nicks in all MUC hosts ([#4478](https://github.com/processone/ejabberd/issues/4478))
- `mod_muc_log`: Crash in `terminate/2` when stopping module ([#4486](https://github.com/processone/ejabberd/issues/4486))
- `mod_muc_occupantid`: Keep salt per MUC service, not individual rooms
- `mod_muc_room`: Rewrite hats code that gets xdata values
- `mod_muc_room`: Handle hats without definition ([#4503](https://github.com/processone/ejabberd/issues/4503))
- `mod_muc_room`: When user has no hats, don't store in hats_users

#### WebAdmin

- `ejabberd_http`: Run new `http_request_handlers_init` fold hook
- `ejabberd_http`: Add helper `get_auto_urls/2` that returns all URLs and TLS
- `ejabberd_web_admin`: Add helper functions `make_menu_system`
- `ejabberd_web_admin`: Show menu system only when can view vhosts
- `ejabberd_web_admin`: Pass Level in `webadmin_menu_system_post` and `inside` hooks
- `mod_conversejs`: Improve link to conversejs in WebAdmin ([#4495](https://github.com/processone/ejabberd/issues/4495))
- When epmd isn't running show explanation in Clustering WebAdmin page
- Use improved WebAdmin menu system in more modules
- When building WebAdmin menu system, `{URLPATH}` in link text is substituted

#### Web Services

- `rest`: Use separate `httpc` profile
- `ejabberd_captcha`: Use `mod_host_meta:get_auto_url/2`
- `ejabberd_http`: Support repeated module in request_handlers
- `ejabberd_http`: Get back handling when BOSH or WS are disabled
- `mod_host_meta`: Move `get_url` functions from `mod_host_meta` to `ejabberd_http`
- `mod_host_meta`: Allow calling `get_url/2` for other modules, not only WebSocket
- `mod_host_meta`: Cosmetic rename Module to Handler
- `mod_http_upload`: New `content_type` option similar to `mod_http_fileserver` ([#4488](https://github.com/processone/ejabberd/issues/4488))
- `mod_http_upload`: Pass ServerHost, not Host which may be `"upload.HOST"`
- `mod_http_upload`: Amend the fix for #4450 to support IDNA correctly ([#3519](https://github.com/processone/ejabberd/issues/3519))
- `mod_http_fileserver`: Support map of paths in `docroot` option
- `mod_conversejs`: Add new Conversejs Paths and ContentTypes ([#4511](https://github.com/processone/ejabberd/issues/4511))
- `mod_conversejs`: Use ContentType functions from `mod_http_fileserver` ([#4511](https://github.com/processone/ejabberd/issues/4511))
- Use `/websocket` URL in default configuration like `mod_conversejs`, it's more meaningful

#### Core and Modules

- Add `replaced_connection_timeout` toplevel option
- Fix nasty SSL warnings ([#4475](https://github.com/processon4475e/ejabberd/issues/))
- `ejabberd_commands`: Show meaningul error message when problem executing command ([#4506](https://github.com/processone/ejabberd/issues/4506))
- `ejabberd_logger`: Append "color clean" only in console template, not file
- `ejabberd_oauth`: Log error if `oauth_list_tokens` executed with unsupported DB ([#4506](https://github.com/processone/ejabberd/issues/4506))
- `misc`: Get back functions and mark them as deprecated
- `mod_adhoc_api`: Show nice command name, as WebAdmin already does
- `mod_pubsub`: Deliver pubsub notifications to remote servers for nodes with presence based delivery
- `mod_scram_update`: Don't hard-code iteration count
- Bump many XEPs versions that are already supported
- Improve documentation of `install_contrib_modules` ([#4487](https://github.com/processone/ejabberd/issues/4487))

## Version 25.10

#### Ad-hoc Commands

- `mod_configure`: New ad-hoc commands that were missing from XEP-0133
- `mod_adhoc_api`: Add support for asynchronous command calling
- `mod_adhoc_api`: If argument is a list of jids, type is `jid-multi`
- `mod_adhoc_api`: If field has several values, type is `text-multi`

#### API Commands

- Add commands argument type `binary_or_list`
- `mod_http_api`: Format sub elements for tuples from maps
- `mod_admin_extra`: Improve roster API commands documentation
- `mod_announce`: New API commands, reusing existing ad-hoc functions
- `ejabberd_admin`: New API command `restart_kindly`, improve `stop_kindly`
- `mod_admin_extra`: New API commands `list_banned` and `count_banned`
- `mod_admin_extra`: Improve API command `status_list`: support for status to be a list
- `mod_muc_admin`: New API commands `muc_get_registered_nick` and nicks ([#4468](https://github.com/processone/ejabberd/issues/4468))
- Use `mod_private:del_data` in `unban_account` API command

#### Configuration

- Rename `New` SQL schema to `Multihost`, and `Default` to `Singlehost` ([#4456](https://github.com/processone/ejabberd/issues/4456))
- Add config transformer from `use_new_schema` -> `sql_multihost_schema`
- `mod_sip`: Fix problem parsing `via` in `yconf` library ([#4444](https://github.com/processone/ejabberd/issues/4444))

#### Erlang/OTP support

- Enable feature `maybe_expr` in the compiler for Erlang/OTP 26 ([#4459](https://github.com/processone/ejabberd/issues/4459))
- Enable feature `maybe_expr` also in the runtime for Erlang/OTP 25
- Runtime: Remove Erlang 24 which won't work anymore with `maybe_expr`
- Remove `EX_RULE` and `EX_STACK` macros only used with ancient erlang

#### GitHub Workflows

- CI: Bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4469](https://github.com/processone/ejabberd/issues/4469))
- CI: Don't care to include commit details in the CT logs HTML page
- CI and Runtime: Reorganize steps to run in parallel, and ARM runner ([#4460](https://github.com/processone/ejabberd/issues/4460))
- Add local composite actions to manage ejabberd and databases
- Container: Build ARM in native runner instead of QEMU, merge and clean
- Installers: Generate ARM installers in native runner
- Tests: Run agnostic-database tests only once, not for every backend
- Tests: The odbc backend is not actually used in Commont Tests
- Weekly: New workflow that condenses CI, test all erlang without caching

#### Installers and Container

- Bump Erlang/OTP version to 27.3.4.3 in installers and container
- Bump Expat 2.7.3, OpenSSL 3.5.4, unixODBC 2.3.14 in installers

#### MUC

- `mod_mam`: New option `archive_muc_as_mucsub`
- `mod_muc`: Check if room is hibernated before calling mod_muc process
- `mod_muc`: Update implementation of XEP-0317 Hats to version 0.3.1 ([#4380](https://github.com/processone/ejabberd/issues/4380))
- `mod_muc`: Make mod_muc_sql properly handle new hats data ([#4380](https://github.com/processone/ejabberd/issues/4380))
- `mod_muc_room`: Don't require password if user is owner of room
- `mod_muc_admin`: Use in WebAdmin the new API commands that get nick registers

#### Core and Modules

- `ejabberd_http_ws`: Pass HTTP headers from WS to C2S connection ([#4471](https://github.com/processone/ejabberd/issues/4471))
- `ejabberd_listener`: Properly pass `send_timeout` option to listener sockets
- `ejabberdctl`: When ping returns pang, return also status code 1 ([#4327](https://github.com/processone/ejabberd/issues/4327))
- `ext_mod`: Print module status message after installation
- `misc`: json_encode should always call json with our filter
- `mod_admin_update_sql`: Use same index name than when creating database
- `mod_block_strangers`: Clarify `access` and `captcha` documentation ([#4221](https://github.com/processone/ejabberd/issues/4221))
- `mod_http_upload`: Encode URL before parsing, as done before `bba1a1e3c` ([#4450](https://github.com/processone/ejabberd/issues/4450))
- `mod_private`: Add `del_data/3`, `get_users_with_data/2`, `count_users_with_data/2`
- `mod_pubsub`: Don't catch `exit:{aborted, _}` inside mnesia transactions
- `mod_push`: Run new hook `push_send_notification` ([#4383](https://github.com/processone/ejabberd/issues/4383))
- WebAdmin: Respect newline and whitespace characters in results

## Version 25.08

#### API Commands

- `ban_account`: Run `sm_kick_user` event when kicking account ([#4415](https://github.com/processone/ejabberd/issues/4415))
- `ban_account`: No need to change password ([#4415](https://github.com/processone/ejabberd/issues/4415))
- `mnesia_change`: New command in `ejabberdctl` script that helps changing the mnesia node name

#### Configuration

- Rename `auth_password_types_hidden_in_scram1` option to `auth_password_types_hidden_in_sasl1`
- `econf`: If a host in configuration is encoded IDNA, decode it ([#3519](https://github.com/processone/ejabberd/issues/3519))
- `ejabberd_config`: New predefined keyword `HOST_URL_ENCODE`
- `ejabberd.yml.example`: Use `HOST_URL_ENCODE` to handle case when vhost is non-latin1
- `mod_conversejs`: Add option `conversejs_plugins` ([#4413](https://github.com/processone/ejabberd/issues/4413))
- `mod_matrix_gw`: Add `leave_timeout` option ([#4386](https://github.com/processone/ejabberd/issues/4386))

#### Documentation and Tests

- `COMPILE.md`: Mention dependencies and add link to Docs ([#4431](https://github.com/processone/ejabberd/issues/4431))
- `ejabberd_doc`: Document commands tags for modules
- CI: bump XMPP-Interop-Testing/xmpp-interop-tests-action ([#4425](https://github.com/processone/ejabberd/issues/4425))
- Runtime: Raise the minimum Erlang tested to Erlang/OTP 24

#### Installers and Container

- Bump Erlang/OTP version to 27.3.4.2
- Bump OpenSSL version to 3.5.2
- `make-binaries`: Disable Linux-PAM's `logind` support

#### Core and Modules

- Bump `p1_acme` to fix `'AttributePKCS-10'` and OTP 28 ([processone/p1_acme#4](https://github.com/processone/p1_acme/issues/4))
- Prevent loops in `xml_compress:decode` with corrupted data
- `ejabberd_auth_mnesia`: Fix issue with filtering duplicates in `get_users()`
- `ejabberd_listener`: Add secret in temporary unix domain socket path ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_listener`: Log error when cannot set definitive unix socket ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_listener`: Try to create provisional socket in final directory ([#4422](https://github.com/processone/ejabberd/issues/4422))
- `ejabberd_logger`: Print log lines colorized in console when using rebar3
- `mod_conversejs`: Ensure assets_path ends in `/` as required by Converse ([#4414](https://github.com/processone/ejabberd/issues/4414))
- `mod_conversejs`: Ensure plugins URL is separated with `/` ([#4413](https://github.com/processone/ejabberd/issues/4413))
- `mod_http_upload`: Encode URLs into IDNA when showing to XMPP client ([#3519](https://github.com/processone/ejabberd/issues/3519))
- `mod_matrix_gw`: Add support for null values in `is_canonical_json` ([#4421](https://github.com/processone/ejabberd/issues/4421))
- `mod_matrix_gw`: Don't send empty direct Matrix messages ([#4420](https://github.com/processone/ejabberd/issues/4420))
- `mod_matrix_gw`: Matrix gateway updates
- `mod_muc`: Report db failures when restoring rooms
- `mod_muc`: Unsubscribe users from members-only rooms when expelled ([#4412](https://github.com/processone/ejabberd/issues/4412))
- `mod_providers`: New module to serve easily XMPP Providers files
- `mod_register`: Don't duplicate welcome subject and message
- `mod_scram_upgrade`: Fix format of passwords updates
- `mod_scram_upgrade`: Only offer upgrades to methods that aren't already stored

## Version 25.07

#### Security fix

- `ext_mod`: Add temporary workaround for zip including absolute path

#### Compilation

- Raise the minimum Elixir tested version to 1.14.0 ([#4281](https://github.com/processone/ejabberd/issues/4281))
- Raise Erlang/OTP minimum requirement to 25.0 ([#4281](https://github.com/processone/ejabberd/issues/4281))
- `configure.ac`: Allow to specify minimal erlang version using `--with-min-erlang`
- `Makefile.in`: Add target `test-<group>`
- `rebar3-format.sh`: Replace csplit with perl
- Container: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4
- Installers: Bump Erlang/OTP 27.3.4.1, Elixir 1.18.4, libexpat 2.7.1, OpenSSL 3.5.1

#### Configuration and Tests

- Add `rest_proxy*` options to configure proxy used by rest module
- `ejabberd_c2s`: Add `auth_password_types_hidden_in_scram1` option
- `ejabberd_http`: Remove unused `default_host` option and state element
- `ejabberd_http`: New option `hosts_alias` and function `resolve_host_alias/1` ([#4400](https://github.com/processone/ejabberd/issues/4400))
- New predefined keywords: `CONFIG_PATH` and `LOG_PATH`
- Fix macro used in string options when defined in env var
- Use auxiliary function to get `$HOME`, use Mnesia directory when not set ([#4402](https://github.com/processone/ejabberd/issues/4402))
- `ejabberd_config`: Better `lists:uniq` substitute
- Tests: update readme and compose to work with current sw versions
- Update Elvis to 4.1.1, fix some warnings and enable their tests

#### Erlang/OTP 28 support

- Add workaround in `p1_acme` for Jose 1.11.10 not supporting OTP 28 `ecPrivkeyVer1` ([#4393](https://github.com/processone/ejabberd/issues/4393))
- Bump `fast_xml` and `xmpp` for improved Erlang/OTP 28 support
- Bump `xmpp` and `p1_acme` patched with Erlang/OTP 28 support
- Fix `make options` in Erlang/OTP 28 ([#4352](https://github.com/processone/ejabberd/issues/4352))
- Fix crash in `rebar3 cover` with Erlang/OTP 28 ([#4353](https://github.com/processone/ejabberd/issues/4353))
- Rebar/Rebar3: Update binaries to work with Erlang/OTP 25-28 ([#4354](https://github.com/processone/ejabberd/issues/4354))
- CI and Runtime: Add Erlang/OTP 28 to the versions matrix

#### SQL

- Fix mnesia to sql exporter after changes to auth tables
- Update code for switching to new schema type to users table changes
- Add mssql specific implementation of `delete_old_mam_messages`
- Make `delete_old_mam_messages_batch` work with sqlite
- `ejabberd_sm_sql`: Use misc:encode_pid/1
- `mysql.sql`: Fix typo in commit 7862c6a when creating users table
- `pg.sql`: Fix missing comma in postgres schema ([#4409](https://github.com/processone/ejabberd/issues/4409))

#### Core and Modules

- `ejabberd_s2s_in`: Allow S2S connections to accept client certificates that have only server purpose ([#4392](https://github.com/processone/ejabberd/issues/4392))
- `ext_mod`: Recommend to write README.md instead txt (processone/ejabberd-contrib#363)
- `ext_mod`: Support library path installed from Debian (processone/ejabberd-contrib#363)
- `ext_mod`: When upgrading module, clean also the compiled directories
- `gen_mod`: Add support to prepare module stopping before actually stopping any module
- `mod_antispam`: Imported from ejabberd-contrib and improved ([#4373](https://github.com/processone/ejabberd/issues/4373))
- `mod_auth_fast`: Clear tokens on kick, change pass and unregister ([#4397](https://github.com/processone/ejabberd/issues/4397))([#4398](https://github.com/processone/ejabberd/issues/4398))([#4399](https://github.com/processone/ejabberd/issues/4399))
- `mod_conversejs`: Add link in WebAdmin to local Converse if configured
- `mod_mam`: Present mam full text search in xep-431 compatible way
- `mod_mam_mnesia`: Handle objects that don't need conversion in `transform/0`
- `mod_matrix_gw`: Don't send empty messages in Matrix rooms ([#4385](https://github.com/processone/ejabberd/issues/4385))
- `mod_matrix_gw`: Support older Matrix rooms versions starting from version 4
- `mod_matrix_gw`: When encoding JSON, handle term that is key-value list ([#4379](https://github.com/processone/ejabberd/issues/4379))
- `mod_matrix_gw_s2s`: Fix key validation in `check_signature`
- `mod_mix` and `mod_muc_rtbl`: Support list of IDs in `pubsub-items-retract` (processone/xmpp#100)
- `mod_pubsub_serverinfo`: Imported module from ejabberd-contrib ([#4408](https://github.com/processone/ejabberd/issues/4408))
- `mod_register`: Normalize username when determining if user want to change pass
- `mod_register`: Strip query data when returning errors
- WebAdmin: New hooks `webadmin_menu_system` to add items to system menu

## Version 25.04

#### Security fixes
- Fixes issue with handling of user provided occupant-id in messages and presences sent to muc room. Server was replacing
  just first instance of occupant-id with its own version, leaving other ones untouched. That would mean that depending
  on order in which clients send occupant-id, they could see value provided by sender, and that could be used to spoof
  as different sender.

#### Commands API
- `kick_users`: New command to kick all logged users for a given host

#### Bugfixes
- Fix issue with sql schema auto upgrade when using `sqlite` database
- Fix problem with container update, that could ignore previous data stored in `mnesia` database
- Revert limit of allowed characters in shared roster group names, that will again allow using symbols like `:`

## Version 25.03

#### Commands API
- `ejabberdctl`: New option `CTL_OVER_HTTP` ([#4340](https://github.com/processone/ejabberd/issues/4340))
- `ejabberd_web_admin`: Support commands with tuple arguments
- `mod_adhoc_api`: New module to execute API Commands using Ad-Hoc Commands ([#4357](https://github.com/processone/ejabberd/issues/4357))
- `mod_http_api`: Sort list elements in a command result
- Show warning when registering command with an existing name
- Fix commands unregistration
- `change_room_option`: Add forgotten support to set `enable_hats` room option
- `change_room_option`: Verify room option value before setting it ([#4337](https://github.com/processone/ejabberd/issues/4337))
- `create_room_with_opts`: Recommend using `;` and `=` separators
- `list_cluster_detailed`: Fix crash when a node is down
- `mnesia_list_tables`: Allow using this internal command
- `mnesia_table_change_storage`: Allow using this internal command
- `status`: Separate command result with newline
- `update_sql`: Fix updating tables created by ejabberd internally
- `update_sql`: Fix MySQL support

#### Configuration
- `acl`: Fix bug matching the acl `shared_group: NAME`
- `define_keyword`: New option to define keywords ([#4350](https://github.com/processone/ejabberd/issues/4350))
- `define_macro`: Add option to `globals()` because it's useless inside `host_config`
- `ejabberd.yml.example`: Enable `mod_muc_occupantid` by default
- Add support to use keywords in toplevel, listener and modules
- Show warning also when deprecated listener option is set as disabled ([#4345](https://github.com/processone/ejabberd/issues/4345))

#### Container
- Bump versions to Erlang/OTP 27.3 and Elixir 1.18.3
- Add `ERL_FLAGS` to compile elixir on qemu cross-platform
- Copy files to stable path, add ecs backwards compatibility
- Fix warning about relative workdir
- Improve entrypoint script: register account, or set random
- Link path to Mnesia spool dir for backwards compatibility
- Place `sockets/` outside `database/`
- Use again direct METHOD, qemu got fixed ([#4280](https://github.com/processone/ejabberd/issues/4280))
- `ejabberd.yml.example`: Copy main example configuration file
- `ejabberd.yml.example`: Define and use macros in the default configuration file
- `ejabberd.yml.example`: Enable `CTL_OVER_HTTP` by default
- `ejabberd.yml.example`: Listen for webadmin in a port number lower than any other
- `ejabberdapi`: Compile during build
- `CONTAINER.md`: Include documentation for ecs container image

#### Core and Modules
- `ejabberd_auth`: Add support for `auth_stored_password_types`
- `ejabberd_router`: Don't rewrite "self-addressed" privileged IQs as results ([#4348](https://github.com/processone/ejabberd/issues/4348))
- `misc`: Fix json version of `json_encode_with_kv_list` for nested kv lists ([#4338](https://github.com/processone/ejabberd/issues/4338))
- OAuth: Fix crashes when oauth is feed with invalid jid ([#4355](https://github.com/processone/ejabberd/issues/4355))
- PubSub: Bubble up db errors in `nodetree_tree_sql:set_node`
- `mod_configure`: Add option `access` to let configure the access name
- `mod_mix_pam`: Remove `Channels` roster group of mix channels ([#4297](https://github.com/processone/ejabberd/issues/4297))
- `mod_muc`: Document MUC room option vcard_xupdate
- `mod_privilege`: Accept non-privileged IQs from privileged components ([#4341](https://github.com/processone/ejabberd/issues/4341))
- `mod_private`: Improve exception handling
- `mod_private`: Don't warn on conversion errors
- `mod_private`: Handle invalid PEP-native bookmarks
- `mod_private`: Don't crash on invalid bookmarks
- `mod_s2s_bidi`: Stop processing other handlers in s2s_in_handle_info ([#4344](https://github.com/processone/ejabberd/issues/4344))
- `mod_s2s_bidi`: Fix issue with wrong namespace

#### Dependencies
- `ex_doc`: Bump to 0.37.2
- `stringprep`: Bump to 1.0.31
- `provider_asn1`: Bump to 0.4.1
- `xmpp` Bump to bring fix for ssdp hash calculation
- `xmpp` Bump to get support for webchat_url ([#3041](https://github.com/processone/ejabberd/issues/3041))
- `xmpp` Bump to get XEP-0317 Hats namespaces version 0.2.0
- `xmpp` Bump to bring SSDP to XEP version 0.4
- `yconf` Bump to support macro inside string

#### Development and Testing
- `mix.exs`: Keep debug info when building `dev` release
- `mix.exs`: The `ex_doc` dependency is only relevant for the `edoc` Mix environment
- `ext_mod`: add `$libdir/include` to include path
- `ext_mod`: fix greedy include path ([#4359](https://github.com/processone/ejabberd/issues/4359))
- `gen_mod`: Support registering commands and `hook_subscribe` in `start/2` result
- `c2s_handle_bind`: New event in `ejabberd_c2s` ([#4356](https://github.com/processone/ejabberd/issues/4356))
- `muc_disco_info_extras`: New event `mod_muc_room` useful for `mod_muc_webchat_url` ([#3041](https://github.com/processone/ejabberd/issues/3041))
- VSCode: Fix compiling support
- Add tests for config features `define_macro` and `define_keyword`
- Allow test to run using `ct_run`
- Fixes to handle re-running test after `update_sql`
- Uninstall `mod_example` when the tests has finished

#### Documentation
- Add XEPs that are indirectly supported and required by XEP-0479
- Document that XEP-0474 0.4.0 was recently upgraded
- Don't use backtick quotes for ejabberd name
- Fix values allowed in db_type of mod_auth_fast documentation
- Reword explanation about ACL names and definitions
- Update moved or broken URLs in documentation

#### Installers
- Bump Erlang/OTP 27.3 and Elixir 1.18.3
- Bump OpenSSL 3.4.1
- Bump crosstool-NG 1.27.0
- Fix building Termcap and Linux-PAM

#### Matrix Gateway
- Preserve XMPP message IDs in Matrix rooms
- Better Matrix room topic and room roles to MUC conversion, support room aliases in invites
- Add `muc#user` element to presences and an initial empty subject
- Fix `gen_iq_handler:remove_iq_handler` call
- Properly handle IQ requests
- Support Matrix room aliases
- Fix handling of 3PI events

#### Unix Domain Socket
- Add support for socket relative path
- Use `/tmp` for temporary socket, as path is restricted to 107 chars
- Handle unix socket when logging remote client
- When stopping listener, delete Unix Domain Socket file
- `get_auto_url` option: Don't build auto URL if port is unix domain socket ([#4345](https://github.com/processone/ejabberd/issues/4345))

## Version 24.12

#### Miscelanea

- Elixir: support loading Elixir modules for auth ([#4315](https://github.com/processone/ejabberd/issues/4315))
- Environment variables `EJABBERD_MACRO` to define macros
- Fix problem starting ejabberd when first host uses SQL, other one mnesia
- HTTP Websocket: Enable `allow_unencrypted_sasl2` on websockets ([#4323](https://github.com/processone/ejabberd/issues/4323))
- Relax checks for channels bindings for connections using external encryption
- Redis: Add support for unix domain socket ([#4318](https://github.com/processone/ejabberd/issues/4318))
- Redis: Use eredis 1.7.1 from Nordix when using mix/rebar3 and Erlang 21+
- `mod_auth_fast`: New module with support XEP-0484: Fast Authentication Streamlining Tokens
- `mod_http_api`: Fix crash when module not enabled (for example, in CT tests)
- `mod_http_api`: New option `default_version`
- `mod_muc`: Make rsm handling in disco items, correctly count skipped rooms
- `mod_offline`: Only delete offline msgs when user has MAM enabled ([#4287](https://github.com/processone/ejabberd/issues/4287))
- `mod_priviled`: Handle properly roster iq
- `mod_pubsub`: Send notifications on PEP item retract
- `mod_s2s_bidi`: Catch extra case in check for s2s bidi element
- `mod_scram_upgrade`: Don't abort the upgrade
- `mod_shared_roster`: The name of a new group is lowercased
- `mod_shared_roster`: Get back support for `groupid@vhost` in `displayed`
- `mod_stun_disco`: Fix syntax of credentials response

#### Commands API

- Change arguments and result to consistent names (API v3)
- `create_rooms_file`: Improve to support vhosts with different config
- `evacuate_kindly`: New command to kick users and prevent login ([#4309](https://github.com/processone/ejabberd/issues/4309))
- `join_cluster`: Explain that this returns immediately (since 5a34020, 24.06)
- `mod_muc_admin`: Rename argument `name` to `room` for consistency

#### Documentation

- Fix some documentation syntax, add links to toplevel, modules and API
- `CONTAINER.md`: Add kubernetes yaml examples to use with podman
- `SECURITY.md`: Add security policy and reporting guidelines
- `ejabberd.service`: Disable the systemd watchdog by default
- `ejabberd.yml.example`: Use non-standard STUN port

#### WebAdmin

- Shared group names are case sensitive, use original case instead of lowercase
- Use lowercase username and server authentication credentials
- Fix calculation of node's uptime days
- Fix link to displayed group when it is from another vhost

## Version 24.10

#### Miscelanea

- `ejabberd_c2s`: Optionally allow unencrypted SASL2
- `ejabberd_system_monitor`: Handle call by `gen_event:swap_handler` ([#4233](https://github.com/processone/ejabberd/issues/4233))
- `ejabberd_http_ws`: Remove support for old websocket connection protocol
- `ejabberd_stun`: Omit `auth_realm` log message
- `ext_mod`: Handle `info` message when contrib module transfers table ownership
- `mod_block_strangers`: Add feature announcement to disco-info ([#4039](https://github.com/processone/ejabberd/issues/4039))
- `mod_mam`: Advertise XEP-0424 feature in server disco-info ([#3340](https://github.com/processone/ejabberd/issues/3340))
- `mod_muc_admin`: Better handling of malformed jids in `send_direct_invitation` command
- `mod_muc_rtbl`: Fix call to `gen_server:stop` ([#4260](https://github.com/processone/ejabberd/issues/4260))
- `mod_privilege`: Support "IQ permission" from XEP-0356 0.4.1 ([#3889](https://github.com/processone/ejabberd/issues/3889))
- `mod_pubsub`: Don't blindly echo PEP notification
- `mod_pubsub`: Skip non-delivery errors for local pubsub generated notifications
- `mod_pubsub`: Fall back to default plugin options
- `mod_pubsub`: Fix choice of node config defaults
- `mod_pubsub`: Fix merging of default node options
- `mod_pubsub`: Fix default node config parsing
- `mod_register`: Support to block IPs in a vhost using `append_host_config` ([#4038](https://github.com/processone/ejabberd/issues/4038))
- `mod_s2s_bidi`: Add support for S2S Bidirectional
- `mod_scram_upgrade`: Add support for SCRAM upgrade tasks
- `mod_vcard`: Return error stanza when storage doesn't support vcard update ([#4266](https://github.com/processone/ejabberd/issues/4266))
- `mod_vcard`: Return explicit error stanza when user attempts to modify other's vcard
- Minor improvements to support `mod_tombstones` (#2456)
- Update `fast_xml` to use `use_maps` and remove obsolete elixir files
- Update `fast_tls` and `xmpp` to improve s2s fallback for invalid direct tls connections
- `make-binaries`: Bump dependency versions: Elixir 1.17.2, OpenSSL 3.3.2, ...

#### Administration

- `ejabberdctl`: If `ERLANG_NODE` lacks host, add hostname ([#4288](https://github.com/processone/ejabberd/issues/4288))
- `ejabberd_app`: At server start, log Erlang and Elixir versions
- MySQL: Fix column type in the schema update of `archive` table in schema update

#### Commands API

- `get_mam_count`: New command to get number of archived messages for an account
- `set_presence`: Return error when session not found
- `update`: Fix command output
- Add `mam` and `offline` tags to the related purge commands

#### Code Quality

- Fix warnings about unused macro definitions reported by Erlang LS
- Fix Elvis report: Fix dollar space syntax
- Fix Elvis report: Remove spaces in weird places
- Fix Elvis report: Don't use ignored variables
- Fix Elvis report: Remove trailing whitespace characters
- Define the types of options that `opt_type.sh` cannot derive automatically
- `ejabberd_http_ws`: Fix dialyzer warnings
- `mod_matrix_gw`: Remove useless option `persist`
- `mod_privilege`: Replace `try...catch` with a clean alternative

#### Development Help

- `elvis.config`: Fix file syntax, set vim mode, disable many tests
- `erlang_ls.config`: Let it find paths, update to Erlang 26, enable crossref
- `hooks_deps`: Hide false-positive warnings about `gen_mod`
- `Makefile`: Add support for `make elvis` when using rebar3
- `.vscode/launch.json`: Experimental support for debugging with Neovim
- CI: Add Elvis tests
- CI: Add XMPP Interop tests
- Runtime: Cache hex.pm archive from rebar3 and mix

#### Documentation

- Add links in top-level options documentation to their Docs website sections
- Document which SQL servers can really use `update_sql_schema`
- Improve documentation of `ldap_servers` and `ldap_backups` options ([#3977](https://github.com/processone/ejabberd/issues/3977))
- `mod_register`: Document behavior when `access` is set to `none` ([#4078](https://github.com/processone/ejabberd/issues/4078))

#### Elixir

- Handle case when elixir support is enabled but not available
- Start ExSync manually to ensure it's started if (and only if) Relive
- `mix.exs`: Fix `mix release` error: `logger` being regular and included application ([#4265](https://github.com/processone/ejabberd/issues/4265))
- `mix.exs`: Remove from `extra_applications` the apps already defined in `deps` ([#4265](https://github.com/processone/ejabberd/issues/4265))

#### WebAdmin

- Add links in user page to offline and roster pages
- Add new "MAM Archive" page to webadmin
- Improve many pages to handle when modules are disabled
- `mod_admin_extra`: Move some webadmin pages to their modules

## Version 24.07

#### Core

- `ejabberd_options`: Add trailing `@` to `@VERSION@` parsing
- `mod_http_api`: Fix problem parsing tuples when using OTP 27 json library ([#4242](https://github.com/processone/ejabberd/issues/4242))
- `mod_http_api`: Restore args conversion of `{"k":"v"}` to tuple lists
- `mod_matrix_gw`: Add misc:json_encode_With_kv_lists and use it in matrix sign function
- `mod_muc`: Output `muc#roominfo_avatarhash` in room disco info as per updated XEP-0486 ([#4234](https://github.com/processone/ejabberd/issues/4234))
- `mod_muc`: Improve cross version handling of muc retractions
- `node_pep`: Add missing feature `item-ids` to node_pep
- `mod_register`: Send welcome message as `chat` too ([#4246](https://github.com/processone/ejabberd/issues/4246))
- `ejabberd_hooks`: Support for ejabberd hook subscribers, useful for [mod_prometheus](https://github.com/processone/ejabberd-contrib/tree/master/mod_prometheus)
- `ejabberd.app`: Don't add `iex` to included_applications
- `make-installers`: Fix path in scripts in regular user install ([#4258](https://github.com/processone/ejabberd/issues/4258))
- Test: New tests for API commands

#### Documentation

- `mod_matrix_gw`: Fix `matrix_id_as_jid` option documentation
- `mod_register`: Add example configuration of `welcome_message` option
- `mix.exs`: Add ejabberd example config files to the hex package
- Update `CODE_OF_CONDUCT.md`

#### ext_mod

- Fetch dependencies from hex.pm when mix is available
- files_to_path is deprecated, use compile_to_path
- Compile all Elixir files in a library with one function call
- Improve error result when problem compiling elixir file
- Handle case when contrib module has no `*.ex` and no `*.erl`
- `mix.exs`: Include Elixir's Logger in the OTP release, useful for [mod_libcluster](https://github.com/processone/ejabberd-contrib/tree/master/mod_libcluster)

#### Logs

- Print message when starting ejabberd application fails
- Use error_logger when printing startup failure message
- Use proper format depending on the formatter ([#4256](https://github.com/processone/ejabberd/issues/4256))

#### SQL

- Add option `update_sql_schema_timeout` to allow schema update use longer timeouts
- Add ability to specify custom timeout for sql operations
- Allow to configure number of restart in `sql_transaction()`
- Make sql query in testsuite compatible with pg9.1
- In `mysql.sql`, fix update instructions for the `archive` table, `origin_id` column ([#4259](https://github.com/processone/ejabberd/issues/4259))

#### WebAdmin

- `ejabberd.yml.example`: Add `api_permissions` group for webadmin ([#4249](https://github.com/processone/ejabberd/issues/4249))
- Don't use host from url in webadmin, prefer host used for authentication
- Fix number of accounts shown in the online-users page
- Fix crash when viewing old shared roster groups ([#4245](https://github.com/processone/ejabberd/issues/4245))
- Support groupid with spaces when making shared roster result ([#4245](https://github.com/processone/ejabberd/issues/4245))

## Version 24.06

#### Core

- `econf`: Add ability to use additional custom errors when parsing options
- `ejabberd_logger`: Reloading configuration will update logger settings
- `gen_mod`: Add support to specify a hook global, not vhost-specific
- `mod_configure`: Retract `Get User Password` command to update XEP-0133 1.3.0
- `mod_conversejs`: Simplify support for `@HOST@` in `default_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167))
- `mod_mam`: Document that XEP-0441 is implemented as well
- `mod_mam`: Update support for XEP-0425 version 0.3.0, keep supporting 0.2.1 ([#4193](https://github.com/processone/ejabberd/issues/4193))
- `mod_matrix_gw`: Fix support for `@HOST@` in `matrix_domain` option ([#4167](https://github.com/processone/ejabberd/issues/4167))
- `mod_muc_log`: Hide join/leave lines, add method to show them
- `mod_muc_log`: Support `allowpm` introduced in 2bd61ab
- `mod_muc_room`: Use ejabberd hooks instead of function calls to `mod_muc_log` ([#4191](https://github.com/processone/ejabberd/issues/4191))
- `mod_private`): Cope with bookmark decoding errors
- `mod_vcard_xupdate`: Send hash after avatar get set for first time
- `prosody2ejabberd`: Handle the `approved` attribute. As feature isn't implemented, discard it ([#4188](https://github.com/processone/ejabberd/issues/4188))

#### SQL

- `update_sql_schema`: Enable this option by default
- CI: Don't load database schema files for mysql and pgsql
- Support Unix Domain Socket with updated p1_pgsql and p1_mysql ([#3716](https://github.com/processone/ejabberd/issues/3716))
- Fix handling of `mqtt_pub` table definition from `mysql.sql` and fix `should_update_schema/1` in `ejabberd_sql_schema.erl`
- Don't start sql connection pools for unknown hosts
- Add `update_primary_key` command to sql schema updater
- Fix crash running `export2sql` when MAM enabled but MUC disabled
- Improve detection of types in odbc

#### Commands API

- New ban commands use private storage to keep ban information ([#4201](https://github.com/processone/ejabberd/issues/4201))
- `join_cluster_here`: New command to join a remote node into our local cluster
- Don't name integer and string results in API examples ([#4198](https://github.com/processone/ejabberd/issues/4198))
- `get_user_subscriptions`: Fix validation of user field in that command
- `mod_admin_extra`: Handle case when `mod_private` is not enabled ([#4201](https://github.com/processone/ejabberd/issues/4201))
- `mod_muc_admin`: Improve validation of arguments in several commands

#### Compile

- `ejabberdctl`: Comment ERTS_VSN variable when not used ([#4194](https://github.com/processone/ejabberd/issues/4194))
- `ejabberdctl`: Fix iexlive after `make prod` when using Elixir
- `ejabberdctl`: If `INET_DIST_INTERFACE` is IPv6, set required option ([#4189](https://github.com/processone/ejabberd/issues/4189))
- `ejabberdctl`: Make native dynamic node names work when using fully qualified domain names
- `rebar.config.script`: Support relaxed dependency version ([#4192](https://github.com/processone/ejabberd/issues/4192))
- `rebar.config`: Update deps version to rebar3's relaxed versioning
- `rebar.lock`: Track file, now that rebar3 uses loose dependency versioning
- `configure.ac`: When using rebar3, unlock dependencies that are disabled ([#4212](https://github.com/processone/ejabberd/issues/4212))
- `configure.ac`: When using rebar3 with old Erlang, unlock some dependencies ([#4213](https://github.com/processone/ejabberd/issues/4213))
- `mix:exs`: Move `xmpp` from `included_applications` to `applications`

#### Dependencies

- Base64url: Use only when using rebar2 and Erlang lower than 24
- Idna: Bump from 6.0.0 to 6.1.1
- Jiffy: Use Json module when Erlang/OTP 27, jiffy with older ones
- Jose: Update to the new 1.11.10 for Erlang/OTP higher than 23
- Luerl: Update to 1.2.0 when OTP same or higher than 20, simplifies commit a09f222
- P1_acme: Update to support Jose 1.11.10 and Ipv6 support ([#4170](https://github.com/processone/ejabberd/issues/4170))
- P1_acme: Update to use Erlang's json library instead of jiffy when OTP 27
- Port_compiler: Update to 1.15.0 that supports Erlang/OTP 27.0

#### Development Help

- `.gitignore`: Ignore ctags/etags files
- `make dialyzer`: Add support to run Dialyzer with Mix
- `make format|indent`: New targets to format and indent source code
- `make relive`: Add Sync tool with Rebar3, ExSync with Mix
- `hook_deps`: Use precise name: hooks are added and later deleted, not removed
- `hook_deps`: Fix to handle FileNo as tuple `{FileNumber, CharacterPosition}`
- Add support to test also EUnit suite
- Fix `code:lib_dir` call to work with Erlang/OTP 27.0-rc2
- Set process flags when Erlang/OTP 27 to help debugging
- Test retractions in mam_tests

#### Documentation

- Add some XEPs support that was forgotten
- Fix documentation links to new URLs generated by MkDocs
- Remove `...` in example configuration: it is assumed and reduces verbosity
- Support for version note in modules too
- Mark toplevel options, commands and modules that changed in latest version
- Now modules themselves can have version annotations in `note`

#### Installers and Container

- make-binaries: Bump Erlang/OTP to 26.2.5 and Elixir 1.16.3
- make-binaries: Bump OpenSSL to 3.3.1
- make-binaries: Bump Linux-PAM to 1.6.1
- make-binaries: Bump Expat to 2.6.2
- make-binaries: Revert temporarily an OTP commit that breaks MSSQL ([#4178](https://github.com/processone/ejabberd/issues/4178))
- CONTAINER.md: Invalid `CTL_ON_CREATE` usage in docker-compose example

#### WebAdmin

- ejabberd_ctl: Improve parsing of commas in arguments
- ejabberd_ctl: Fix output of UTF-8-encoded binaries
- WebAdmin: Remove webadmin_view for now, as commands allow more fine-grained permissions
- WebAdmin: Unauthorized response: include some text to direct to the logs
- WebAdmin: Improve home page
- WebAdmin: Sort alphabetically the menu items, except the most used ones
- WebAdmin: New login box in the left menu bar
- WebAdmin: Add make_command functions to produce HTML command element
- Document 'any' argument and result type, useful for internal commands
- Commands with 'internal' tag: don't list and block execution by frontends
- WebAdmin: Move content to commands; new pages; hook changes; new commands

## Version 24.02

#### Core:

- Added Matrix gateway in `mod_matrix_gw`
- Support SASL2 and Bind2
- Support tls-server-end-point channel binding and sasl2 codec
- Support tls-exporter channel binding
- Support XEP-0474: SASL SCRAM Downgrade Protection
- Fix presenting features and returning results of inline bind2 elements
- `disable_sasl_scram_downgrade_protection`: New option to disable XEP-0474
- `negotiation_timeout`: Increase default value from 30s to 2m
- mod_carboncopy: Teach how to interact with bind2 inline requests

#### Other:

- ejabberdctl: Fix startup problem when having set `EJABBERD_OPTS` and logger options
- ejabberdctl: Set EJABBERD_OPTS back to `""`, and use previous flags as example
- eldap: Change logic for `eldap tls_verify=soft` and `false`
- eldap: Don't set `fail_if_no_peer_cert` for eldap ssl client connections
- Ignore hints when checking for chat states
- mod_mam: Support XEP-0424 Message Retraction
- mod_mam: Fix XEP-0425: Message Moderation with SQL storage
- mod_ping: Support XEP-0198 pings when stream management is enabled
- mod_pubsub: Normalize pubsub `max_items` node options on read
- mod_pubsub: PEP nodetree: Fix reversed logic in node fixup function
- mod_pubsub: Only care about PEP bookmarks options when creating node from scratch

#### SQL:

- MySQL: Support `sha256_password` auth plugin
- ejabberd_sql_schema: Use the first unique index as a primary key
- Update SQL schema files for MAM's XEP-0424
- New option [`sql_flags`](https://docs.ejabberd.im/admin/configuration/toplevel/#sql-flags): right now only useful to enable `mysql_alternative_upsert`

#### Installers and Container:

- Container: Add ability to ignore failures in execution of `CTL_ON_*` commands
- Container: Update to Erlang/OTP 26.2, Elixir 1.16.1 and Alpine 3.19
- Container: Update this custom ejabberdctl to match the main one
- make-binaries: Bump OpenSSL 3.2.1, Erlang/OTP 26.2.2, Elixir 1.16.1
- make-binaries: Bump many dependency versions

#### Commands API:

- `print_sql_schema`: New command available in ejabberdctl command-line script
- ejabberdctl: Rework temporary node name generation
- ejabberdctl: Print argument description, examples and note in help
- ejabberdctl: Document exclusive ejabberdctl commands like all the others
- Commands: Add a new `muc_sub` tag to all the relevant commands
- Commands: Improve syntax of many commands documentation
- Commands: Use list arguments in many commands that used separators
- Commands: `set_presence`: switch priority argument from string to integer
- ejabberd_commands: Add the command API version as [a tag `vX`](https://docs.ejabberd.im/developer/ejabberd-api/admin-tags/#v1)
- ejabberd_ctl: Add support for list and tuple arguments
- ejabberd_xmlrpc: Fix support for restuple error response
- mod_http_api: When no specific API version is requested, use the latest

#### Compilation with Rebar3/Elixir/Mix:
- Fix compilation with Erlang/OTP 27: don't use the reserved word 'maybe'
- configure: Fix explanation of `--enable-group` option ([#4135](https://github.com/processone/ejabberd/issues/4135))
- Add observer and runtime_tools in releases when `--enable-tools`
- Update "make translations" to reduce build requirements
- Use Luerl 1.0 for Erlang 20, 1.1.1 for 21-26, and temporary fork for 27
- Makefile: Add `install-rel` and `uninstall-rel`
- Makefile: Rename `make rel` to `make prod`
- Makefile: Update `make edoc` to use ExDoc, requires mix
- Makefile: No need to use `escript` to run rebar|rebar3|mix
- configure: If `--with-rebar=rebar3` but rebar3 not system-installed, use local one
- configure: Use Mix or Rebar3 by default instead of Rebar2 to compile ejabberd
- ejabberdctl: Detect problem running iex or etop and show explanation
- Rebar3: Include Elixir files when making a release
- Rebar3: Workaround to fix protocol consolidation
- Rebar3: Add support to compile Elixir dependencies
- Rebar3: Compile explicitly our Elixir files when `--enable-elixir`
- Rebar3: Provide proper path to `iex`
- Rebar/Rebar3: Update binaries to work with Erlang/OTP 24-27
- Rebar/Rebar3: Remove Elixir as a rebar dependency
- Rebar3/Mix: If `dev` profile/environment, enable tools automatically
- Elixir: Fix compiling ejabberd as a dependency ([#4128](https://github.com/processone/ejabberd/issues/4128))
- Elixir: Fix ejabberdctl start/live when installed
- Elixir: Fix: `FORMATTER ERROR: bad return value` ([#4087](https://github.com/processone/ejabberd/issues/4087))
- Elixir: Fix: Couldn't find file `Elixir Hex API`
- Mix: Enable stun by default when `vars.config` not found
- Mix: New option `vars_config_path` to set path to `vars.config` ([#4128](https://github.com/processone/ejabberd/issues/4128))
- Mix: Fix ejabberdctl iexlive problem locating iex in an OTP release

## Version 23.10

#### Compilation:

- Erlang/OTP: Raise the requirement to Erlang/OTP 20.0 as a minimum
- CI: Update tests to Erlang/OTP 26 and recent Elixir
- Move Xref and Dialyzer options from workflows to `rebar.config`
- Add sections to `rebar.config` to organize its content
- Dialyzer dirty workarounds because `re:mp()` is not an exported type
- When installing module already configured, keep config as example
- Elixir 1.15 removed support for `--app`
- Elixir: Improve support to stop external modules written in Elixir
- Elixir: Update syntax of function calls as recommended by Elixir compiler
- Elixir: When building OTP release with mix, keep `ERLANG_NODE=ejabberd@localhost`
- `ejabberdctl`: Pass `ERLANG_OPTS` when calling `erl` to parse the `INET_DIST_INTERFACE` ([#4066](https://github.com/processone/ejabberd/issues/#4066)

#### Commands:

- `create_room_with_opts`: Fix typo and move examples to `args_example` ([#4080](https://github.com/processone/ejabberd/issues/#4080))
- `etop`: Let `ejabberdctl etop` work in a release (if `observer` application is available)
- `get_roster`: Command now returns groups in a list instead of newlines ([#4088](https://github.com/processone/ejabberd/issues/#4088))
- `halt`: New command to halt ejabberd abruptly with an error status code
- `ejabberdctl`: Fix calling ejabberdctl command with wrong number of arguments with Erlang 26
- `ejabberdctl`: Improve printing lists in results
- `ejabberdctl`: Support `policy=user` in the help and return proper arguments
- `ejabberdctl`: Document how to stop a debug shell: control+g

#### Container:

- Dockerfile: Add missing dependency for mssql databases
- Dockerfile: Reorder stages and steps for consistency
- Dockerfile: Use Alpine as base for `METHOD=package`
- Dockerfile: Rename packages to improve compatibility
- Dockerfile: Provide specific OTP and elixir vsn for direct compilation
- Halt ejabberd if a command in `CTL_ON_` fails during ejabberd startup

#### Core:

- `auth_external_user_exists_check`: New option ([#3377](https://github.com/processone/ejabberd/issues/#3377))
- `gen_mod`: Extend `gen_mod` API to simplify hooks and IQ handlers registration
- `gen_mod`: Add shorter forms for `gen_mod` hook/`iq_handler` API
- `gen_mod`: Update modules to the new `gen_mod` API
- `install_contrib_modules`: New option to define contrib modules to install automatically
- `unix_socket`: New listener option, useful when setting unix socket files ([#4059](https://github.com/processone/ejabberd/issues/#4059))
- `ejabberd_systemd`: Add a few debug messages
- `ejabberd_systemd`: Avoid using `gen_server` timeout ([#4054](https://github.com/processone/ejabberd/issues/#4054))([#4058](https://github.com/processone/ejabberd/issues/#4058))
- `ejabberd_listener`: Increase default listen queue backlog value to 128, which is the default value on both Linux and FreeBSD ([#4025](https://github.com/processone/ejabberd/issues/#4025))
- OAuth: Handle `badpass` error message
- When sending message on behalf of user, trigger `user_send_packet` ([#3990](https://github.com/processone/ejabberd/issues/#3990))
- Web Admin: In roster page move the `AddJID` textbox to top ([#4067](https://github.com/processone/ejabberd/issues/#4067))
- Web Admin: Show a warning when visiting webadmin with non-privileged account ([#4089](https://github.com/processone/ejabberd/issues/#4089))

#### Docs:

- Example configuration: clarify 5223 tls options; specify s2s shaper
- Make sure that `policy=user` commands have `host` instead of `server` arg in docs
- Improve syntax of many command descriptions for the Docs site
- Move example Perl extauth script from ejabberd git to Docs site
- Remove obsolete example files, and add link in Docs to the archived copies

#### Installers (`make-binaries`):
- Bump Erlang/OTP version to 26.1.1, and other dependencies
- Remove outdated workaround
- Don't build Linux-PAM examples
- Fix check for current Expat version
- Apply minor simplifications
- Don't duplicate config entries
- Don't hard-code musl version
- Omit unnecessary glibc setting
- Set kernel version for all builds
- Let curl fail on HTTP errors

#### Modules:

- `mod_muc_log`: Add trailing backslash to URLs shown in disco info
- `mod_muc_occupantid`: New module with support for XEP-0421 Occupant Id ([#3397](https://github.com/processone/ejabberd/issues/#3397))
- `mod_muc_rtbl`: Better error handling in ([#4050](https://github.com/processone/ejabberd/issues/#4050))
- `mod_private`: Add support for XEP-0402 PEP Native Bookmarks
- `mod_privilege`: Don't fail to edit roster ([#3942](https://github.com/processone/ejabberd/issues/#3942))
- `mod_pubsub`: Fix usage of `plugins` option, which produced `default_node_config` ignore ([#4070](https://github.com/processone/ejabberd/issues/#4070))
- `mod_pubsub`: Add `pubsub_delete_item` hook
- `mod_pubsub`: Report support of `config-node-max` in pep
- `mod_pubsub`: Relay pubsub iq queries to muc members without using bare jid ([#4093](https://github.com/processone/ejabberd/issues/#4093))
- `mod_pubsub`: Allow pubsub node owner to overwrite items published by other persons
- `mod_push_keepalive`: Delay `wake_on_start`
- `mod_push_keepalive`: Don't let hook crash
- `mod_push`: Add `notify_on` option
- `mod_push`: Set `last-message-sender` to bare JID
- `mod_register_web`: Make redirect to page that end with `/` ([#3177](https://github.com/processone/ejabberd/issues/#3177))
- `mod_shared_roster_ldap`: Don't crash in `get_member_jid` on empty output ([#3614](https://github.com/processone/ejabberd/issues/#3614))

#### MUC:

- Add support to register nick in a room ([#3455](https://github.com/processone/ejabberd/issues/#3455))
- Convert `allow_private_message` MUC room option to `allowpm` ([#3736](https://github.com/processone/ejabberd/issues/#3736))
- Update xmpp version to send `roomconfig_changesubject` in disco#info ([#4085](https://github.com/processone/ejabberd/issues/#4085))
- Fix crash when loading room from DB older than ffa07c6, 23.04
- Fix support to retract a MUC room message
- Don't always store messages passed through `muc_filter_message` ([#4083](https://github.com/processone/ejabberd/issues/#4083))
- Pass also MUC room retract messages over the `muc_filter_message` ([#3397](https://github.com/processone/ejabberd/issues/#3397))
- Pass MUC room private messages over the `muc_filter_message` too ([#3397](https://github.com/processone/ejabberd/issues/#3397))
- Store the subject author JID, and run `muc_filter_message` when sending subject ([#3397](https://github.com/processone/ejabberd/issues/#3397))
- Remove existing role information for users that are kicked from room ([#4035](https://github.com/processone/ejabberd/issues/#4035))
- Expand rule "mucsub subscribers are members in members only rooms" to more places

#### SQL:

- Add ability to force alternative upsert implementation in mysql
- Properly parse mysql version even if it doesn't have type tag
- Use prepared statement with mysql
- Add alternate version of mysql upsert
- `ejabberd_auth_sql`: Reset scram fields when setting plain password
- `mod_privacy_sql`: Fix return values from `calculate_diff`
- `mod_privacy_sql`: Optimize `set_list`
- `mod_privacy_sql`: Use more efficient way to calculate changes in `set_privacy_list`

## Version 23.04

#### General:

- New `s2s_out_bounce_packet` hook
- Re-allow anonymous connection for connection without client certificates ([#3985](https://github.com/processone/ejabberd/issues/3985))
- Stop `ejabberd_system_monitor` before stopping node
- `captcha_url` option now accepts `auto` value, and it's the default
- `mod_mam`: Add support for XEP-0425: Message Moderation
- `mod_mam_sql`: Fix problem with results of mam queries using rsm with max and before
- `mod_muc_rtbl`: New module for Real-Time Block List for MUC rooms ([#4017](https://github.com/processone/ejabberd/issues/4017))
- `mod_roster`: Set roster name from XEP-0172, or the stored one ([#1611](https://github.com/processone/ejabberd/issues/1611))
- `mod_roster`: Preliminary support to store extra elements in subscription request ([#840](https://github.com/processone/ejabberd/issues/840))
- `mod_pubsub`: Pubsub xdata fields `max_item/item_expira/children_max` use `max` not `infinity`
- `mod_vcard_xupdate`: Invalidate `vcard_xupdate` cache on all nodes when vcard is updated

#### Admin:

- `ext_mod`: Improve support for loading `*.so` files from `ext_mod` dependencies
- Improve output in `gen_html_doc_for_commands` command
- Fix ejabberdctl output formatting ([#3979](https://github.com/processone/ejabberd/issues/3979))
- Log HTTP handler exceptions

#### MUC:

- New command `get_room_history`
- Persist `none` role for outcasts
- Try to populate room history from mam when unhibernating
- Make `mod_muc_room:set_opts` process persistent flag first
- Allow passing affiliations and subscribers to `create_room_with_opts` command
- Store state in db in `mod_muc:create_room()`
- Make subscribers members by default

#### SQL schemas:

- Fix a long standing bug in new schema migration
- `update_sql` command: Many improvements in new schema migration
- `update_sql` command: Add support to migrate MySQL too
- Change PostgreSQL SERIAL to BIGSERIAL columns
- Fix minor SQL schema inconsistencies
- Remove unnecessary indexes
- New SQL schema migrate fix

#### MS SQL:

- MS SQL schema fixes
- Add `new` schema for MS SQL
- Add MS SQL support for new schema migration
- Minor MS SQL improvements
- Fix MS SQL error caused by `ORDER BY` in subquery

#### SQL Tests:

- Add support for running tests on MS SQL
- Add ability to run tests on upgraded DB
- Un-deprecate `ejabberd_config:set_option/2`
- Use python3 to run `extauth.py` for tests
- Correct README for creating test docker MS SQL DB
- Fix TSQLlint warnings in MSSQL test script

#### Testing:

- Fix Shellcheck warnings in shell scripts
- Fix Remark-lint warnings
- Fix Prospector and Pylint warnings in test `extauth.py`
- Stop testing ejabberd with Erlang/OTP 19.3, as Github Actions no longer supports ubuntu-18.04
- Test only with oldest OTP supported (20.0), newest stable (25.3) and bleeding edge (26.0-rc2)
- Upload Common Test logs as artifact in case of failure

#### `ecs` container image:
- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14
- Add `tini` as runtime init
- Set `ERLANG_NODE` fixed to `ejabberd@localhost`
- Upload images as artifacts to Github Actions
- Publish tag images automatically to ghcr.io

#### `ejabberd` container image:
- Update Alpine to 3.17 to get Erlang/OTP 25 and Elixir 1.14
- Add `METHOD` to build container using packages ([#3983](https://github.com/processone/ejabberd/issues/3983))
- Add `tini` as runtime init
- Detect runtime dependencies automatically
- Remove unused Mix stuff: ejabberd script and static COOKIE
- Copy captcha scripts to `/opt/ejabberd-*/lib` like the installers
- Expose only `HOME` volume, it contains all the required subdirs
- ejabberdctl: Don't use `.../releases/COOKIE`, it's no longer included

#### Installers:

- make-binaries: Bump versions, e.g. erlang/otp to 25.3
- make-binaries: Fix building with erlang/otp v25.x
- make-packages: Fix for installers workflow, which didn't find lynx

## Version 23.01

#### General:

- Add `misc:uri_parse/2` to allow declaring default ports for protocols
- CAPTCHA: Add support to define module instead of path to script
- Clustering: Handle `mnesia_system_event mnesia_up` when other node joins this ([#3842](https://github.com/processone/ejabberd/issues/3842))
- ConverseJS: Don't set i18n option because Converse enforces it instead of browser lang ([#3951](https://github.com/processone/ejabberd/issues/3951))
- ConverseJS: Try to redirect access to files `mod_conversejs` to CDN when there is no local copies
- ext_mod: compile C files and install them in ejabberd's `priv`
- ext_mod: Support to get module status from Elixir modules
- make-binaries: reduce log output
- make-binaries: Bump zlib version to 1.2.13
- MUC: Don't store mucsub presence events in offline storage
- MUC: `hibernation_time` is not an option worth storing in room state ([#3946](https://github.com/processone/ejabberd/issues/3946))
- Multicast: Jid format when `multicastc` was cached ([#3950](https://github.com/processone/ejabberd/issues/3950))
- mysql: Pass `ssl` options to mysql driver
- pgsql: Do not set `standard_conforming_strings` to `off` ([#3944](https://github.com/processone/ejabberd/issues/3944))
- OAuth: Accept `jid` as a HTTP URL query argument
- OAuth: Handle when client is not identified
- PubSub: Expose the `pubsub#type` field in `disco#info` query to the node ([#3914](https://github.com/processone/ejabberd/issues/3914))
- Translations: Update German translation

#### Admin:

- `api_permissions`: Fix option crash when doesn't have `who:` section
- `log_modules_fully`: New option to list modules that will log everything
- `outgoing_s2s_families`: Changed option's default to IPv6, and fall back to IPv4
- Fix bash completion when using Relive or other install methods
- Fix portability issue with some shells ([#3970](https://github.com/processone/ejabberd/issues/3970))
- Allow admin command to subscribe new users to `members_only` rooms
- Use alternative `split/2` function that works with Erlang/OTP as old as 19.3
- Silent warning in OTP24 about not specified `cacerts` in SQL connections
- Fix compilation warnings with Elixir 1.14

#### DOAP:

- Support extended `-protocol` erlang attribute
- Add extended RFCs and XEP details to some protocol attributes
- `tools/generate-doap.sh`: New script to generate DOAP file, add `make doap` ([#3915](https://github.com/processone/ejabberd/issues/3915))
- `ejabberd.doap`: New DOAP file describing ejabberd supported protocols

#### MQTT:

- Add MQTT bridge module
- Add support for certificate authentication in MQTT bridge
- Implement reload in MQTT bridge
- Add support for websockets to MQTT bridge
- Recognize ws5/wss5 urls in MQTT bridge
- `mqtt_publish`: New hook for MQTT publish event
- `mqtt_(un)subscribe`: New hooks for MQTT subscribe & unsubscribe events

#### VSCode:

- Improve `.devcontainer` to use use devcontainer image and `.vscode`
- Add `.vscode` files to instruct VSCode how to run ejabberd
- Add Erlang LS default configuration
- Add Elvis default configuration

## Version 22.10

#### Core:

- Add `log_burst_limit_*` options ([#3865](https://github.com/processone/ejabberd/issues/3865))
- Support `ERL_DIST_PORT` option to work without epmd
- Auth JWT: Catch all errors from `jose_jwt:verify` and log debugging details ([#3890](https://github.com/processone/ejabberd/issues/3890))
- CAPTCHA: Support `@VERSION@` and `@SEMVER@` in `captcha_cmd` option ([#3835](https://github.com/processone/ejabberd/issues/3835))
- HTTP: Fix unix socket support ([#3894](https://github.com/processone/ejabberd/issues/3894))
- HTTP: Handle invalid values in `X-Forwarded-For` header more gracefuly
- Listeners: Let module take over socket
- Listeners: Don't register listeners that failed to start in config reload
- `mod_admin_extra`: Handle empty roster group names
- `mod_conversejs`: Fix crash when mod_register not enabled ([#3824](https://github.com/processone/ejabberd/issues/3824))
- `mod_host_meta`: Complain at start if listener is not encrypted
- `mod_ping`: Fix regression on `stop_ping` in clustering context ([#3817](https://github.com/processone/ejabberd/issues/3817))
- `mod_pubsub`: Don't crash on command failures
- `mod_shared_roster`: Fix cache invalidation
- `mod_shared_roster_ldap`: Update roster_get hook to use `#roster_item{}`
- `prosody2ejabberd`: Fix parsing of scram password from prosody

#### MIX:

- Fix MIX's filter_nodes
- Return user jid on join
- `mod_mix_pam`: Add new MIX namespaces to disco features
- `mod_mix_pam`: Add handling of IQs with newer MIX namespaces
- `mod_mix_pam`: Do roster pushes on join/leave
- `mod_mix_pam`: Parse sub elements of the mix join remote result
- `mod_mix_pam`: Provide MIX channels as roster entries via hook
- `mod_mix_pam`: Display joined channels on webadmin page
- `mod_mix_pam`: Adapt to renaming of `participant-id` from mix_roster_channel record
- `mod_roster`: Change hook type from `#roster{}` to `#roster_item{}`
- `mod_roster`: Respect MIX `<annotate/>` setting
- `mod_roster`: Adapt to change of mix_annotate type to boolean in roster_query
- `mod_shared_roster`: Fix wrong hook type `#roster{}` (now `#roster_item{}`)

#### MUC:

- Store role, and use it when joining a moderated room ([#3330](https://github.com/processone/ejabberd/issues/3330))
- Don't persist `none` role ([#3330](https://github.com/processone/ejabberd/issues/3330))
- Allow MUC service admins to bypass max_user_conferences limitation
- Show allow_query_users room option in disco info ([#3830](https://github.com/processone/ejabberd/issues/3830))
- mod_muc_room: Don't set affiliation to `none` if it's already `none` in `process_item_change/3`
- Fix mucsub unsubscribe notification payload to have muc_unsubcribe in it
- Allow muc_{un}subscribe hooks to modify sent packets
- Pass room state to muc_{un}subscribed hook
- The archive_msg export fun requires MUC Service for room archives
- Export `mod_muc_admin:get_room_pid/2`
- Export function for getting room diagnostics

#### SQL:

- Handle errors reported from begin/commit inside transaction
- Make connection close errors bubble up from inside sql transaction
- Make first sql reconnect wait shorter time
- React to sql driver process exit earlier
- Skip connection exit message when we triggered reconnection
- Add syntax_tools to applications, required when using ejabberd_sql_pt ([#3869](https://github.com/processone/ejabberd/issues/3869))
- Fix mam delete_old_messages_batch for sql backend
- Use `INSERT ... ON DUPLICATE KEY UPDATE` for upsert on mysql
- Update mysql library
- Catch mysql connection being close earlier

#### Build:

- `make all`: Generate start scripts here, not in `make install` ([#3821](https://github.com/processone/ejabberd/issues/3821))
- `make clean`: Improve this and "distclean"
- `make deps`: Ensure deps configuration is ran when getting deps ([#3823](https://github.com/processone/ejabberd/issues/3823))
- `make help`: Update with recent changes
- `make install`: Don't leak DESTDIR in files copied by 'make install'
- `make options`: Fix error reporting on OTP24+
- `make update`: configure also in this case, similarly to `make deps`
- Add definition to detect OTP older than 25, used by ejabberd_auth_http
- Configure eimp with mix to detect image convert properly ([#3823](https://github.com/processone/ejabberd/issues/3823))
- Remove unused macro definitions detected by rebar3_hank
- Remove unused header files which content is already in xmpp library

#### Container:

- Get ejabberd-contrib sources to include them
- Copy `.ejabberd-modules` directory if available
- Do not clone repo inside container build
- Use `make deps`, which performs additional steps ([#3823](https://github.com/processone/ejabberd/issues/3823))
- Support `ERL_DIST_PORT` option to work without epmd
- Copy `ejabberd-docker-install.bat` from docker-ejabberd git and rename it
- Set a less frequent healthcheck to reduce CPU usage ([#3826](https://github.com/processone/ejabberd/issues/3826))
- Fix build instructions, add more podman examples

#### Installers:

- make-binaries: Include CAPTCHA script with release
- make-binaries: Edit rebar.config more carefully
- make-binaries: Fix linking of EIMP dependencies
- make-binaries: Fix GitHub release version checks
- make-binaries: Adjust Mnesia spool directory path
- make-binaries: Bump Erlang/OTP version to 24.3.4.5
- make-binaries: Bump Expat and libpng versions
- make-packages: Include systemd unit with RPM
- make-packages: Fix permissions on RPM systems
- make-installers: Support non-root installation
- make-installers: Override code on upgrade
- make-installers: Apply cosmetic changes

#### External modules:

- ext_mod: Support managing remote nodes in the cluster
- ext_mod: Handle correctly when COMMIT.json not found
- Don't bother with COMMIT.json user-friendly feature in automated user case
- Handle not found COMMIT.json, for example in GH Actions
- Add WebAdmin page for managing external modules

#### Workflows Actions:

- Update workflows to Erlang 25
- Update workflows: Ubuntu 18 is deprecated and 22 is added
- CI: Remove syntax_tools from applications, as fast_xml fails Dialyzer
- Runtime: Add Xref options to be as strict as CI

## Version 22.05

#### Core
- C2S: Don't expect that socket will be available in `c2s_terminated` hook
- Event handling process hook tracing
- Guard against `erlang:system_info(logical_processors)` not always returning a number
- `domain_balancing`: Allow for specifying `type` only, without specifying `component_number`

#### MQTT
- Add TLS certificate authentication for MQTT connections
- Fix login when generating client id, keep connection record (#3593)
- Pass property name as expected in mqtt_codec (fixes login using MQTT 5)
- Support MQTT subscriptions spread over the cluster (#3750)

#### MUC
- Attach meta field with real jid to mucsub subscription events
- Handle user removal
- Stop empty MUC rooms 30 seconds after creation
- `default_room_options`: Update options configurable
- `subscribe_room_many_max_users`: New option in `mod_muc_admin`

#### mod_conversejs
- Improved options to support `@HOST@` and `auto` values
- Set `auth` and `register` options based on ejabberd configuration
- `conversejs_options`: New option
- `conversejs_resources`: New option

#### PubSub
- `mod_pubsub`: Allow for limiting `item_expire` value
- `mod_pubsub`: Unsubscribe JID on whitelist removal
- `node_pep`: Add config-node and multi-items features (#3714)

#### SQL
- Improve compatibility with various db engine versions
- Sync old-to-new schema script with reality (#3790)
- Slight improvement in MSSQL testing support, but not yet complete

#### Other Modules
- `auth_jwt`: Checking if an user is active in SM for a JWT authenticated user (#3795)
- `mod_configure`: Implement Get List of Registered/Online Users from XEP-0133
- `mod_host_meta`: New module to serve host-meta files, see XEP-0156
- `mod_mam`: Store all mucsub notifications not only message notifications
- `mod_ping`: Delete ping timer if resource is gone after the ping has been sent
- `mod_ping`: Don't send ping if resource is gone
- `mod_push`: Fix notifications for pending sessions (XEP-0198)
- `mod_push`: Keep push session ID on session resume
- `mod_shared_roster`: Adjust special group cache size
- `mod_shared_roster`: Normalize JID on unset_presence (#3752)
- `mod_stun_disco`: Fix parsing of IPv6 listeners

#### Dependencies
- autoconf: Supported from 2.59 to the new 2.71
- fast_tls: Update to 1.1.14 to support OpenSSL 3
- jiffy: Update to 1.1.1 to support Erlang/OTP 25.0-rc1
- luerl: Update to 1.0.0, now available in hex.pm
- lager: This dependency is used only when Erlang is older than 22
- rebar2: Updated binary to work from Erlang/OTP 22 to 25
- rebar3: Updated binary to work from Erlang/OTP 22 to 25
- `make update`: Fix when used with rebar 3.18

#### Compile
- `mix release`: Copy `include/` files for ejabberd, deps and otp, in `mix.exs`
- `rebar3 release`: Fix ERTS path in `ejabberdctl`
- `configure.ac`: Set default ejabberd version number when not using git
- `mix.exs`: Move some dependencies as optional
- `mix.exs`: No need to use Distillery, Elixir has built-in support for OTP releases (#3788)
- `tools/make-binaries`: New script for building Linux binaries
- `tools/make-installers`: New script for building command line installers

#### Start
- New `make relive` similar to `ejabberdctl live` without installing
- `ejabberdctl`: Fix some warnings detected by ShellCheck
- `ejabberdctl`: Mention in the help: `etop`, `ping` and `started`/`stopped`
- `make rel`: Switch to paths: `conf/`, `database/`, `logs/`
- `mix.exs`: Add `-boot` and `-boot_var` in `ejabberdctl` instead of adding `vm.args`
- `tools/captcha.sh`: Fix some warnings detected by ShellCheck

#### Commands
- Accept more types of ejabberdctl commands arguments as JSON-encoded
- `delete_old_mam_messages_batch`: New command with rate limit
- `delete_old_messages_batch`: New command with rate limit
- `get_room_occupants_number`: Don't request the whole MUC room state (#3684, #1964)
- `get_vcard`: Add support for MUC room vCard
- `oauth_revoke_token`: Add support to work with all backends
- `room_unused_*`: Optimize commands in SQL by reusing `created_at`
- `rooms_unused_...`: Let `get_all_rooms` handle `global` argument (#3726)
- `stop|restart`: Terminate ejabberd_sm before everything else to ensure sessions closing (#3641)
- `subscribe_room_many`: New command

#### Translations
- Updated Catalan
- Updated French
- Updated German
- Updated Portuguese
- Updated Portuguese (Brazil)
- Updated Spanish

#### Workflows
- CI: Publish CT logs and Cover on failure to an external GH Pages repo
- CI: Test shell scripts using ShellCheck (#3738)
- Container: New workflow to build and publish containers
- Installers: Add job to create draft release
- Installers: New workflow to build binary packages
- Runtime: New workflow to test compilation, rel, starting and ejabberdctl

## Version 21.12

#### Commands
- `create_room_with_opts`: Fixed when using SQL storage
- `change_room_option`: Add missing fields from config inside `mod_muc_admin:change_options`
- piefxis: Fixed arguments of all commands

#### Modules
- mod_caps: Don't forget caps on XEP-0198 resumption
- mod_conversejs: New module to serve a simple page for Converse.js
- mod_http_upload_quota: Avoid `max_days` race
- mod_muc: Support MUC hats (XEP-0317, conversejs/prosody compatible)
- mod_muc: Optimize MucSub processing
- mod_muc: Fix exception in mucsub {un}subscription events multicast handler
- mod_multicast: Improve and optimize multicast routing code
- mod_offline: Allow storing non-composing x:events in offline
- mod_ping: Send ping from server, not bare user JID
- mod_push: Fix handling of MUC/Sub messages
- mod_register: New allow_modules option to restrict registration modules
- mod_register_web: Handle unknown host gracefully
- mod_register_web: Use mod_register configured restrictions

#### PubSub
- Add `delete_expired_pubsub_items` command
- Add `delete_old_pubsub_items` command
- Optimize publishing on large nodes (SQL)
- Support unlimited number of items
- Support `max_items=max` node configuration
- Bump default value for `max_items` limit from 10 to 1000
- Use configured `max_items` by default
- node_flat: Avoid catch-all clauses for RSM
- node_flat_sql: Avoid catch-all clauses for RSM

#### SQL
- Use `INSERT ... ON CONFLICT` in SQL_UPSERT for PostgreSQL >= 9.5
- mod_mam export: assign MUC entries to the MUC service
- MySQL: Fix typo when creating index
- PgSQL: Add SASL auth support, PostgreSQL 14
- PgSQL: Add missing SQL migration for table `push_session`
- PgSQL: Fix `vcard_search` definition in pgsql new schema

#### Other
- `captcha-ng.sh`: "sort -R" command not POSIX, added "shuf" and "cat" as fallback
- Make s2s connection table cleanup more robust
- Update export/import of scram password to XEP-0227 1.1
- Update Jose to 1.11.1 (the last in hex.pm correctly versioned)

## Version 21.07

#### Compilation
- Add rebar3 3.15.2 binary
- Add support for mix to: `./configure --enable-rebar=mix`
- Improved `make rel` to work with rebar3 and mix
- Add `make dev` to build a development release with rebar3 or mix
- Hex: Add `sql/` and `vars.config` to Hex package files
- Hex: Update mix applications list to fix error `p1_utils is listed as both...`
- There are so many targets in Makefile... add `make help`
- Fix extauth.py failure in test suite with Python 3
- Added experimental support for GitHub Codespaces
- Switch test service from TravisCI to GitHub Actions

#### Commands:

- Display extended error message in ejabberdctl
- Remove SMP option from ejabberdctl.cfg, `-smp` was removed in OTP 21
- `create_room`: After creating room, store in DB if it's persistent
- `help`: Major changes in its usage and output
- `srg_create`: Update to use `label` parameter instead of `name`

#### Modules:

- ejabberd_listener: New `send_timeout` option
- mod_mix: Improvements to update to 0.14.1
- mod_muc_room: Don't leak owner JIDs
- mod_multicast: Routing for more MUC packets
- mod_multicast: Correctly strip only other bcc addresses
- mod_mqtt: Allow shared roster group placeholder in mqtt topic
- mod_pubsub: Several fixes when using PubSub with RSM
- mod_push: Handle MUC/Sub events correctly
- mod_shared_roster: Delete cache after performing change to be sure that in cache will be up to date data
- mod_shared_roster: Improve database and caching
- mod_shared_roster: Reconfigure cache when options change
- mod_vcard: Fix invalid_encoding error when using extended plane characters in vcard
- mod_vcard: Update econf:vcard() to generate correct vcard_temp record
- WebAdmin: New simple pages to view mnesia tables information and content
- WebSocket: Fix typos

#### SQL:

- MySQL Backend Patch for scram-sha512
- SQLite: When exporting for SQLite, use its specific escape options
- SQLite: Minor fixes for new_sql_schema support
- mod_privacy: Cast as boolean when exporting privacy_list_data to PostgreSQL
- mod_mqtt: Add mqtt_pub table definition for MSSQL
- mod_shared_roster: Add missing indexes to `sr_group` tables in all SQL databases

## Version 21.04

#### API Commands:

- `add_rosteritem/...`: Add argument guards to roster commands
- `get_user_subscriptions`: New command for MUC/Sub
- `remove_mam_for_user_with_peer`: Fix when removing room archive
- `send_message`: Fix bug introduced in ejabberd 21.01
- `set_vcard`: Return modules errors

#### Build and setup:

- Allow ejabberd to be compatible as a dependency for an Erlang project using rebar3
- CAPTCHA: New question/answer-based CAPTCHA script
- `--enable-lua`: new configure option for luerl instead of --enable-tools
- Remove support for HiPE, it was experimental and Erlang/OTP 24 removes it
- Update `sql_query` record to handle the Erlang/OTP 24 compiler reports
- Updated dependencies to fix Dialyzer warnings

#### Miscellaneous:

- CAPTCHA: Update `FORM_TYPE` from captcha to register
- LDAP: fix eldap certificate verification
- MySQL: Fix for "specified key was too long"
- Translations: updated the Esperanto, Greek, and Japanese translations
- Websocket: Fix PONG responses

#### Modules:

- `mod_block_strangers`: If stanza is type error, allow it passing
- `mod_caps`: Don't request roster when not needed
- `mod_caps`: Skip reading roster in one more case
- `mod_mam`: Remove `queryid` from MAM fin element
- `mod_mqtt`: When deregistering XMPP account, close its MQTT sessions
- `mod_muc`: Take in account subscriber's affiliation when checking access to moderated room
- `mod_muc`: Use monitors to track online and hard-killed rooms
- `mod_muc`: When occupant is banned, remove his subscriptions too
- `mod_privacy`: Make fetching roster lazy
- `mod_pubsub`: Don't fail on PEP unsubscribe
- `mod_pubsub`: Fix `gen_pubsub_node:get_state` return value
- `mod_vcard`: Obtain and provide photo type in vCard LDAP

## Version 21.01

#### Miscellaneous changes:

- `log_rotate_size` option: Fix handling of ‘infinity’ value
- `mod_time`: Fix invalid timezone
- Auth JWT: New `check_decoded_jwt` hook runs the default JWT verifier
- MUC: Allow non-occupant non-subscribed service admin send private MUC message
- MUC: New `max_password` and `max_captcha_whitelist` options
- OAuth: New `oauth_cache_rest_failure_life_time` option
- PEP: Skip reading pep nodes that we know won’t be requested due to caps
- SQL: Add sql script to migrate mysql from old schema to new
- SQL: Don’t use REPLACE for upsert when there are “-” fields.
- Shared Rosters LDAP: Add multi-domain support (and flexibility)
- Sqlite3: Fix dependency version
- Stun: Block loopback addresses by default
- Several documentation fixes and clarifications

#### Commands:

- `decide_room`: Use better fallback value for room activity time when skipping room
- `delete_old_message`: Fix when using sqlite spool table
- `module_install`: Make ext_mod compile module with debug_info flags
- `room_unused_*`: Don’t fetch subscribers list
- `send_message`: Don’t include empty in messages
- `set_room_affiliation`: Validate affiliations

#### Running:

- Docker: New `Dockerfile` and `devcontainer.json`
- New `ejabberdctl foreground-quiet`
- Systemd: Allow for listening on privileged ports
- Systemd: Integrate nicely with systemd

#### Translations:

- Moved gettext PO files to a new `ejabberd-po` repository
- Improved several translations: Catalan, Chinese, German, Greek, Indonesian, Norwegian, Portuguese (Brazil), Spanish.

## Version 20.12

- Add support for `SCRAM-SHA-{256,512}-{PLUS}` authentication
- Don't use same value in cache for user don't exist and wrong password
- `outgoing_s2s_ipv*_address`: New options to set ipv4/ipv6 outbound s2s out interface
- s2s_send_packet: this hook now filters outgoing s2s stanzas
- start_room: new hook runs when a room process is started
- check_decoded_jwt: new hook to check decoded JWT after success authentication

#### Admin
- Docker: Fix DB initialization
- New sql_odbc_driver option: choose the mssql ODBC driver
- Rebar3: Fully supported. Enable with `./configure --with-rebar=/path/to/rebar3`
- systemd: start ejabberd in foreground

#### Modules:
- MAM: Make sure that jid used as base in mam xml_compress is bare
- MAM: Support for MAM Flipped Pages
- MUC: Always show MucSub subscribers nicks
- MUC: Don't forget not-persistent rooms in load_permanent_rooms
- MUC Admin: Better error reporting
- MUC Admin: Fix commands with hibernated rooms
- MUC Admin: Many improvements in rooms_unused_list/destroy
- MUC Admin: create_room_with_opts Store options only if room starts
- Pubsub: Remove 'dag' node plugin documentation
- Push: Fix API call return type on error
- Push: Support cache config changes on reload
- Register: Allow for account-removal-only setup again
- Roster: Make roster subscriptions work better with invalid roster state in db
- Vcard: Fix vCard search by User when using Mnesia
- WebAdmin: Allow vhost admins to view WebAdmin menus
- WebAdmin: Don't do double utf-8 conversion on translated strings
- WebAdmin: Mark dangerous buttons with CSS
- WebSocket: Make websocket send put back pressure on c2s process

## Version 20.07

#### Changes in this version
- Add support for using unix sockets in listeners.
- Make this version compatible with erlang R23
- Make room permissions checks more strict for subscribers
- Fix problem with muc rooms crashing when using muc logger
  with some locales
- Limit stat calls that logger module issues
- Don't throw errors when using user_regexp acl rule and
  having non-matching host
- Fix problem with leaving old data when updating shared rosters
- Fix edge case that caused failure of resuming old sessions with
  stream management.
- Fix crash when room that was started with logging enabled was later
  changed to logging disabled
- Increase default shaper limits (this should help with delays for
  clients that are using jingle)
- Fix couple compatibility problems which prevented working on
  erlang R19
- Fix sending presence unavailable when session terminates for
  clients that only send directed presences (helps with sometimes
  not leaving muc rooms on disconnect).
- Prevent supervisor errors for sockets that were closed before
  they were passed to handler modules
- Make stun module work better with ipv6 addresses

## Version 20.03

#### Changes in this version
- Add support of ssl connection when connection to mysql
  database (configured with `sql_ssl: true` option)
- Experimental support for cockroachdb when configured
  with postgres connector
- Add cache and optimize queries issued by `mod_shared_roster`,
  this should greatly improve performance of this module when
  used with `sql` backend
- Fix problem with accessing webadmin
- Make webadmin work even when url is missing trailing slash
- When compiling external modules with ext_mod, use flags
  that were detected during compilation of ejabberd
- Make config changed to ldap options be updated when issued
  `reload_config` command
- Fix `room_empty_destory` command
- Fix reporting errors in `send_stanza` command when xml
  passed to it couldn't be passed correctly

## Version 20.02

#### Changes in this version
- Fix problems when trying to use string format with unicode
  values directly in xmpp nodes
- Add missing oauth_client table declaration in lite.new.sql
- Improve compatibility with CocroachDB
- Fix importing of piefxis files that did use scram passwords
- Fix importing of piefxis files that had multiple includes
  in them
- Update jiffy dependency
- Allow storage of emojis when using mssql database (Thanks
  to Christoph Scholz)
- Make ejabberd_auth_http be able to use auth_opts
- Make custom_headers options in http modules correctly
  override built-in values
- Fix return value of reload_config and dump_config commands

## Version 20.01

#### New features
- Implement OAUTH authentication in mqtt
- Make logging infrastructure use new logger introduced
  in Erlang (requires OTP22)
- New configuration parser/validator
- Initial work on being able to use CockroachDB as database backend
- Add gc command
- Add option to disable using prepared statements on Postgresql
- Implement routine for converting password to SCRAM format
  for all backends not only SQL
- Add infrastructure for having module documentation directly
  in individual module source code
- Generate man page automatically
- Implement copy feature in mod_carboncopy

#### Fixes
- Make webadmin work with configurable paths
- Fix handling of result in xmlrpc module
- Make webadmin work even when accessed through not declared domain
- Better error reporting in xmlrpc
- Limit amount of results returned by disco queries to pubsub nodes
- Improve validation of configured JWT keys
- Fix race condition in Redis/SQL startup
- Fix loading order of third party modules
- Fix reloading of ACL rules
- Make account removal requests properly route response
- Improve handling of malformed inputs in send_message command
- Omit push notification if storing message in offline storage
  failed
- Fix crash in stream management when timeout was not set

## Version 19.09

#### Admin
- The minimum required Erlang/OTP version is now 19.3
- Fix API call using OAuth (#2982)
- Rename MUC command arguments from Host to Service (#2976)

#### Webadmin
- Don't treat 'Host' header as a virtual XMPP host (#2989)
- Fix some links to Guide in WebAdmin and add new ones (#3003)
- Use select fields to input host in WebAdmin Backup (#3000)
- Check account auth provided in WebAdmin is a local host (#3000)

#### ACME
- Improve ACME implementation
- Fix IDA support in ACME requests
- Fix unicode formatting in ACME module
- Log an error message on IDNA failure
- Support IDN hostnames in ACME requests
- Don't attempt to create ACME directory on ejabberd startup
- Don't allow requesting certificates for localhost or IP-like domains
- Don't auto request certificate for localhost and IP-like domains
- Add listener for ACME challenge in example config

#### Authentication
- JWT-only authentication for some users (#3012)

#### MUC
- Apply default role after revoking admin affiliation (#3023)
- Custom exit message is not broadcast (#3004)
- Revert "Affiliations other than admin and owner cannot invite to members_only rooms" (#2987)
- When join new room with password, set pass and password_protected (#2668)
- Improve rooms_* commands to accept 'global' as MUC service argument (#2976)
- Rename MUC command arguments from Host to Service (#2976)

#### SQL
- Fix transactions for Microsoft SQL Server (#2978)
- Spawn SQL connections on demand only

#### Misc
- Add support for XEP-0328: JID Prep
- Added gsfonts for captcha
- Log Mnesia table type on creation
- Replicate Mnesia 'bosh' table when nodes are joined
- Fix certificate selection for s2s (#3015)
- Provide meaningful error when adding non-local users to shared roster (#3000)
- Websocket: don't treat 'Host' header as a virtual XMPP host (#2989)
- Fix sm ack related c2s error (#2984)
- Don't hide the reason why c2s connection has failed
- Unicode support
- Correctly handle unicode in log messages
- Fix unicode processing in ejabberd.yml

## Version 19.08

#### Administration
- Improve ejabberd halting procedure
- Process unexpected erlang messages uniformly: logging a warning
- mod_configure: Remove modules management

#### Configuration
- Use new configuration validator
- ejabberd_http: Use correct virtual host when consulting trusted_proxies
- Fix Elixir modules detection in the configuration file
- Make option 'validate_stream' global
- Allow multiple definitions of host_config and append_host_config
- Introduce option 'captcha_url'
- mod_stream_mgmt: Allow flexible timeout format
- mod_mqtt: Allow flexible timeout format in session_expiry option

#### Misc
- Fix SQL connections leakage
- New authentication method using JWT tokens
- extauth: Add 'certauth' command
- Improve SQL pool logic
- Add and improve type specs
- Improve extraction of translated strings
- Improve error handling/reporting when loading language translations
- Improve hooks validator and fix bugs related to hooks registration
- Gracefully close inbound s2s connections
- mod_mqtt: Fix usage of TLS
- mod_offline: Make count_offline_messages cache work when using mam for storage
- mod_privacy: Don't attempt to query 'undefined' active list
- mod_privacy: Fix race condition

#### MUC
- Add code for hibernating inactive muc_room processes
- Improve handling of unexpected iq in mod_muc_room
- Attach mod_muc_room processes to a supervisor
- Restore room when receiving message or generic iq for not started room
- Distribute routing of MUC messages across all CPU cores

#### PubSub
- Fix pending nodes retrieval for SQL backend
- Check access_model when publishing PEP
- Remove deprecated pubsub plugins
- Expose access_model and publish_model in pubsub#metadata

## Version 19.05

#### Admin
- The minimum required Erlang/OTP version is now 19.1
- Provide a suggestion when unknown command, module, option or request handler is detected
- Deprecate some listening options: captcha, register, web_admin, http_bind and xmlrpc
- Add commands to get Mnesia info: mnesia_info and mnesia_table_info
- Fix Register command to respect mod_register's Access option
- Fixes in Prosody import: privacy and rooms
- Remove TLS options from the example config
- Improve request_handlers validator
- Fix syntax in example Elixir config file

#### Auth
- Correctly support cache tags in ejabberd_auth
- Don't process failed EXTERNAL authentication by mod_fail2ban
- Don't call to mod_register when it's not loaded
- Make anonymous auth don't {de}register user when there are other resources

#### Developer
- Rename listening callback from start/2 to start/3
- New hook called when room gets destroyed: room_destroyed
- New hooks for tracking mucsub subscriptions changes: muc_subscribed, muc_unsubscribed
- Make static hooks analyzer working again

#### MUC
- Service admins are allowed to recreate room even if archive is nonempty
- New option user_mucsub_from_muc_archive
- Avoid late arrival of get_disco_item response
- Handle get_subscribed_rooms call from mod_muc_room pid
- Fix room state cleanup from db on change of persistent option change
- Make get_subscribed_rooms work even for non-persistant rooms
- Allow non-moderator subscribers to get list of room subscribers

#### Offline
- New option bounce_groupchat: make it not bounce mucsub/groupchat messages
- New option use_mam_for_storage: fetch data from mam instead of spool table
- When applying limit of max msgs in spool check only spool size
- Do not store mucsub wrapped messages with no-store hint in offline storage
- Always store ActivityMarker messages
- Don't issue count/message fetch queries for offline from mam when not needed
- Properly handle infinity as max number of message in mam offline storage
- Sort messages by stanza_id when using mam storage in mod_offline
- Return correct value from count_offline_messages with mam storage option
- Make mod_offline put msg ignored by mam in spool when mam storage is on

#### SQL:
- Add SQL schemas for MQTT tables
- Report better errors on SQL terms decode failure
- Fix PostgreSQL compatibility in mod_offline_sql:remove_old_messages
- Fix handling of list arguments on pgsql
- Preliminary support for SQL in process_rosteritems command

#### Tests
- Add tests for user mucsub mam from muc mam
- Add tests for offline with mam storage
- Add tests for offline use_mam_for_storage
- Initial Docker environment to run ejabberd test suite
- Test offline:use_mam_for_storage, mam:user_mucsub_from_muc_archive used together

#### Websocket
- Add WebSockets support to mod_mqtt
- Return "Bad request" error when origin in websocket connection doesn't match
- Fix RFC6454 violation on websocket connection when validating Origin header
- Origin header validation on websocket connection

#### Other modules
- mod_adhoc: Use xml:lang from stanza when it's missing in <command/> element
- mod_announce: Add 'sessionid' attribute when required
- mod_bosh: Don't put duplicate polling attribute in bosh payload
- mod_http_api: Improve argument error messages and log messages
- mod_http_upload: Feed whole image to eimp:identify/1
- mod_http_upload: Log nicer warning on unknown host
- mod_http_upload: Case-insensitive host comparison
- mod_mqtt: Support other socket modules
- mod_push: Check for payload in encrypted messages

## Version 19.02

#### Admin
- Fix in configure.ac the Erlang/OTP version: from 17.5 to 19.0
- reload_config command: Fix crash when sql_pool_size option is used
- reload_config command: Fix crash when SQL is not configured
- rooms_empty_destroy command: Several fixes to behave more conservative
- Fix serverhost->host parameter name for muc_(un)register_nick API

#### Configuration
- Allow specifying tag for listener for api_permission purposes
- Change default ciphers to intermediate
- Define default ciphers/protocol_option in example config
- Don't crash on malformed 'modules' section
- mod_mam: New option clear_archive_on_room_destroy to prevent archive removal on room destroy
- mod_mam: New option access_preferences to restrict who can modify the MAM preferences
- mod_muc: New option access_mam to restrict who can modify that room option
- mod_offline: New option store_groupchat to allow storing group chat messages

#### Core
- Add MQTT protocol support
- Fix (un)setting of priority
- Use OTP application startup infrastructure for starting dependencies
- Improve starting order of several dependencies

#### MAM
- mod_mam_mnesia/sql: Improve check for empty archive
- disallow room creation if archive not empty and clear_archive_on_room_destroy is false
- allow check if archive is empty for or user or room
- Additional checks for database failures

#### MUC
- Make sure that room_destroyed is called even when some code throws in terminate
- Update muc room state after adding extra access field to it
- MUC/Sub: Send mucsub subscriber notification events with from set to room jid

#### Shared Roster
- Don't perform roster push for non-local contacts
- Handle versioning result when shared roster group has remote account
- Fix SQL queries

#### Miscelanea
- CAPTCHA: Add no-store hint to CAPTCHA challenge stanzas
- HTTP: Reject http_api request with malformed Authentication header
- mod_carboncopy: Don't lose carbons on presence change or session resumption
- mod_mix: Fix submission-id and channel resource
- mod_ping: Fix ping IQ reply/timeout processing (17.x regression)
- mod_private: Hardcode item ID for PEP bookmarks
- mod_push: Improve notification error handling
- PIEFXIS: Fix user export when password is scrammed
- Prosody: Improve import of roster items, rooms and attributes
- Translations: fixed "make translations"
- WebAdmin: Fix support to restart module with new options

## Version 18.12

- MAM data store compression
- Proxy protocol support
- MUC Self-Ping optimization (XEP-0410)
- Bookmarks conversion (XEP-0411)


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Guidelines for Respectful and Efficient Communication on Issues, Discussions, and PRs

To ensure that our maintainers can efficiently manage issues and provide timely updates, we kindly ask that all comments on GitHub tickets remain relevant to the topic of the issue. Please avoid posting comments solely to ping maintainers or ask for updates. If you need information on the status of an issue, consider the following:

- **Check the Issue Timeline:** Review the existing comments and updates on the issue before posting.
- **Use Reactions:** If you want to show that you are interested in an issue, use GitHub's reaction feature (e.g., thumbs up) instead of commenting.
- **Be Patient:** Understand that maintainers may be working on multiple tasks and will provide updates as soon as possible.

Additionally, please be aware that:

- **User Responses:** Users who report issues may no longer be using the software, may have switched to other projects, or may simply be busy. It is their right not to respond to follow-up questions or comments.
- **Maintainer Priorities:** Maintainers have the right to define their own priorities and schedule. They will address issues based on their availability and the project's needs.

By following these guidelines, you help us maintain a productive and respectful environment for everyone involved.

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at the email address: conduct AT process-one.net. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version]

[homepage]: https://www.contributor-covenant.org/
[version]: https://www.contributor-covenant.org/version/1/4/


================================================
FILE: COMPILE.md
================================================
Compile and Install ejabberd
============================

This document explains how to compile and install ejabberd
from source code.

For a more detailed explanation, please check the
ejabberd Docs: [Source Code Installation][docs-source].

[docs-source]: https://docs.ejabberd.im/admin/install/source/


Requirements
------------

To compile ejabberd you need:

- GNU Make
- GCC
- Libexpat ≥ 1.95
- Libyaml ≥ 0.1.4
- Erlang/OTP ≥ 25.0
- OpenSSL ≥ 1.0.0

Other optional libraries are:

- Zlib ≥ 1.2.3, for Stream Compression support (XEP-0138)
- PAM library, for Pluggable Authentication Modules (PAM)
- ImageMagick's Convert program and Ghostscript fonts, for CAPTCHA
  challenges
- Elixir ≥ 1.14.0, for Elixir support

If your system splits packages in libraries and development headers,
install the development packages too.


Download Source Code
--------------------

There are several ways to obtain the ejabberd source code:

- Source code archive from [ProcessOne Downloads][p1dl]
- Source code package from [ejabberd GitHub Releases][ghr]
- Latest development code from [ejabberd Git repository][gitrepo]

[p1dl]: https://www.process-one.net/download/ejabberd/
[ghr]: https://github.com/processone/ejabberd/releases
[gitrepo]: https://github.com/processone/ejabberd


Compile
-------

The general instructions to compile ejabberd are:

    ./configure
    make

If the source code doesn't contain a `configure` script,
first of all install `autoconf` and run this to generate it:

    ./autogen.sh

To configure the compilation, features, install paths...

    ./configure --help

The build tool automatically downloads and compiles the
erlang libraries that [ejabberd depends on][docs-repo].

[
Download .txt
gitextract_hsr6yf71/

├── .devcontainer/
│   ├── Dockerfile
│   ├── devcontainer.json
│   └── prepare-container.sh
├── .dockerignore
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   ├── PULL_REQUEST_TEMPLATE
│   ├── actions/
│   │   ├── manage-database/
│   │   │   └── action.yml
│   │   └── manage-ejabberd/
│   │       └── action.yml
│   ├── container/
│   │   ├── Dockerfile
│   │   ├── ejabberd-container-install.bat
│   │   ├── ejabberd.yml.example
│   │   └── ejabberdctl.template
│   ├── lock.yml
│   └── workflows/
│       ├── ci.yml
│       ├── container.yml
│       ├── installers.yml
│       ├── runtime.yml
│       └── weekly.yml
├── .gitignore
├── .shellcheckrc
├── .vscode/
│   ├── extensions.json
│   ├── launch.json
│   ├── relive.sh
│   └── settings.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── COMPILE.md
├── CONTAINER.md
├── CONTRIBUTING.md
├── CONTRIBUTORS.md
├── COPYING
├── Makefile.in
├── README.md
├── SECURITY.md
├── _checkouts/
│   └── configure_deps/
│       ├── rebar.config
│       └── src/
│           ├── configure_deps.app.src
│           ├── configure_deps.erl
│           └── configure_deps_prv.erl
├── autogen.sh
├── config/
│   ├── ejabberd.exs
│   └── runtime.exs
├── configure.ac
├── configure.bat
├── cover.spec
├── ejabberd.doap
├── ejabberd.init.template
├── ejabberd.service.template
├── ejabberd.yml.example
├── ejabberdctl.cfg.example
├── ejabberdctl.template
├── elvis.config
├── erlang_ls.config
├── include/
│   ├── ELDAPv3.hrl
│   ├── bosh.hrl
│   ├── ejabberd_auth.hrl
│   ├── ejabberd_commands.hrl
│   ├── ejabberd_ctl.hrl
│   ├── ejabberd_db_serialize.hrl
│   ├── ejabberd_http.hrl
│   ├── ejabberd_oauth.hrl
│   ├── ejabberd_router.hrl
│   ├── ejabberd_sm.hrl
│   ├── ejabberd_sql.hrl
│   ├── ejabberd_sql_pt.hrl
│   ├── ejabberd_web_admin.hrl
│   ├── eldap.hrl
│   ├── http_bind.hrl
│   ├── logger.hrl
│   ├── mod_announce.hrl
│   ├── mod_antispam.hrl
│   ├── mod_caps.hrl
│   ├── mod_invites.hrl
│   ├── mod_last.hrl
│   ├── mod_mam.hrl
│   ├── mod_matrix_gw.hrl
│   ├── mod_muc.hrl
│   ├── mod_muc_room.hrl
│   ├── mod_offline.hrl
│   ├── mod_privacy.hrl
│   ├── mod_private.hrl
│   ├── mod_proxy65.hrl
│   ├── mod_push.hrl
│   ├── mod_roster.hrl
│   ├── mod_shared_roster.hrl
│   ├── mod_vcard.hrl
│   ├── mqtt.hrl
│   ├── pubsub.hrl
│   └── translate.hrl
├── inetrc
├── install-sh
├── lib/
│   ├── ejabberd/
│   │   ├── config/
│   │   │   ├── attr.ex
│   │   │   ├── config.ex
│   │   │   ├── ejabberd_hook.ex
│   │   │   ├── ejabberd_module.ex
│   │   │   ├── logger/
│   │   │   │   └── ejabberd_logger.ex
│   │   │   ├── opts_formatter.ex
│   │   │   ├── store.ex
│   │   │   └── validator/
│   │   │       ├── validation.ex
│   │   │       ├── validator_attrs.ex
│   │   │       ├── validator_dependencies.ex
│   │   │       └── validator_utility.ex
│   │   ├── config_util.ex
│   │   ├── hooks.ex
│   │   └── logger.ex
│   ├── ejabberd_auth_example.ex
│   ├── mix/
│   │   └── tasks/
│   │       └── deps.tree.ex
│   └── mod_example.ex
├── m4/
│   ├── ax_lib_sqlite3.m4
│   └── erlang-extra.m4
├── man/
│   └── ejabberd.yml.5
├── mix.exs
├── package.json
├── plugins/
│   ├── configure_deps.erl
│   ├── deps_erl_opts.erl
│   ├── override_deps_versions2.erl
│   └── override_opts.erl
├── priv/
│   ├── css/
│   │   ├── admin.css
│   │   ├── bosh.css
│   │   ├── muc.css
│   │   ├── oauth.css
│   │   └── register.css
│   ├── js/
│   │   ├── admin.js
│   │   └── muc.js
│   ├── lua/
│   │   └── redis_sm.lua
│   ├── mod_invites/
│   │   ├── apps.html
│   │   ├── apps.json
│   │   ├── base.html
│   │   ├── base_min.html
│   │   ├── client.html
│   │   ├── copyright
│   │   ├── invite.html
│   │   ├── invite_invalid.html
│   │   ├── register.html
│   │   ├── register_error.html
│   │   ├── register_success.html
│   │   ├── roster.html
│   │   └── static/
│   │       ├── invite.css
│   │       └── invite.js
│   └── msgs/
│       ├── ar.msg
│       ├── bg.msg
│       ├── ca.msg
│       ├── cs.msg
│       ├── de.msg
│       ├── el.msg
│       ├── eo.msg
│       ├── es.msg
│       ├── fr.msg
│       ├── gl.msg
│       ├── he.msg
│       ├── hu.msg
│       ├── id.msg
│       ├── it.msg
│       ├── ja.msg
│       ├── nl.msg
│       ├── no.msg
│       ├── pl.msg
│       ├── pt-br.msg
│       ├── pt.msg
│       ├── ru.msg
│       ├── sk.msg
│       ├── sq.msg
│       ├── sv.msg
│       ├── ta.msg
│       ├── th.msg
│       ├── tr.msg
│       ├── uk.msg
│       ├── vi.msg
│       ├── wa.msg
│       └── zh.msg
├── rebar
├── rebar.config
├── rebar.config.script
├── rebar3
├── rel/
│   ├── files/
│   │   ├── erl
│   │   └── install_upgrade.escript
│   ├── relive.config
│   ├── relive.escript
│   ├── reltool.config.script
│   ├── setup-dev.sh
│   ├── setup-relive.sh
│   ├── sys.config
│   └── vm.args
├── sql/
│   ├── lite.new.sql
│   ├── lite.sql
│   ├── mssql.new.sql
│   ├── mssql.sql
│   ├── mysql.new.sql
│   ├── mysql.old-to-new.sql
│   ├── mysql.sql
│   ├── pg.new.sql
│   └── pg.sql
├── src/
│   ├── ELDAPv3.asn1db
│   ├── ELDAPv3.erl
│   ├── acl.erl
│   ├── econf.erl
│   ├── ejabberd.app.src.script
│   ├── ejabberd.erl
│   ├── ejabberd_access_permissions.erl
│   ├── ejabberd_acme.erl
│   ├── ejabberd_admin.erl
│   ├── ejabberd_app.erl
│   ├── ejabberd_auth.erl
│   ├── ejabberd_auth_anonymous.erl
│   ├── ejabberd_auth_external.erl
│   ├── ejabberd_auth_jwt.erl
│   ├── ejabberd_auth_ldap.erl
│   ├── ejabberd_auth_mnesia.erl
│   ├── ejabberd_auth_pam.erl
│   ├── ejabberd_auth_sql.erl
│   ├── ejabberd_backend_sup.erl
│   ├── ejabberd_batch.erl
│   ├── ejabberd_bosh.erl
│   ├── ejabberd_c2s.erl
│   ├── ejabberd_c2s_config.erl
│   ├── ejabberd_captcha.erl
│   ├── ejabberd_cluster.erl
│   ├── ejabberd_cluster_mnesia.erl
│   ├── ejabberd_commands.erl
│   ├── ejabberd_commands_doc.erl
│   ├── ejabberd_config.erl
│   ├── ejabberd_config_transformer.erl
│   ├── ejabberd_ctl.erl
│   ├── ejabberd_db_serialize.erl
│   ├── ejabberd_db_sup.erl
│   ├── ejabberd_doc.erl
│   ├── ejabberd_hooks.erl
│   ├── ejabberd_http.erl
│   ├── ejabberd_http_ws.erl
│   ├── ejabberd_iq.erl
│   ├── ejabberd_listener.erl
│   ├── ejabberd_local.erl
│   ├── ejabberd_logger.erl
│   ├── ejabberd_mnesia.erl
│   ├── ejabberd_oauth.erl
│   ├── ejabberd_oauth_mnesia.erl
│   ├── ejabberd_oauth_rest.erl
│   ├── ejabberd_oauth_sql.erl
│   ├── ejabberd_old_config.erl
│   ├── ejabberd_option.erl
│   ├── ejabberd_options.erl
│   ├── ejabberd_options_doc.erl
│   ├── ejabberd_piefxis.erl
│   ├── ejabberd_pkix.erl
│   ├── ejabberd_redis.erl
│   ├── ejabberd_redis_sup.erl
│   ├── ejabberd_regexp.erl
│   ├── ejabberd_router.erl
│   ├── ejabberd_router_mnesia.erl
│   ├── ejabberd_router_multicast.erl
│   ├── ejabberd_router_redis.erl
│   ├── ejabberd_router_sql.erl
│   ├── ejabberd_s2s.erl
│   ├── ejabberd_s2s_in.erl
│   ├── ejabberd_s2s_out.erl
│   ├── ejabberd_service.erl
│   ├── ejabberd_shaper.erl
│   ├── ejabberd_sip.erl
│   ├── ejabberd_sm.erl
│   ├── ejabberd_sm_mnesia.erl
│   ├── ejabberd_sm_redis.erl
│   ├── ejabberd_sm_sql.erl
│   ├── ejabberd_sql.erl
│   ├── ejabberd_sql_pt.erl
│   ├── ejabberd_sql_schema.erl
│   ├── ejabberd_sql_sup.erl
│   ├── ejabberd_stun.erl
│   ├── ejabberd_sup.erl
│   ├── ejabberd_system_monitor.erl
│   ├── ejabberd_systemd.erl
│   ├── ejabberd_tmp_sup.erl
│   ├── ejabberd_update.erl
│   ├── ejabberd_web.erl
│   ├── ejabberd_web_admin.erl
│   ├── ejabberd_websocket.erl
│   ├── ejabberd_websocket_codec.erl
│   ├── ejabberd_xmlrpc.erl
│   ├── ejd2sql.erl
│   ├── eldap.erl
│   ├── eldap_filter.erl
│   ├── eldap_filter_yecc.yrl
│   ├── eldap_pool.erl
│   ├── eldap_utils.erl
│   ├── ext_mod.erl
│   ├── extauth.erl
│   ├── extauth_sup.erl
│   ├── gen_iq_handler.erl
│   ├── gen_mod.erl
│   ├── gen_pubsub_node.erl
│   ├── gen_pubsub_nodetree.erl
│   ├── jd2ejd.erl
│   ├── misc.erl
│   ├── mod_adhoc.erl
│   ├── mod_adhoc_api.erl
│   ├── mod_adhoc_api_opt.erl
│   ├── mod_adhoc_opt.erl
│   ├── mod_admin_extra.erl
│   ├── mod_admin_update_sql.erl
│   ├── mod_announce.erl
│   ├── mod_announce_mnesia.erl
│   ├── mod_announce_opt.erl
│   ├── mod_announce_sql.erl
│   ├── mod_antispam.erl
│   ├── mod_antispam_dump.erl
│   ├── mod_antispam_files.erl
│   ├── mod_antispam_filter.erl
│   ├── mod_antispam_opt.erl
│   ├── mod_antispam_rtbl.erl
│   ├── mod_auth_fast.erl
│   ├── mod_auth_fast_mnesia.erl
│   ├── mod_auth_fast_opt.erl
│   ├── mod_avatar.erl
│   ├── mod_avatar_opt.erl
│   ├── mod_block_strangers.erl
│   ├── mod_block_strangers_opt.erl
│   ├── mod_blocking.erl
│   ├── mod_bosh.erl
│   ├── mod_bosh_mnesia.erl
│   ├── mod_bosh_opt.erl
│   ├── mod_bosh_redis.erl
│   ├── mod_bosh_sql.erl
│   ├── mod_caps.erl
│   ├── mod_caps_mnesia.erl
│   ├── mod_caps_opt.erl
│   ├── mod_caps_sql.erl
│   ├── mod_carboncopy.erl
│   ├── mod_client_state.erl
│   ├── mod_client_state_opt.erl
│   ├── mod_configure.erl
│   ├── mod_configure_opt.erl
│   ├── mod_conversejs.erl
│   ├── mod_conversejs_opt.erl
│   ├── mod_delegation.erl
│   ├── mod_delegation_opt.erl
│   ├── mod_disco.erl
│   ├── mod_disco_opt.erl
│   ├── mod_fail2ban.erl
│   ├── mod_fail2ban_opt.erl
│   ├── mod_host_meta.erl
│   ├── mod_host_meta_opt.erl
│   ├── mod_http_api.erl
│   ├── mod_http_api_opt.erl
│   ├── mod_http_fileserver.erl
│   ├── mod_http_fileserver_opt.erl
│   ├── mod_http_upload.erl
│   ├── mod_http_upload_opt.erl
│   ├── mod_http_upload_quota.erl
│   ├── mod_http_upload_quota_opt.erl
│   ├── mod_invites.erl
│   ├── mod_invites_http.erl
│   ├── mod_invites_http_erlylib.erl
│   ├── mod_invites_mnesia.erl
│   ├── mod_invites_opt.erl
│   ├── mod_invites_register.erl
│   ├── mod_invites_sql.erl
│   ├── mod_jidprep.erl
│   ├── mod_jidprep_opt.erl
│   ├── mod_last.erl
│   ├── mod_last_mnesia.erl
│   ├── mod_last_opt.erl
│   ├── mod_last_sql.erl
│   ├── mod_legacy_auth.erl
│   ├── mod_mam.erl
│   ├── mod_mam_mnesia.erl
│   ├── mod_mam_opt.erl
│   ├── mod_mam_sql.erl
│   ├── mod_matrix_gw.erl
│   ├── mod_matrix_gw_opt.erl
│   ├── mod_matrix_gw_room.erl
│   ├── mod_matrix_gw_s2s.erl
│   ├── mod_matrix_gw_sup.erl
│   ├── mod_metrics.erl
│   ├── mod_metrics_opt.erl
│   ├── mod_mix.erl
│   ├── mod_mix_mnesia.erl
│   ├── mod_mix_opt.erl
│   ├── mod_mix_pam.erl
│   ├── mod_mix_pam_mnesia.erl
│   ├── mod_mix_pam_opt.erl
│   ├── mod_mix_pam_sql.erl
│   ├── mod_mix_sql.erl
│   ├── mod_mqtt.erl
│   ├── mod_mqtt_bridge.erl
│   ├── mod_mqtt_bridge_opt.erl
│   ├── mod_mqtt_bridge_session.erl
│   ├── mod_mqtt_mnesia.erl
│   ├── mod_mqtt_opt.erl
│   ├── mod_mqtt_session.erl
│   ├── mod_mqtt_sql.erl
│   ├── mod_mqtt_ws.erl
│   ├── mod_muc.erl
│   ├── mod_muc_admin.erl
│   ├── mod_muc_admin_opt.erl
│   ├── mod_muc_log.erl
│   ├── mod_muc_log_opt.erl
│   ├── mod_muc_mnesia.erl
│   ├── mod_muc_opt.erl
│   ├── mod_muc_room.erl
│   ├── mod_muc_rtbl.erl
│   ├── mod_muc_rtbl_opt.erl
│   ├── mod_muc_sql.erl
│   ├── mod_muc_sup.erl
│   ├── mod_multicast.erl
│   ├── mod_multicast_opt.erl
│   ├── mod_offline.erl
│   ├── mod_offline_mnesia.erl
│   ├── mod_offline_opt.erl
│   ├── mod_offline_sql.erl
│   ├── mod_ping.erl
│   ├── mod_ping_opt.erl
│   ├── mod_pres_counter.erl
│   ├── mod_pres_counter_opt.erl
│   ├── mod_privacy.erl
│   ├── mod_privacy_mnesia.erl
│   ├── mod_privacy_opt.erl
│   ├── mod_privacy_sql.erl
│   ├── mod_private.erl
│   ├── mod_private_mnesia.erl
│   ├── mod_private_opt.erl
│   ├── mod_private_sql.erl
│   ├── mod_privilege.erl
│   ├── mod_privilege_opt.erl
│   ├── mod_providers.erl
│   ├── mod_providers_opt.erl
│   ├── mod_proxy65.erl
│   ├── mod_proxy65_lib.erl
│   ├── mod_proxy65_mnesia.erl
│   ├── mod_proxy65_opt.erl
│   ├── mod_proxy65_redis.erl
│   ├── mod_proxy65_service.erl
│   ├── mod_proxy65_sql.erl
│   ├── mod_proxy65_stream.erl
│   ├── mod_pubsub.erl
│   ├── mod_pubsub_mnesia.erl
│   ├── mod_pubsub_opt.erl
│   ├── mod_pubsub_serverinfo.erl
│   ├── mod_pubsub_serverinfo_opt.erl
│   ├── mod_pubsub_sql.erl
│   ├── mod_push.erl
│   ├── mod_push_keepalive.erl
│   ├── mod_push_keepalive_opt.erl
│   ├── mod_push_mnesia.erl
│   ├── mod_push_opt.erl
│   ├── mod_push_sql.erl
│   ├── mod_register.erl
│   ├── mod_register_opt.erl
│   ├── mod_register_web.erl
│   ├── mod_roster.erl
│   ├── mod_roster_mnesia.erl
│   ├── mod_roster_opt.erl
│   ├── mod_roster_sql.erl
│   ├── mod_s2s_bidi.erl
│   ├── mod_s2s_dialback.erl
│   ├── mod_s2s_dialback_opt.erl
│   ├── mod_scram_upgrade.erl
│   ├── mod_scram_upgrade_opt.erl
│   ├── mod_service_log.erl
│   ├── mod_service_log_opt.erl
│   ├── mod_shared_roster.erl
│   ├── mod_shared_roster_ldap.erl
│   ├── mod_shared_roster_ldap_opt.erl
│   ├── mod_shared_roster_mnesia.erl
│   ├── mod_shared_roster_opt.erl
│   ├── mod_shared_roster_sql.erl
│   ├── mod_sic.erl
│   ├── mod_sip.erl
│   ├── mod_sip_opt.erl
│   ├── mod_sip_proxy.erl
│   ├── mod_sip_registrar.erl
│   ├── mod_stats.erl
│   ├── mod_stream_mgmt.erl
│   ├── mod_stream_mgmt_opt.erl
│   ├── mod_stun_disco.erl
│   ├── mod_stun_disco_opt.erl
│   ├── mod_time.erl
│   ├── mod_vcard.erl
│   ├── mod_vcard_ldap.erl
│   ├── mod_vcard_ldap_opt.erl
│   ├── mod_vcard_mnesia.erl
│   ├── mod_vcard_mnesia_opt.erl
│   ├── mod_vcard_opt.erl
│   ├── mod_vcard_sql.erl
│   ├── mod_vcard_xupdate.erl
│   ├── mod_vcard_xupdate_opt.erl
│   ├── mod_version.erl
│   ├── mod_version_opt.erl
│   ├── mqtt_codec.erl
│   ├── node_flat.erl
│   ├── node_flat_sql.erl
│   ├── node_pep.erl
│   ├── node_pep_sql.erl
│   ├── nodetree_tree.erl
│   ├── nodetree_tree_sql.erl
│   ├── nodetree_virtual.erl
│   ├── prosody2ejabberd.erl
│   ├── proxy_protocol.erl
│   ├── pubsub_db_sql.erl
│   ├── pubsub_index.erl
│   ├── pubsub_migrate.erl
│   ├── pubsub_subscription.erl
│   ├── pubsub_subscription_sql.erl
│   ├── rest.erl
│   ├── str.erl
│   ├── translate.erl
│   ├── win32_dns.erl
│   └── xml_compress.erl
├── test/
│   ├── README
│   ├── announce_tests.erl
│   ├── antispam_tests.erl
│   ├── carbons_tests.erl
│   ├── commands_tests.erl
│   ├── configtest_tests.erl
│   ├── csi_tests.erl
│   ├── docker/
│   │   ├── README.md
│   │   ├── db/
│   │   │   └── mssql/
│   │   │       └── initdb/
│   │   │           └── initdb_mssql.sql
│   │   └── docker-compose.yml
│   ├── ejabberd_SUITE.erl
│   ├── ejabberd_SUITE_data/
│   │   ├── ca.key
│   │   ├── ca.pem
│   │   ├── cert.pem
│   │   ├── configtest.yml
│   │   ├── ejabberd.extauth.yml
│   │   ├── ejabberd.ldap.yml
│   │   ├── ejabberd.ldif
│   │   ├── ejabberd.mnesia.yml
│   │   ├── ejabberd.mssql.yml
│   │   ├── ejabberd.mysql.yml
│   │   ├── ejabberd.pgsql.yml
│   │   ├── ejabberd.redis.yml
│   │   ├── ejabberd.sqlite.yml
│   │   ├── ejabberd.yml
│   │   ├── extauth.py
│   │   ├── gencerts.sh
│   │   ├── macros.yml
│   │   ├── openssl.cnf
│   │   ├── self-signed-cert.pem
│   │   ├── spam_domains.txt
│   │   ├── spam_jids.txt
│   │   ├── spam_urls.txt
│   │   ├── sql_sort.pl
│   │   └── whitelist_domains.txt
│   ├── ejabberd_test_options.erl
│   ├── elixir-config/
│   │   ├── attr_test.exs
│   │   ├── config_test.exs
│   │   ├── ejabberd_logger.exs
│   │   ├── shared/
│   │   │   ├── ejabberd.exs
│   │   │   ├── ejabberd_different_from_default.exs
│   │   │   └── ejabberd_for_validation.exs
│   │   └── validation_test.exs
│   ├── example_tests.erl
│   ├── invites_tests.erl
│   ├── jidprep_tests.erl
│   ├── json_test.erl
│   ├── ldap_srv.erl
│   ├── mam_tests.erl
│   ├── mod_configtest.erl
│   ├── muc_tests.erl
│   ├── offline_tests.erl
│   ├── privacy_tests.erl
│   ├── private_tests.erl
│   ├── proxy65_tests.erl
│   ├── pubsub_tests.erl
│   ├── push_tests.erl
│   ├── replaced_tests.erl
│   ├── roster_tests.erl
│   ├── sm_tests.erl
│   ├── stundisco_tests.erl
│   ├── suite.erl
│   ├── suite.hrl
│   ├── upload_tests.erl
│   ├── vcard_tests.erl
│   └── webadmin_tests.erl
├── tools/
│   ├── captcha-ng.sh
│   ├── captcha.sh
│   ├── check_xep_versions.sh
│   ├── dl_invites_page_deps.sh
│   ├── ejabberdctl.bc
│   ├── emacs-indent.sh
│   ├── extract-erlydtl-templates.sh
│   ├── extract-tr.sh
│   ├── generate-doap.sh
│   ├── hook_deps.sh
│   ├── jhbtest.pl
│   ├── make-binaries
│   ├── make-installers
│   ├── make-packages
│   ├── opt_types.sh
│   ├── prepare-tr.sh
│   ├── rebar3-format.sh
│   ├── update-deps-releases.pl
│   └── xml_compress_gen.erl
└── vars.config.in
Download .txt
SYMBOL INDEX (780 symbols across 36 files)

FILE: config/ejabberd.exs
  class Ejabberd.ConfigFile (line 1) | defmodule Ejabberd.ConfigFile
    method start (line 4) | def start do
    method shaper (line 18) | defp shaper do
    method acl (line 24) | defp acl do
    method access (line 29) | defp access do

FILE: lib/ejabberd/config/attr.ex
  class Ejabberd.Config.Attr (line 1) | defmodule Ejabberd.Config.Attr
    method extract_attrs_from_block_with_defaults (line 30) | def extract_attrs_from_block_with_defaults(block) do
    method validate (line 44) | def validate(attr), do: validate([attr])
    method get_type_for_attr (line 50) | def get_type_for_attr(attr_name) do
    method get_default_for_attr (line 60) | def get_default_for_attr(attr_name) do
    method extract_attrs_from_block (line 71) | defp extract_attrs_from_block({:__block__, [], attrs}), do: Enum.map(a...
    method extract_attrs_from_block (line 72) | defp extract_attrs_from_block({:@, _, [attrs]}), do: extract_attrs_fro...
    method extract_attrs_from_block (line 73) | defp extract_attrs_from_block({attr_name, _, [value]}), do: {attr_name...
    method extract_attrs_from_block (line 74) | defp extract_attrs_from_block(nil), do: []
    method put_into_list_if_not_already (line 80) | defp put_into_list_if_not_already(attr), do: [attr]
    method insert_default_attrs_if_missing (line 85) | defp insert_default_attrs_if_missing(attrs) do
    method valid_attr? (line 97) | defp valid_attr?({attr_name, param} = attr) do
    method is_of_type? (line 118) | defp is_of_type?(_, _), do: false

FILE: lib/ejabberd/config/config.ex
  class Ejabberd.Config (line 1) | defmodule Ejabberd.Config
    method __before_compile__ (line 25) | def __before_compile__(_env) do
    method init (line 34) | def init(file_path, force \\ false) do
    method get_ejabberd_opts (line 50) | def get_ejabberd_opts do
    method start_hooks (line 60) | def start_hooks do
    method do_init (line 103) | defp do_init(file_path) do
    method get_modules_parsed_in_order (line 117) | defp get_modules_parsed_in_order,
    method get_listeners_parsed_in_order (line 121) | defp get_listeners_parsed_in_order,
    method get_hooks_parsed_in_order (line 124) | defp get_hooks_parsed_in_order,
    method get_general_opts (line 128) | defp get_general_opts,
    method call_start_func_and_store_data (line 133) | defp call_start_func_and_store_data(module) do
    method extract_and_store_module_name (line 139) | defp extract_and_store_module_name({{:module, mod, _bytes, _}, _}) do

FILE: lib/ejabberd/config/ejabberd_hook.ex
  class Ejabberd.Config.EjabberdHook (line 1) | defmodule Ejabberd.Config.EjabberdHook
    method start (line 16) | def start(%EjabberdHook{hook: hook, opts: opts, fun: fun}) do

FILE: lib/ejabberd/config/ejabberd_module.ex
  class Ejabberd.Config.EjabberdModule (line 1) | defmodule Ejabberd.Config.EjabberdModule
    method validate (line 24) | def validate(modules) do
    method fetch_git_repos (line 33) | def fetch_git_repos(modules) do
    method is_git_module? (line 41) | defp is_git_module?(%EjabberdModule{attrs: attrs}) do
    method fetch_and_install_git_module (line 48) | defp fetch_and_install_git_module(%EjabberdModule{attrs: attrs}) do
    method fetch_and_store_repo_source_if_not_exists (line 60) | defp fetch_and_store_repo_source_if_not_exists(path, repo) do
    method infer_mod_name_from_git_url (line 67) | defp infer_mod_name_from_git_url(repo),

FILE: lib/ejabberd/config/logger/ejabberd_logger.ex
  class Ejabberd.Config.EjabberdLogger (line 1) | defmodule Ejabberd.Config.EjabberdLogger
    method do_log_errors (line 19) | defp do_log_errors({:ok, _mod}), do: nil
    method do_log_errors (line 20) | defp do_log_errors({:error, _mod, errors}), do: (Enum.each errors, &do...
    method do_log_errors (line 21) | defp do_log_errors({:attribute, errors}), do: (Enum.each errors, &log_...
    method do_log_errors (line 22) | defp do_log_errors({:dependency, errors}), do: (Enum.each errors, &log...
    method log_attribute_error (line 24) | defp log_attribute_error({{attr_name, _val}, :attr_not_supported}), do:
    method log_attribute_error (line 27) | defp log_attribute_error({{attr_name, val}, :type_not_supported}), do:
    method log_dependency_error (line 30) | defp log_dependency_error({module, :not_found}), do:

FILE: lib/ejabberd/config/opts_formatter.ex
  class Ejabberd.Config.OptsFormatter (line 1) | defmodule Ejabberd.Config.OptsFormatter
    method format_opts_for_ejabberd (line 18) | def format_opts_for_ejabberd(opts) do
    method format_attrs_for_ejabberd (line 23) | defp format_attrs_for_ejabberd({:listeners, mods}),
    method format_attrs_for_ejabberd (line 26) | defp format_attrs_for_ejabberd({:modules, mods}),
    method format_attrs_for_ejabberd (line 32) | defp format_attrs_for_ejabberd(opts),
    method format_mods_for_ejabberd (line 35) | defp format_mods_for_ejabberd(mods) do
    method format_listeners_for_ejabberd (line 41) | defp format_listeners_for_ejabberd(mods) do

FILE: lib/ejabberd/config/store.ex
  class Ejabberd.Config.Store (line 1) | defmodule Ejabberd.Config.Store
    method start_link (line 21) | def start_link do
    method put (line 31) | def put(key, val) do
    method get (line 42) | def get(key) do
    method stop (line 52) | def stop do

FILE: lib/ejabberd/config/validator/validation.ex
  class Ejabberd.Config.Validation (line 1) | defmodule Ejabberd.Config.Validation
    method validate (line 19) | def validate(module), do: validate([module])
    method do_validate (line 24) | defp do_validate(modules, mod) do
    method resolve_validation_result (line 32) | defp resolve_validation_result({_modules, mod, errors}) do

FILE: lib/ejabberd/config/validator/validator_attrs.ex
  class Ejabberd.Config.Validator.Attrs (line 1) | defmodule Ejabberd.Config.Validator.Attrs
    method validate (line 19) | def validate({modules, mod, errors}) do

FILE: lib/ejabberd/config/validator/validator_dependencies.ex
  class Ejabberd.Config.Validator.Dependencies (line 1) | defmodule Ejabberd.Config.Validator.Dependencies
    method validate (line 19) | def validate({modules, mod, errors}) do

FILE: lib/ejabberd/config/validator/validator_utility.ex
  class Ejabberd.Config.ValidatorUtility (line 1) | defmodule Ejabberd.Config.ValidatorUtility
    method put_error (line 13) | def put_error(errors, key, val) do

FILE: lib/ejabberd/config_util.ex
  class Ejabberd.ConfigUtil (line 1) | defmodule Ejabberd.ConfigUtil
    method is_elixir_config (line 15) | def is_elixir_config(filename) do

FILE: lib/ejabberd/hooks.ex
  class Ejabberd.Hooks (line 1) | defmodule Ejabberd.Hooks
    method add (line 4) | def add(hook_name, host, module, function, priority) do
    method delete (line 9) | def delete(hook_name, host, module, function, priority) do

FILE: lib/ejabberd/logger.ex
  class Ejabberd.Logger (line 1) | defmodule Ejabberd.Logger
    method critical (line 3) | def critical(message, args \\ []), do: :logger.critical(message, args)
    method error (line 4) | def error(message, args \\ []),    do: :logger.error(message, args)
    method warning (line 5) | def warning(message, args \\ []),  do: :logger.warning(message, args)
    method info (line 6) | def info(message, args \\ []),     do: :logger.info(message, args)
    method debug (line 7) | def debug(message, args \\ []),    do: :logger.debug( message, args)

FILE: lib/ejabberd_auth_example.ex
  class Ejabberd.Auth.Example (line 1) | defmodule Ejabberd.Auth.Example
    method start (line 17) | def start(host) do
    method stop (line 23) | def stop(host) do
    method check_password (line 29) | def check_password("alice", _authz_id, _host, "secret"), do: {:nocache...
    method check_password (line 30) | def check_password(_username, _authz_id, _host, _secret), do: {:nocach...
    method user_exists (line 33) | def user_exists("alice", _host), do: {:nocache, true}
    method user_exists (line 34) | def user_exists(_username, _host), do: {:nocache, false}
    method plain_password_required (line 37) | def plain_password_required(_binary), do: true
    method store_type (line 40) | def store_type(_host), do: :external
    method use_cache (line 43) | def use_cache(_host), do: false

FILE: lib/mix/tasks/deps.tree.ex
  class Mix.Tasks.Ejabberd.Deps.Tree (line 1) | defmodule Mix.Tasks.Ejabberd.Deps.Tree
    method run (line 14) | def run(_argv) do
    method build_dependency_tree (line 36) | defp build_dependency_tree(mods) do
    method build_dependency_tree (line 43) | defp build_dependency_tree(_mods, mod, []), do: %{module: mod, depende...
    method get_mod_names (line 62) | defp get_mod_names([]), do: []
    method get_mod_names (line 64) | defp get_mod_names(%{module: mod, dependency: deps}), do: [mod | get_m...
    method keep_only_mods_not_used_as_dep (line 66) | defp keep_only_mods_not_used_as_dep(mods, mods_used_as_dep) do
    method get_dependencies_of_mod (line 72) | defp get_dependencies_of_mod(deps, mod_name) do
    method format_mods_into_string (line 78) | defp format_mods_into_string(mods), do: format_mods_into_string(mods, 0)
    method format_mods_into_string (line 79) | defp format_mods_into_string([], _indentation), do: ""
    method format_mods_into_string (line 86) | defp format_mods_into_string(%{module: mod, dependency: deps}, 0) do
    method format_mods_into_string (line 90) | defp format_mods_into_string(%{module: mod, dependency: deps}, indenta...

FILE: lib/mod_example.ex
  class Ejabberd.Module.Example (line 1) | defmodule Ejabberd.Module.Example
    method start (line 17) | def start(host, _opts) do
    method stop (line 23) | def stop(host) do
    method on_presence (line 29) | def on_presence(user, _server, _resource, _packet) do
    method depends (line 34) | def depends(_host, _opts) do
    method mod_options (line 38) | def mod_options(_host) do
    method mod_doc (line 42) | def mod_doc() do

FILE: mix.exs
  class Ejabberd.MixProject (line 1) | defmodule Ejabberd.MixProject
    method project (line 4) | def project do
    method version (line 26) | def version do
    method description (line 38) | def description do
    method application (line 44) | def application do
    method dialyzer (line 55) | defp dialyzer do
    method if_version_above (line 66) | defp if_version_above(ver, okResult) do
    method if_version_below (line 74) | defp if_version_below(ver, okResult) do
    method erlc_options (line 82) | defp erlc_options do
    method cond_options (line 97) | defp cond_options do
    method deps (line 108) | defp deps do
    method deps_include (line 129) | defp deps_include() do
    method cond_deps (line 140) | defp cond_deps do
    method cond_apps (line 160) | defp cond_apps do
    method cond_included_apps (line 169) | defp cond_included_apps do
    method package (line 183) | defp package do
    method vars (line 197) | defp vars do
    method config (line 214) | defp config(key) do
    method releases (line 221) | defp releases do
    method copy_extra_files (line 236) | defp copy_extra_files(release) do
    method docs (line 322) | defp docs do
  class Mix.Tasks.Compile.Asn1 (line 349) | defmodule Mix.Tasks.Compile.Asn1
    method run (line 356) | def run(args) do
    method manifests (line 380) | def manifests, do: [manifest()]
    method manifest (line 381) | defp manifest, do: Path.join(Mix.Project.manifest_path(), @manifest)
    method clean (line 383) | def clean, do: Erlang.clean(manifest())

FILE: priv/js/admin.js
  function selectAll (line 2) | function selectAll() {
  function unSelectAll (line 9) | function unSelectAll() {

FILE: priv/js/muc.js
  function sh (line 2) | function sh(e) {
  function jlf (line 10) | function jlf() {

FILE: priv/mod_invites/static/invite.js
  function toggle_password (line 110) | function toggle_password(e) {

FILE: sql/lite.new.sql
  type users (line 19) | CREATE TABLE users (
  type last (line 32) | CREATE TABLE last (
  type rosterusers (line 41) | CREATE TABLE rosterusers (
  type i_rosteru_sh_user_jid (line 56) | CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers (server_host, u...
  type i_rosteru_sh_jid (line 57) | CREATE INDEX i_rosteru_sh_jid ON rosterusers (server_host, jid)
  type rostergroups (line 60) | CREATE TABLE rostergroups (
  type i_rosterg_sh_user_jid (line 67) | CREATE INDEX i_rosterg_sh_user_jid ON rostergroups (server_host, usernam...
  type sr_group (line 69) | CREATE TABLE sr_group (
  type i_sr_group_sh_name (line 77) | CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group (server_host, name)
  type sr_user (line 79) | CREATE TABLE sr_user (
  type i_sr_user_sh_jid_grp (line 87) | CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user (server_host, jid, grp)
  type i_sr_user_sh_grp (line 88) | CREATE INDEX i_sr_user_sh_grp ON sr_user (server_host, grp)
  type spool (line 90) | CREATE TABLE spool (
  type i_spool_sh_username (line 98) | CREATE INDEX i_spool_sh_username ON spool (server_host, username)
  type archive (line 100) | CREATE TABLE archive (
  type i_archive_sh_username_timestamp (line 115) | CREATE INDEX i_archive_sh_username_timestamp ON archive (server_host, us...
  type i_archive_sh_username_peer (line 116) | CREATE INDEX i_archive_sh_username_peer ON archive (server_host, usernam...
  type i_archive_sh_username_bare_peer (line 117) | CREATE INDEX i_archive_sh_username_bare_peer ON archive (server_host, us...
  type i_archive_sh_timestamp (line 118) | CREATE INDEX i_archive_sh_timestamp ON archive (server_host, timestamp)
  type i_archive_sh_username_origin_id (line 119) | CREATE INDEX i_archive_sh_username_origin_id ON archive (server_host, us...
  type archive_prefs (line 125) | CREATE TABLE archive_prefs (
  type vcard (line 135) | CREATE TABLE vcard (
  type vcard_search (line 143) | CREATE TABLE vcard_search (
  type i_vcard_search_sh_lfn (line 172) | CREATE INDEX i_vcard_search_sh_lfn       ON vcard_search(server_host, lfn)
  type i_vcard_search_sh_lfamily (line 173) | CREATE INDEX i_vcard_search_sh_lfamily   ON vcard_search(server_host, lf...
  type i_vcard_search_sh_lgiven (line 174) | CREATE INDEX i_vcard_search_sh_lgiven    ON vcard_search(server_host, lg...
  type i_vcard_search_sh_lmiddle (line 175) | CREATE INDEX i_vcard_search_sh_lmiddle   ON vcard_search(server_host, lm...
  type i_vcard_search_sh_lnickname (line 176) | CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, ln...
  type i_vcard_search_sh_lbday (line 177) | CREATE INDEX i_vcard_search_sh_lbday     ON vcard_search(server_host, lb...
  type i_vcard_search_sh_lctry (line 178) | CREATE INDEX i_vcard_search_sh_lctry     ON vcard_search(server_host, lc...
  type i_vcard_search_sh_llocality (line 179) | CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, ll...
  type i_vcard_search_sh_lemail (line 180) | CREATE INDEX i_vcard_search_sh_lemail    ON vcard_search(server_host, le...
  type i_vcard_search_sh_lorgname (line 181) | CREATE INDEX i_vcard_search_sh_lorgname  ON vcard_search(server_host, lo...
  type i_vcard_search_sh_lorgunit (line 182) | CREATE INDEX i_vcard_search_sh_lorgunit  ON vcard_search(server_host, lo...
  type privacy_default_list (line 184) | CREATE TABLE privacy_default_list (
  type privacy_list (line 191) | CREATE TABLE privacy_list (
  type i_privacy_list_sh_username_name (line 199) | CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list (ser...
  type privacy_list_data (line 201) | CREATE TABLE privacy_list_data (
  type private_storage (line 214) | CREATE TABLE private_storage (
  type roster_version (line 223) | CREATE TABLE roster_version (
  type pubsub_node (line 230) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 237) | CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent)
  type i_pubsub_node_tuple (line 238) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node)
  type pubsub_node_option (line 240) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 245) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid)
  type pubsub_node_owner (line 247) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 251) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid)
  type pubsub_state (line 253) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 260) | CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid)
  type i_pubsub_state_tuple (line 261) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid)
  type pubsub_item (line 263) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 271) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid)
  type i_pubsub_item_tuple (line 272) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid)
  type pubsub_subscription_opt (line 274) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 279) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 281) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 289) | CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host)
  type i_muc_room_host_created_at (line 290) | CREATE INDEX i_muc_room_host_created_at ON muc_room (host, created_at)
  type muc_registered (line 292) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 300) | CREATE INDEX i_muc_registered_nick ON muc_registered (nick)
  type i_muc_registered_jid_host (line 301) | CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host)
  type muc_online_room (line 303) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 311) | CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name...
  type muc_online_users (line 313) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 323) | CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, se...
  type muc_room_subscribers (line 325) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 334) | CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(hos...
  type i_muc_room_subscribers_jid (line 335) | CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid)
  type i_muc_room_subscribers_host_room_jid (line 336) | CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_sub...
  type motd (line 338) | CREATE TABLE motd (
  type caps_features (line 346) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 353) | CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode)
  type sm (line 355) | CREATE TABLE sm (
  type i_sm_node (line 367) | CREATE INDEX i_sm_node ON sm(node)
  type i_sm_sh_username (line 368) | CREATE INDEX i_sm_sh_username ON sm (server_host, username)
  type oauth_token (line 370) | CREATE TABLE oauth_token (
  type oauth_client (line 377) | CREATE TABLE oauth_client (
  type route (line 384) | CREATE TABLE route (
  type i_route (line 392) | CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid)
  type bosh (line 394) | CREATE TABLE bosh (
  type i_bosh_sid (line 400) | CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid)
  type proxy65 (line 402) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 411) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid)
  type i_proxy65_jid (line 412) | CREATE INDEX i_proxy65_jid ON proxy65 (jid_i)
  type push_session (line 414) | CREATE TABLE push_session (
  type i_push_session_susn (line 424) | CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host, us...
  type i_push_session_sh_username_timestamp (line 425) | CREATE INDEX i_push_session_sh_username_timestamp ON push_session (serve...
  type mix_channel (line 427) | CREATE TABLE mix_channel (
  type i_mix_channel (line 438) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service)
  type i_mix_channel_serv (line 439) | CREATE INDEX i_mix_channel_serv ON mix_channel (service)
  type mix_participant (line 441) | CREATE TABLE mix_participant (
  type i_mix_participant (line 452) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, servi...
  type mix_subscription (line 454) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 463) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, ser...
  type i_mix_subscription_chan_serv_node (line 464) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 466) | CREATE TABLE mix_pam (
  type i_mix_pam (line 475) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel...
  type mqtt_pub (line 477) | CREATE TABLE mqtt_pub (
  type i_mqtt_topic_server (line 492) | CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host)
  type invite_token (line 494) | CREATE TABLE invite_token (
  type i_invite_token_username_server_host (line 505) | CREATE INDEX i_invite_token_username_server_host ON invite_token(usernam...
  type i_invite_token_invitee (line 506) | CREATE INDEX i_invite_token_invitee ON invite_token(invitee)

FILE: sql/lite.sql
  type users (line 19) | CREATE TABLE users (
  type last (line 31) | CREATE TABLE last (
  type rosterusers (line 38) | CREATE TABLE rosterusers (
  type i_rosteru_user_jid (line 52) | CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers (username, jid)
  type i_rosteru_jid (line 53) | CREATE INDEX i_rosteru_jid ON rosterusers (jid)
  type rostergroups (line 56) | CREATE TABLE rostergroups (
  type pk_rosterg_user_jid (line 62) | CREATE INDEX pk_rosterg_user_jid ON rostergroups (username, jid)
  type sr_group (line 64) | CREATE TABLE sr_group (
  type i_sr_group_name (line 70) | CREATE UNIQUE INDEX i_sr_group_name ON sr_group (name)
  type sr_user (line 72) | CREATE TABLE sr_user (
  type i_sr_user_jid_grp (line 78) | CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user (jid, grp)
  type i_sr_user_grp (line 79) | CREATE INDEX i_sr_user_grp ON sr_user (grp)
  type spool (line 81) | CREATE TABLE spool (
  type i_despool (line 88) | CREATE INDEX i_despool ON spool (username)
  type archive (line 90) | CREATE TABLE archive (
  type i_username_timestamp (line 104) | CREATE INDEX i_username_timestamp ON archive(username, timestamp)
  type i_archive_username_peer (line 105) | CREATE INDEX i_archive_username_peer ON archive (username, peer)
  type i_archive_username_bare_peer (line 106) | CREATE INDEX i_archive_username_bare_peer ON archive (username, bare_peer)
  type i_timestamp (line 107) | CREATE INDEX i_timestamp ON archive(timestamp)
  type i_archive_username_origin_id (line 108) | CREATE INDEX i_archive_username_origin_id ON archive (username, origin_id)
  type archive_prefs (line 114) | CREATE TABLE archive_prefs (
  type vcard (line 122) | CREATE TABLE vcard (
  type vcard_search (line 128) | CREATE TABLE vcard_search (
  type i_vcard_search_lfn (line 155) | CREATE INDEX i_vcard_search_lfn       ON vcard_search(lfn)
  type i_vcard_search_lfamily (line 156) | CREATE INDEX i_vcard_search_lfamily   ON vcard_search(lfamily)
  type i_vcard_search_lgiven (line 157) | CREATE INDEX i_vcard_search_lgiven    ON vcard_search(lgiven)
  type i_vcard_search_lmiddle (line 158) | CREATE INDEX i_vcard_search_lmiddle   ON vcard_search(lmiddle)
  type i_vcard_search_lnickname (line 159) | CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname)
  type i_vcard_search_lbday (line 160) | CREATE INDEX i_vcard_search_lbday     ON vcard_search(lbday)
  type i_vcard_search_lctry (line 161) | CREATE INDEX i_vcard_search_lctry     ON vcard_search(lctry)
  type i_vcard_search_llocality (line 162) | CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality)
  type i_vcard_search_lemail (line 163) | CREATE INDEX i_vcard_search_lemail    ON vcard_search(lemail)
  type i_vcard_search_lorgname (line 164) | CREATE INDEX i_vcard_search_lorgname  ON vcard_search(lorgname)
  type i_vcard_search_lorgunit (line 165) | CREATE INDEX i_vcard_search_lorgunit  ON vcard_search(lorgunit)
  type privacy_default_list (line 167) | CREATE TABLE privacy_default_list (
  type privacy_list (line 172) | CREATE TABLE privacy_list (
  type i_privacy_list_username_name (line 179) | CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list (userna...
  type privacy_list_data (line 181) | CREATE TABLE privacy_list_data (
  type private_storage (line 194) | CREATE TABLE private_storage (
  type i_private_storage_username_namespace (line 201) | CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_stor...
  type roster_version (line 204) | CREATE TABLE roster_version (
  type pubsub_node (line 209) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 216) | CREATE INDEX i_pubsub_node_parent ON pubsub_node (parent)
  type i_pubsub_node_tuple (line 217) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node (host, node)
  type pubsub_node_option (line 219) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 224) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option (nodeid)
  type pubsub_node_owner (line 226) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 230) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner (nodeid)
  type pubsub_state (line 232) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 239) | CREATE INDEX i_pubsub_state_jid ON pubsub_state (jid)
  type i_pubsub_state_tuple (line 240) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state (nodeid, jid)
  type pubsub_item (line 242) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 250) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item (itemid)
  type i_pubsub_item_tuple (line 251) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item (nodeid, itemid)
  type pubsub_subscription_opt (line 253) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 258) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 260) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 267) | CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room (name, host)
  type i_muc_room_host_created_at (line 268) | CREATE INDEX i_muc_room_host_created_at ON muc_room (host, created_at)
  type muc_registered (line 270) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 277) | CREATE INDEX i_muc_registered_nick ON muc_registered (nick)
  type i_muc_registered_jid_host (line 278) | CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered (jid, host)
  type muc_online_room (line 280) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 287) | CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room (name...
  type muc_online_users (line 289) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 298) | CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users (username, se...
  type muc_room_subscribers (line 300) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 309) | CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(hos...
  type i_muc_room_subscribers_jid (line 310) | CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid)
  type i_muc_room_subscribers_host_room_jid (line 311) | CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_sub...
  type motd (line 313) | CREATE TABLE motd (
  type caps_features (line 319) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 326) | CREATE INDEX i_caps_features_node_subnode ON caps_features (node, subnode)
  type sm (line 328) | CREATE TABLE sm (
  type i_sm_sid (line 338) | CREATE UNIQUE INDEX i_sm_sid ON sm(usec, pid)
  type i_sm_node (line 339) | CREATE INDEX i_sm_node ON sm(node)
  type i_sm_username (line 340) | CREATE INDEX i_sm_username ON sm(username)
  type oauth_token (line 342) | CREATE TABLE oauth_token (
  type oauth_client (line 349) | CREATE TABLE oauth_client (
  type route (line 356) | CREATE TABLE route (
  type i_route (line 364) | CREATE UNIQUE INDEX i_route ON route(domain, server_host, node, pid)
  type bosh (line 366) | CREATE TABLE bosh (
  type i_bosh_sid (line 372) | CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid)
  type proxy65 (line 374) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 383) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid)
  type i_proxy65_jid (line 384) | CREATE INDEX i_proxy65_jid ON proxy65 (jid_i)
  type push_session (line 386) | CREATE TABLE push_session (
  type i_push_usn (line 394) | CREATE UNIQUE INDEX i_push_usn ON push_session (username, service, node)
  type i_push_ut (line 395) | CREATE UNIQUE INDEX i_push_ut ON push_session (username, timestamp)
  type mix_channel (line 397) | CREATE TABLE mix_channel (
  type i_mix_channel (line 408) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service)
  type i_mix_channel_serv (line 409) | CREATE INDEX i_mix_channel_serv ON mix_channel (service)
  type mix_participant (line 411) | CREATE TABLE mix_participant (
  type i_mix_participant (line 422) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, servi...
  type mix_subscription (line 424) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 433) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, ser...
  type i_mix_subscription_chan_serv_node (line 434) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 436) | CREATE TABLE mix_pam (
  type i_mix_pam (line 444) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service)
  type mqtt_pub (line 446) | CREATE TABLE mqtt_pub (
  type i_mqtt_topic (line 460) | CREATE UNIQUE INDEX i_mqtt_topic ON mqtt_pub (topic)
  type invite_token (line 462) | CREATE TABLE invite_token (
  type i_invite_token_username (line 472) | CREATE INDEX i_invite_token_username ON invite_token(username)
  type i_invite_token_invitee (line 473) | CREATE INDEX i_invite_token_invitee ON invite_token(invitee)

FILE: sql/mysql.new.sql
  type users (line 19) | CREATE TABLE users (
  type last (line 36) | CREATE TABLE last (
  type rosterusers (line 45) | CREATE TABLE rosterusers (
  type i_rosteru_sh_user_jid (line 60) | CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers(server_host(191...
  type i_rosteru_sh_jid (line 61) | CREATE INDEX i_rosteru_sh_jid ON rosterusers(server_host(191), jid)
  type rostergroups (line 63) | CREATE TABLE rostergroups (
  type i_rosterg_sh_user_jid (line 70) | CREATE INDEX i_rosterg_sh_user_jid ON rostergroups(server_host(191), use...
  type sr_group (line 72) | CREATE TABLE sr_group (
  type i_sr_group_sh_name (line 80) | CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group(server_host(191), name)
  type sr_user (line 82) | CREATE TABLE sr_user (
  type i_sr_user_sh_jid_grp (line 90) | CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user(server_host(191), ji...
  type i_sr_user_sh_grp (line 91) | CREATE INDEX i_sr_user_sh_grp ON sr_user(server_host(191), grp)
  type spool (line 93) | CREATE TABLE spool (
  type i_spool_sh_username (line 101) | CREATE INDEX i_spool_sh_username USING BTREE ON spool(server_host(191), ...
  type i_spool_created_at (line 102) | CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at)
  type archive (line 104) | CREATE TABLE archive (
  type i_text (line 119) | CREATE FULLTEXT INDEX i_text ON archive(txt)
  type i_archive_sh_username_timestamp (line 120) | CREATE INDEX i_archive_sh_username_timestamp USING BTREE ON archive(serv...
  type i_archive_sh_username_peer (line 121) | CREATE INDEX i_archive_sh_username_peer USING BTREE ON archive(server_ho...
  type i_archive_sh_username_bare_peer (line 122) | CREATE INDEX i_archive_sh_username_bare_peer USING BTREE ON archive(serv...
  type i_archive_sh_timestamp (line 123) | CREATE INDEX i_archive_sh_timestamp USING BTREE ON archive(server_host(1...
  type i_archive_sh_username_origin_id (line 124) | CREATE INDEX i_archive_sh_username_origin_id USING BTREE ON archive(serv...
  type archive_prefs (line 131) | CREATE TABLE archive_prefs (
  type vcard (line 141) | CREATE TABLE vcard (
  type vcard_search (line 149) | CREATE TABLE vcard_search (
  type i_vcard_search_sh_lfn (line 178) | CREATE INDEX i_vcard_search_sh_lfn       ON vcard_search(server_host(191...
  type i_vcard_search_sh_lfamily (line 179) | CREATE INDEX i_vcard_search_sh_lfamily   ON vcard_search(server_host(191...
  type i_vcard_search_sh_lgiven (line 180) | CREATE INDEX i_vcard_search_sh_lgiven    ON vcard_search(server_host(191...
  type i_vcard_search_sh_lmiddle (line 181) | CREATE INDEX i_vcard_search_sh_lmiddle   ON vcard_search(server_host(191...
  type i_vcard_search_sh_lnickname (line 182) | CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host(191...
  type i_vcard_search_sh_lbday (line 183) | CREATE INDEX i_vcard_search_sh_lbday     ON vcard_search(server_host(191...
  type i_vcard_search_sh_lctry (line 184) | CREATE INDEX i_vcard_search_sh_lctry     ON vcard_search(server_host(191...
  type i_vcard_search_sh_llocality (line 185) | CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host(191...
  type i_vcard_search_sh_lemail (line 186) | CREATE INDEX i_vcard_search_sh_lemail    ON vcard_search(server_host(191...
  type i_vcard_search_sh_lorgname (line 187) | CREATE INDEX i_vcard_search_sh_lorgname  ON vcard_search(server_host(191...
  type i_vcard_search_sh_lorgunit (line 188) | CREATE INDEX i_vcard_search_sh_lorgunit  ON vcard_search(server_host(191...
  type privacy_default_list (line 190) | CREATE TABLE privacy_default_list (
  type privacy_list (line 197) | CREATE TABLE privacy_list (
  type i_privacy_list_sh_username_name (line 205) | CREATE UNIQUE INDEX i_privacy_list_sh_username_name USING BTREE ON priva...
  type privacy_list_data (line 207) | CREATE TABLE privacy_list_data (
  type i_privacy_list_data_id (line 220) | CREATE INDEX i_privacy_list_data_id ON privacy_list_data(id)
  type private_storage (line 222) | CREATE TABLE private_storage (
  type i_private_storage_sh_sername_namespace (line 230) | CREATE UNIQUE INDEX i_private_storage_sh_sername_namespace USING BTREE O...
  type roster_version (line 233) | CREATE TABLE roster_version (
  type pubsub_node (line 245) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 252) | CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120))
  type i_pubsub_node_tuple (line 253) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(71), node(120))
  type pubsub_node_option (line 255) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 260) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid)
  type pubsub_node_owner (line 263) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 267) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid)
  type pubsub_state (line 270) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 277) | CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60))
  type i_pubsub_state_tuple (line 278) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60))
  type pubsub_item (line 281) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 289) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36))
  type i_pubsub_item_tuple (line 290) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36))
  type pubsub_subscription_opt (line 293) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 298) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 300) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 308) | CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75...
  type i_muc_room_host_created_at (line 309) | CREATE INDEX i_muc_room_host_created_at ON muc_room(host(75), created_at)
  type muc_registered (line 311) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 319) | CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75))
  type i_muc_registered_jid_host (line 320) | CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registe...
  type muc_online_room (line 322) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 330) | CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_onlin...
  type muc_online_users (line 332) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 342) | CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(u...
  type muc_room_subscribers (line 344) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 354) | CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_sub...
  type i_muc_room_subscribers_jid (line 355) | CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscrib...
  type motd (line 357) | CREATE TABLE motd (
  type caps_features (line 365) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 372) | CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), sub...
  type sm (line 374) | CREATE TABLE sm (
  type i_sm_node (line 386) | CREATE INDEX i_sm_node ON sm(node(75))
  type i_sm_sh_username (line 387) | CREATE INDEX i_sm_sh_username ON sm(server_host(191), username)
  type oauth_token (line 389) | CREATE TABLE oauth_token (
  type oauth_client (line 396) | CREATE TABLE oauth_client (
  type route (line 403) | CREATE TABLE route (
  type i_route (line 411) | CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(7...
  type bosh (line 413) | CREATE TABLE bosh (
  type i_bosh_sid (line 419) | CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75))
  type proxy65 (line 421) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 430) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191))
  type i_proxy65_jid (line 431) | CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191))
  type push_session (line 433) | CREATE TABLE push_session (
  type i_push_session_susn (line 443) | CREATE UNIQUE INDEX i_push_session_susn ON push_session (server_host(191...
  type i_push_session_sh_username_timestamp (line 444) | CREATE INDEX i_push_session_sh_username_timestamp ON push_session (serve...
  type mix_channel (line 446) | CREATE TABLE mix_channel (
  type i_mix_channel (line 457) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel(191), service(...
  type i_mix_channel_serv (line 458) | CREATE INDEX i_mix_channel_serv ON mix_channel (service(191))
  type mix_participant (line 460) | CREATE TABLE mix_participant (
  type i_mix_participant (line 471) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), ...
  type mix_subscription (line 473) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 482) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153)...
  type i_mix_subscription_chan_serv_node (line 483) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 485) | CREATE TABLE mix_pam (
  type i_mix_pam (line 494) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), server_host(191...
  type mqtt_pub (line 496) | CREATE TABLE mqtt_pub (
  type invite_token (line 512) | CREATE TABLE invite_token (
  type i_invite_token_username (line 524) | CREATE INDEX i_invite_token_username USING BTREE ON invite_token(usernam...
  type i_invite_token_invitee (line 525) | CREATE INDEX i_invite_token_invitee USING BTREE ON invite_token(invitee(...

FILE: sql/mysql.sql
  type users (line 19) | CREATE TABLE users (
  type last (line 35) | CREATE TABLE last (
  type rosterusers (line 42) | CREATE TABLE rosterusers (
  type i_rosteru_user_jid (line 56) | CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers(username(75), jid(...
  type i_rosteru_jid (line 57) | CREATE INDEX i_rosteru_jid ON rosterusers(jid)
  type rostergroups (line 59) | CREATE TABLE rostergroups (
  type pk_rosterg_user_jid (line 65) | CREATE INDEX pk_rosterg_user_jid ON rostergroups(username(75), jid(75))
  type sr_group (line 67) | CREATE TABLE sr_group (
  type i_sr_group_name (line 73) | CREATE UNIQUE INDEX i_sr_group_name ON sr_group(name)
  type sr_user (line 75) | CREATE TABLE sr_user (
  type i_sr_user_jid_group (line 81) | CREATE UNIQUE INDEX i_sr_user_jid_group ON sr_user(jid(75), grp(75))
  type i_sr_user_grp (line 82) | CREATE INDEX i_sr_user_grp ON sr_user(grp)
  type spool (line 84) | CREATE TABLE spool (
  type i_despool (line 91) | CREATE INDEX i_despool USING BTREE ON spool(username)
  type i_spool_created_at (line 92) | CREATE INDEX i_spool_created_at USING BTREE ON spool(created_at)
  type archive (line 94) | CREATE TABLE archive (
  type i_text (line 108) | CREATE FULLTEXT INDEX i_text ON archive(txt)
  type i_username_timestamp (line 109) | CREATE INDEX i_username_timestamp USING BTREE ON archive(username(191), ...
  type i_username_peer (line 110) | CREATE INDEX i_username_peer USING BTREE ON archive(username(191), peer(...
  type i_username_bare_peer (line 111) | CREATE INDEX i_username_bare_peer USING BTREE ON archive(username(191), ...
  type i_timestamp (line 112) | CREATE INDEX i_timestamp USING BTREE ON archive(timestamp)
  type i_archive_username_origin_id (line 113) | CREATE INDEX i_archive_username_origin_id USING BTREE ON archive(usernam...
  type archive_prefs (line 120) | CREATE TABLE archive_prefs (
  type vcard (line 128) | CREATE TABLE vcard (
  type vcard_search (line 134) | CREATE TABLE vcard_search (
  type i_vcard_search_lfn (line 161) | CREATE INDEX i_vcard_search_lfn       ON vcard_search(lfn)
  type i_vcard_search_lfamily (line 162) | CREATE INDEX i_vcard_search_lfamily   ON vcard_search(lfamily)
  type i_vcard_search_lgiven (line 163) | CREATE INDEX i_vcard_search_lgiven    ON vcard_search(lgiven)
  type i_vcard_search_lmiddle (line 164) | CREATE INDEX i_vcard_search_lmiddle   ON vcard_search(lmiddle)
  type i_vcard_search_lnickname (line 165) | CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname)
  type i_vcard_search_lbday (line 166) | CREATE INDEX i_vcard_search_lbday     ON vcard_search(lbday)
  type i_vcard_search_lctry (line 167) | CREATE INDEX i_vcard_search_lctry     ON vcard_search(lctry)
  type i_vcard_search_llocality (line 168) | CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality)
  type i_vcard_search_lemail (line 169) | CREATE INDEX i_vcard_search_lemail    ON vcard_search(lemail)
  type i_vcard_search_lorgname (line 170) | CREATE INDEX i_vcard_search_lorgname  ON vcard_search(lorgname)
  type i_vcard_search_lorgunit (line 171) | CREATE INDEX i_vcard_search_lorgunit  ON vcard_search(lorgunit)
  type privacy_default_list (line 173) | CREATE TABLE privacy_default_list (
  type privacy_list (line 178) | CREATE TABLE privacy_list (
  type i_privacy_list_username_name (line 185) | CREATE UNIQUE INDEX i_privacy_list_username_name USING BTREE ON privacy_...
  type privacy_list_data (line 187) | CREATE TABLE privacy_list_data (
  type i_privacy_list_data_id (line 200) | CREATE INDEX i_privacy_list_data_id ON privacy_list_data(id)
  type private_storage (line 202) | CREATE TABLE private_storage (
  type i_private_storage_username_namespace (line 209) | CREATE UNIQUE INDEX i_private_storage_username_namespace USING BTREE ON ...
  type roster_version (line 212) | CREATE TABLE roster_version (
  type pubsub_node (line 222) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 229) | CREATE INDEX i_pubsub_node_parent ON pubsub_node(parent(120))
  type i_pubsub_node_tuple (line 230) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node(host(71), node(120))
  type pubsub_node_option (line 232) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 237) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option(nodeid)
  type pubsub_node_owner (line 240) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 244) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner(nodeid)
  type pubsub_state (line 247) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 254) | CREATE INDEX i_pubsub_state_jid ON pubsub_state(jid(60))
  type i_pubsub_state_tuple (line 255) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state(nodeid, jid(60))
  type pubsub_item (line 258) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 266) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item(itemid(36))
  type i_pubsub_item_tuple (line 267) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item(nodeid, itemid(36))
  type pubsub_subscription_opt (line 270) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 275) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 277) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 284) | CREATE UNIQUE INDEX i_muc_room_name_host USING BTREE ON muc_room(name(75...
  type i_muc_room_host_created_at (line 285) | CREATE INDEX i_muc_room_host_created_at ON muc_room(host(75), created_at)
  type muc_registered (line 287) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 294) | CREATE INDEX i_muc_registered_nick USING BTREE ON muc_registered(nick(75))
  type i_muc_registered_jid_host (line 295) | CREATE UNIQUE INDEX i_muc_registered_jid_host USING BTREE ON muc_registe...
  type muc_online_room (line 297) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 304) | CREATE UNIQUE INDEX i_muc_online_room_name_host USING BTREE ON muc_onlin...
  type muc_online_users (line 306) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 315) | CREATE UNIQUE INDEX i_muc_online_users USING BTREE ON muc_online_users(u...
  type muc_room_subscribers (line 317) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 327) | CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_sub...
  type i_muc_room_subscribers_jid (line 328) | CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscrib...
  type motd (line 330) | CREATE TABLE motd (
  type caps_features (line 336) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 343) | CREATE INDEX i_caps_features_node_subnode ON caps_features(node(75), sub...
  type sm (line 345) | CREATE TABLE sm (
  type i_sid (line 355) | CREATE UNIQUE INDEX i_sid ON sm(usec, pid(75))
  type i_node (line 356) | CREATE INDEX i_node ON sm(node(75))
  type i_username (line 357) | CREATE INDEX i_username ON sm(username)
  type oauth_token (line 359) | CREATE TABLE oauth_token (
  type oauth_client (line 366) | CREATE TABLE oauth_client (
  type route (line 373) | CREATE TABLE route (
  type i_route (line 381) | CREATE UNIQUE INDEX i_route ON route(domain(75), server_host(75), node(7...
  type bosh (line 383) | CREATE TABLE bosh (
  type i_bosh_sid (line 389) | CREATE UNIQUE INDEX i_bosh_sid ON bosh(sid(75))
  type proxy65 (line 391) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 400) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 (sid(191))
  type i_proxy65_jid (line 401) | CREATE INDEX i_proxy65_jid ON proxy65 (jid_i(191))
  type push_session (line 403) | CREATE TABLE push_session (
  type i_push_usn (line 411) | CREATE UNIQUE INDEX i_push_usn ON push_session (username(191), service(1...
  type i_push_ut (line 412) | CREATE UNIQUE INDEX i_push_ut ON push_session (username(191), timestamp)
  type mix_channel (line 414) | CREATE TABLE mix_channel (
  type i_mix_channel (line 425) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel(191), service(...
  type i_mix_channel_serv (line 426) | CREATE INDEX i_mix_channel_serv ON mix_channel (service(191))
  type mix_participant (line 428) | CREATE TABLE mix_participant (
  type i_mix_participant (line 439) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel(191), ...
  type mix_subscription (line 441) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 450) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel(153)...
  type i_mix_subscription_chan_serv_node (line 451) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 453) | CREATE TABLE mix_pam (
  type i_mix_pam (line 461) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username(191), channel(191), s...
  type mqtt_pub (line 463) | CREATE TABLE mqtt_pub (
  type invite_token (line 478) | CREATE TABLE invite_token (
  type i_invite_token_username (line 489) | CREATE INDEX i_invite_token_username USING BTREE ON invite_token(usernam...
  type i_invite_token_invitee (line 490) | CREATE INDEX i_invite_token_invitee USING BTREE ON invite_token(invitee(...

FILE: sql/pg.new.sql
  type users (line 172) | CREATE TABLE users (
  type last (line 189) | CREATE TABLE last (
  type rosterusers (line 198) | CREATE TABLE rosterusers (
  type i_rosteru_sh_user_jid (line 213) | CREATE UNIQUE INDEX i_rosteru_sh_user_jid ON rosterusers USING btree (se...
  type i_rosteru_sh_jid (line 214) | CREATE INDEX i_rosteru_sh_jid ON rosterusers USING btree (server_host, jid)
  type rostergroups (line 217) | CREATE TABLE rostergroups (
  type i_rosterg_sh_user_jid (line 224) | CREATE INDEX i_rosterg_sh_user_jid ON rostergroups USING btree (server_h...
  type sr_group (line 226) | CREATE TABLE sr_group (
  type i_sr_group_sh_name (line 233) | CREATE UNIQUE INDEX i_sr_group_sh_name ON sr_group USING btree (server_h...
  type sr_user (line 235) | CREATE TABLE sr_user (
  type i_sr_user_sh_jid_grp (line 242) | CREATE UNIQUE INDEX i_sr_user_sh_jid_grp ON sr_user USING btree (server_...
  type i_sr_user_sh_grp (line 243) | CREATE INDEX i_sr_user_sh_grp ON sr_user USING btree (server_host, grp)
  type spool (line 245) | CREATE TABLE spool (
  type i_spool_sh_username (line 253) | CREATE INDEX i_spool_sh_username ON spool USING btree (server_host, user...
  type archive (line 255) | CREATE TABLE archive (
  type i_archive_sh_username_timestamp (line 270) | CREATE INDEX i_archive_sh_username_timestamp ON archive USING btree (ser...
  type i_archive_sh_username_peer (line 271) | CREATE INDEX i_archive_sh_username_peer ON archive USING btree (server_h...
  type i_archive_sh_username_bare_peer (line 272) | CREATE INDEX i_archive_sh_username_bare_peer ON archive USING btree (ser...
  type i_archive_sh_timestamp (line 273) | CREATE INDEX i_archive_sh_timestamp ON archive USING btree (server_host,...
  type i_archive_sh_username_origin_id (line 274) | CREATE INDEX i_archive_sh_username_origin_id ON archive USING btree (ser...
  type archive_prefs (line 281) | CREATE TABLE archive_prefs (
  type vcard (line 291) | CREATE TABLE vcard (
  type vcard_search (line 299) | CREATE TABLE vcard_search (
  type i_vcard_search_sh_lfn (line 328) | CREATE INDEX i_vcard_search_sh_lfn       ON vcard_search(server_host, lfn)
  type i_vcard_search_sh_lfamily (line 329) | CREATE INDEX i_vcard_search_sh_lfamily   ON vcard_search(server_host, lf...
  type i_vcard_search_sh_lgiven (line 330) | CREATE INDEX i_vcard_search_sh_lgiven    ON vcard_search(server_host, lg...
  type i_vcard_search_sh_lmiddle (line 331) | CREATE INDEX i_vcard_search_sh_lmiddle   ON vcard_search(server_host, lm...
  type i_vcard_search_sh_lnickname (line 332) | CREATE INDEX i_vcard_search_sh_lnickname ON vcard_search(server_host, ln...
  type i_vcard_search_sh_lbday (line 333) | CREATE INDEX i_vcard_search_sh_lbday     ON vcard_search(server_host, lb...
  type i_vcard_search_sh_lctry (line 334) | CREATE INDEX i_vcard_search_sh_lctry     ON vcard_search(server_host, lc...
  type i_vcard_search_sh_llocality (line 335) | CREATE INDEX i_vcard_search_sh_llocality ON vcard_search(server_host, ll...
  type i_vcard_search_sh_lemail (line 336) | CREATE INDEX i_vcard_search_sh_lemail    ON vcard_search(server_host, le...
  type i_vcard_search_sh_lorgname (line 337) | CREATE INDEX i_vcard_search_sh_lorgname  ON vcard_search(server_host, lo...
  type i_vcard_search_sh_lorgunit (line 338) | CREATE INDEX i_vcard_search_sh_lorgunit  ON vcard_search(server_host, lo...
  type privacy_default_list (line 340) | CREATE TABLE privacy_default_list (
  type privacy_list (line 347) | CREATE TABLE privacy_list (
  type i_privacy_list_sh_username_name (line 355) | CREATE UNIQUE INDEX i_privacy_list_sh_username_name ON privacy_list USIN...
  type privacy_list_data (line 357) | CREATE TABLE privacy_list_data (
  type i_privacy_list_data_id (line 370) | CREATE INDEX i_privacy_list_data_id ON privacy_list_data USING btree (id)
  type private_storage (line 372) | CREATE TABLE private_storage (
  type i_private_storage_sh_username_namespace (line 380) | CREATE UNIQUE INDEX i_private_storage_sh_username_namespace ON private_s...
  type roster_version (line 382) | CREATE TABLE roster_version (
  type pubsub_node (line 401) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 408) | CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent)
  type i_pubsub_node_tuple (line 409) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host...
  type pubsub_node_option (line 411) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 416) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btr...
  type pubsub_node_owner (line 418) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 422) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree...
  type pubsub_state (line 424) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 431) | CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid)
  type i_pubsub_state_tuple (line 432) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (no...
  type pubsub_item (line 434) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 442) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid)
  type i_pubsub_item_tuple (line 443) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (node...
  type pubsub_subscription_opt (line 445) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 450) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 452) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 460) | CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, ...
  type i_muc_room_host_created_at (line 461) | CREATE INDEX i_muc_room_host_created_at ON muc_room USING btree (host, c...
  type muc_registered (line 463) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 471) | CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick)
  type i_muc_registered_jid_host (line 472) | CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING bt...
  type muc_online_room (line 474) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 482) | CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING...
  type muc_online_users (line 484) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 494) | CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (...
  type muc_room_subscribers (line 496) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 505) | CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USI...
  type i_muc_room_subscribers_jid (line 506) | CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING bt...
  type i_muc_room_subscribers_host_room_jid (line 507) | CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_sub...
  type motd (line 509) | CREATE TABLE motd (
  type caps_features (line 517) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 524) | CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (...
  type sm (line 526) | CREATE TABLE sm (
  type i_sm_node (line 538) | CREATE INDEX i_sm_node ON sm USING btree (node)
  type i_sm_sh_username (line 539) | CREATE INDEX i_sm_sh_username ON sm USING btree (server_host, username)
  type oauth_token (line 541) | CREATE TABLE oauth_token (
  type i_oauth_token_token (line 548) | CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token)
  type oauth_client (line 550) | CREATE TABLE oauth_client (
  type route (line 557) | CREATE TABLE route (
  type i_route (line 565) | CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, n...
  type bosh (line 567) | CREATE TABLE bosh (
  type i_bosh_sid (line 573) | CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid)
  type proxy65 (line 575) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 584) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid)
  type i_proxy65_jid (line 585) | CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i)
  type push_session (line 587) | CREATE TABLE push_session (
  type i_push_session_susn (line 597) | CREATE UNIQUE INDEX i_push_session_susn ON push_session USING btree (ser...
  type i_push_session_sh_username_timestamp (line 598) | CREATE INDEX i_push_session_sh_username_timestamp ON push_session USING ...
  type mix_channel (line 600) | CREATE TABLE mix_channel (
  type i_mix_channel (line 611) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service)
  type i_mix_channel_serv (line 612) | CREATE INDEX i_mix_channel_serv ON mix_channel (service)
  type mix_participant (line 614) | CREATE TABLE mix_participant (
  type i_mix_participant (line 625) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, servi...
  type mix_subscription (line 627) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 636) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, ser...
  type i_mix_subscription_chan_serv_node (line 637) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 639) | CREATE TABLE mix_pam (
  type i_mix_pam (line 648) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, server_host, channel...
  type mqtt_pub (line 650) | CREATE TABLE mqtt_pub (
  type i_mqtt_topic_server (line 665) | CREATE UNIQUE INDEX i_mqtt_topic_server ON mqtt_pub (topic, server_host)
  type invite_token (line 667) | CREATE TABLE invite_token (
  type i_invite_token_username_server_host (line 678) | CREATE INDEX i_invite_token_username_server_host ON invite_token USING b...
  type i_invite_token_invitee (line 679) | CREATE INDEX i_invite_token_invitee ON invite_token USING btree (invitee)

FILE: sql/pg.sql
  type users (line 19) | CREATE TABLE users (
  type last (line 35) | CREATE TABLE last (
  type rosterusers (line 42) | CREATE TABLE rosterusers (
  type i_rosteru_user_jid (line 56) | CREATE UNIQUE INDEX i_rosteru_user_jid ON rosterusers USING btree (usern...
  type i_rosteru_jid (line 57) | CREATE INDEX i_rosteru_jid ON rosterusers USING btree (jid)
  type rostergroups (line 60) | CREATE TABLE rostergroups (
  type pk_rosterg_user_jid (line 66) | CREATE INDEX pk_rosterg_user_jid ON rostergroups USING btree (username, ...
  type sr_group (line 68) | CREATE TABLE sr_group (
  type i_sr_group_name (line 74) | CREATE UNIQUE INDEX i_sr_group_name ON sr_group USING btree (name)
  type sr_user (line 76) | CREATE TABLE sr_user (
  type i_sr_user_jid_grp (line 82) | CREATE UNIQUE INDEX i_sr_user_jid_grp ON sr_user USING btree (jid, grp)
  type i_sr_user_grp (line 83) | CREATE INDEX i_sr_user_grp ON sr_user USING btree (grp)
  type spool (line 85) | CREATE TABLE spool (
  type i_despool (line 92) | CREATE INDEX i_despool ON spool USING btree (username)
  type archive (line 94) | CREATE TABLE archive (
  type i_username_timestamp (line 108) | CREATE INDEX i_username_timestamp ON archive USING btree (username, time...
  type i_username_peer (line 109) | CREATE INDEX i_username_peer ON archive USING btree (username, peer)
  type i_username_bare_peer (line 110) | CREATE INDEX i_username_bare_peer ON archive USING btree (username, bare...
  type i_timestamp (line 111) | CREATE INDEX i_timestamp ON archive USING btree (timestamp)
  type i_archive_username_origin_id (line 112) | CREATE INDEX i_archive_username_origin_id ON archive USING btree (userna...
  type archive_prefs (line 119) | CREATE TABLE archive_prefs (
  type vcard (line 127) | CREATE TABLE vcard (
  type vcard_search (line 133) | CREATE TABLE vcard_search (
  type i_vcard_search_lfn (line 160) | CREATE INDEX i_vcard_search_lfn       ON vcard_search(lfn)
  type i_vcard_search_lfamily (line 161) | CREATE INDEX i_vcard_search_lfamily   ON vcard_search(lfamily)
  type i_vcard_search_lgiven (line 162) | CREATE INDEX i_vcard_search_lgiven    ON vcard_search(lgiven)
  type i_vcard_search_lmiddle (line 163) | CREATE INDEX i_vcard_search_lmiddle   ON vcard_search(lmiddle)
  type i_vcard_search_lnickname (line 164) | CREATE INDEX i_vcard_search_lnickname ON vcard_search(lnickname)
  type i_vcard_search_lbday (line 165) | CREATE INDEX i_vcard_search_lbday     ON vcard_search(lbday)
  type i_vcard_search_lctry (line 166) | CREATE INDEX i_vcard_search_lctry     ON vcard_search(lctry)
  type i_vcard_search_llocality (line 167) | CREATE INDEX i_vcard_search_llocality ON vcard_search(llocality)
  type i_vcard_search_lemail (line 168) | CREATE INDEX i_vcard_search_lemail    ON vcard_search(lemail)
  type i_vcard_search_lorgname (line 169) | CREATE INDEX i_vcard_search_lorgname  ON vcard_search(lorgname)
  type i_vcard_search_lorgunit (line 170) | CREATE INDEX i_vcard_search_lorgunit  ON vcard_search(lorgunit)
  type privacy_default_list (line 172) | CREATE TABLE privacy_default_list (
  type privacy_list (line 177) | CREATE TABLE privacy_list (
  type i_privacy_list_username_name (line 184) | CREATE UNIQUE INDEX i_privacy_list_username_name ON privacy_list USING b...
  type privacy_list_data (line 186) | CREATE TABLE privacy_list_data (
  type i_privacy_list_data_id (line 199) | CREATE INDEX i_privacy_list_data_id ON privacy_list_data USING btree (id)
  type private_storage (line 201) | CREATE TABLE private_storage (
  type i_private_storage_username_namespace (line 208) | CREATE UNIQUE INDEX i_private_storage_username_namespace ON private_stor...
  type roster_version (line 211) | CREATE TABLE roster_version (
  type pubsub_node (line 228) | CREATE TABLE pubsub_node (
  type i_pubsub_node_parent (line 235) | CREATE INDEX i_pubsub_node_parent ON pubsub_node USING btree (parent)
  type i_pubsub_node_tuple (line 236) | CREATE UNIQUE INDEX i_pubsub_node_tuple ON pubsub_node USING btree (host...
  type pubsub_node_option (line 238) | CREATE TABLE pubsub_node_option (
  type i_pubsub_node_option_nodeid (line 243) | CREATE INDEX i_pubsub_node_option_nodeid ON pubsub_node_option USING btr...
  type pubsub_node_owner (line 245) | CREATE TABLE pubsub_node_owner (
  type i_pubsub_node_owner_nodeid (line 249) | CREATE INDEX i_pubsub_node_owner_nodeid ON pubsub_node_owner USING btree...
  type pubsub_state (line 251) | CREATE TABLE pubsub_state (
  type i_pubsub_state_jid (line 258) | CREATE INDEX i_pubsub_state_jid ON pubsub_state USING btree (jid)
  type i_pubsub_state_tuple (line 259) | CREATE UNIQUE INDEX i_pubsub_state_tuple ON pubsub_state USING btree (no...
  type pubsub_item (line 261) | CREATE TABLE pubsub_item (
  type i_pubsub_item_itemid (line 269) | CREATE INDEX i_pubsub_item_itemid ON pubsub_item USING btree (itemid)
  type i_pubsub_item_tuple (line 270) | CREATE UNIQUE INDEX i_pubsub_item_tuple ON pubsub_item USING btree (node...
  type pubsub_subscription_opt (line 272) | CREATE TABLE pubsub_subscription_opt (
  type i_pubsub_subscription_opt (line 277) | CREATE UNIQUE INDEX i_pubsub_subscription_opt ON pubsub_subscription_opt...
  type muc_room (line 279) | CREATE TABLE muc_room (
  type i_muc_room_name_host (line 286) | CREATE UNIQUE INDEX i_muc_room_name_host ON muc_room USING btree (name, ...
  type i_muc_room_host_created_at (line 287) | CREATE INDEX i_muc_room_host_created_at ON muc_room USING btree (host, c...
  type muc_registered (line 289) | CREATE TABLE muc_registered (
  type i_muc_registered_nick (line 296) | CREATE INDEX i_muc_registered_nick ON muc_registered USING btree (nick)
  type i_muc_registered_jid_host (line 297) | CREATE UNIQUE INDEX i_muc_registered_jid_host ON muc_registered USING bt...
  type muc_online_room (line 299) | CREATE TABLE muc_online_room (
  type i_muc_online_room_name_host (line 306) | CREATE UNIQUE INDEX i_muc_online_room_name_host ON muc_online_room USING...
  type muc_online_users (line 308) | CREATE TABLE muc_online_users (
  type i_muc_online_users (line 317) | CREATE UNIQUE INDEX i_muc_online_users ON muc_online_users USING btree (...
  type muc_room_subscribers (line 319) | CREATE TABLE muc_room_subscribers (
  type i_muc_room_subscribers_host_jid (line 328) | CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USI...
  type i_muc_room_subscribers_jid (line 329) | CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING bt...
  type i_muc_room_subscribers_host_room_jid (line 330) | CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_sub...
  type motd (line 332) | CREATE TABLE motd (
  type caps_features (line 338) | CREATE TABLE caps_features (
  type i_caps_features_node_subnode (line 345) | CREATE INDEX i_caps_features_node_subnode ON caps_features USING btree (...
  type sm (line 347) | CREATE TABLE sm (
  type i_sm_sid (line 357) | CREATE UNIQUE INDEX i_sm_sid ON sm USING btree (usec, pid)
  type i_sm_node (line 358) | CREATE INDEX i_sm_node ON sm USING btree (node)
  type i_sm_username (line 359) | CREATE INDEX i_sm_username ON sm USING btree (username)
  type oauth_token (line 361) | CREATE TABLE oauth_token (
  type i_oauth_token_token (line 368) | CREATE UNIQUE INDEX i_oauth_token_token ON oauth_token USING btree (token)
  type oauth_client (line 370) | CREATE TABLE oauth_client (
  type route (line 377) | CREATE TABLE route (
  type i_route (line 385) | CREATE UNIQUE INDEX i_route ON route USING btree (domain, server_host, n...
  type bosh (line 387) | CREATE TABLE bosh (
  type i_bosh_sid (line 393) | CREATE UNIQUE INDEX i_bosh_sid ON bosh USING btree (sid)
  type proxy65 (line 395) | CREATE TABLE proxy65 (
  type i_proxy65_sid (line 404) | CREATE UNIQUE INDEX i_proxy65_sid ON proxy65 USING btree (sid)
  type i_proxy65_jid (line 405) | CREATE INDEX i_proxy65_jid ON proxy65 USING btree (jid_i)
  type push_session (line 407) | CREATE TABLE push_session (
  type i_push_usn (line 415) | CREATE UNIQUE INDEX i_push_usn ON push_session USING btree (username, se...
  type i_push_ut (line 416) | CREATE INDEX i_push_ut ON push_session USING btree (username, timestamp)
  type mix_channel (line 418) | CREATE TABLE mix_channel (
  type i_mix_channel (line 429) | CREATE UNIQUE INDEX i_mix_channel ON mix_channel (channel, service)
  type i_mix_channel_serv (line 430) | CREATE INDEX i_mix_channel_serv ON mix_channel (service)
  type mix_participant (line 432) | CREATE TABLE mix_participant (
  type i_mix_participant (line 443) | CREATE UNIQUE INDEX i_mix_participant ON mix_participant (channel, servi...
  type mix_subscription (line 445) | CREATE TABLE mix_subscription (
  type i_mix_subscription (line 454) | CREATE UNIQUE INDEX i_mix_subscription ON mix_subscription (channel, ser...
  type i_mix_subscription_chan_serv_node (line 455) | CREATE INDEX i_mix_subscription_chan_serv_node ON mix_subscription (chan...
  type mix_pam (line 457) | CREATE TABLE mix_pam (
  type i_mix_pam (line 465) | CREATE UNIQUE INDEX i_mix_pam ON mix_pam (username, channel, service)
  type mqtt_pub (line 467) | CREATE TABLE mqtt_pub (
  type i_mqtt_topic (line 481) | CREATE UNIQUE INDEX i_mqtt_topic ON mqtt_pub (topic)
  type invite_token (line 483) | CREATE TABLE invite_token (
  type i_invite_token_username (line 493) | CREATE INDEX i_invite_token_username ON invite_token USING btree (username)
  type i_invite_token_invitee (line 494) | CREATE INDEX i_invite_token_invitee ON invite_token USING btree (invitee)

FILE: test/ejabberd_SUITE_data/extauth.py
  function read_from_stdin (line 6) | def read_from_stdin(read_bytes):
  function read (line 12) | def read():
  function write (line 45) | def write(result):

FILE: test/elixir-config/attr_test.exs
  class Ejabberd.Config.AttrTest (line 1) | defmodule Ejabberd.Config.AttrTest

FILE: test/elixir-config/config_test.exs
  class Ejabberd.ConfigTest (line 1) | defmodule Ejabberd.ConfigTest

FILE: test/elixir-config/ejabberd_logger.exs
  class Ejabberd.Config.EjabberdLoggerTest (line 1) | defmodule Ejabberd.Config.EjabberdLoggerTest

FILE: test/elixir-config/shared/ejabberd.exs
  class Ejabberd.ConfigFile (line 1) | defmodule Ejabberd.ConfigFile
    method start (line 4) | def start do
    method shaper (line 11) | defp shaper do

FILE: test/elixir-config/shared/ejabberd_different_from_default.exs
  class Ejabberd.ConfigFile (line 1) | defmodule Ejabberd.ConfigFile
    method start (line 4) | def start do

FILE: test/elixir-config/shared/ejabberd_for_validation.exs
  class Ejabberd.ConfigFile (line 1) | defmodule Ejabberd.ConfigFile
    method start (line 4) | def start do

FILE: test/elixir-config/validation_test.exs
  class Ejabberd.Config.ValidationTest (line 1) | defmodule Ejabberd.Config.ValidationTest
Condensed preview — 592 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,340K chars).
[
  {
    "path": ".devcontainer/Dockerfile",
    "chars": 44,
    "preview": "FROM ghcr.io/processone/devcontainer:latest\n"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 189,
    "preview": "{\n\t\"name\": \"ejabberd\",\n\t\"build\": {\"dockerfile\": \"Dockerfile\"},\n\t\"extensions\": [\"erlang-ls.erlang-ls\"],\n\t\"postCreateComma"
  },
  {
    "path": ".devcontainer/prepare-container.sh",
    "chars": 143,
    "preview": "echo \"export PATH=/workspaces/ejabberd/_build/relive:$PATH\" >>$HOME/.bashrc\necho \"COOKIE\" >$HOME/.erlang.cookie\nchmod 40"
  },
  {
    "path": ".dockerignore",
    "chars": 628,
    "preview": ".git\n.win32\n.examples\n*.swp\n*~\n\\#*#\n.#*\n.edts\n*.dump\n/Makefile\n/config.log\n/config.status\n/config/releases.exs\n/configur"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 674,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nassignees: ''\n\n---\n\nBefore creating a ticket, p"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 751,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: Kind:Feature\nassignees: ''\n\n---\n\nBef"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE",
    "chars": 764,
    "preview": "We are open to contributions for ejabberd, as GitHub pull requests (PR).\nHere are a few points to consider before submit"
  },
  {
    "path": ".github/actions/manage-database/action.yml",
    "chars": 5898,
    "preview": "name: 'Manage Database'\n\ninputs:\n  for:\n    default: \"\"\n    description: 'One or more databases to manage:\n             "
  },
  {
    "path": ".github/actions/manage-ejabberd/action.yml",
    "chars": 6390,
    "preview": "name: 'Manage ejabberd'\n\ninputs:\n  for:\n    default: \"\"\n    description: 'Release method, one of:\n                  prod"
  },
  {
    "path": ".github/container/Dockerfile",
    "chars": 6537,
    "preview": "#' Define default build variables\nARG OTP_VSN='28.4.1.0'\nARG ELIXIR_VSN='1.19.5'\nARG UID='9000'\nARG USER='ejabberd'\nARG "
  },
  {
    "path": ".github/container/ejabberd-container-install.bat",
    "chars": 9083,
    "preview": "@echo off\r\n\r\n::\r\n::   ejabberd container installer for Windows\r\n::   -------------------------------------\r\n::          "
  },
  {
    "path": ".github/container/ejabberd.yml.example",
    "chars": 6309,
    "preview": "###\n###              ejabberd configuration file\n###\n### The parameters used in this configuration file are explained at"
  },
  {
    "path": ".github/container/ejabberdctl.template",
    "chars": 21035,
    "preview": "#!/bin/sh\n\n# define default configuration\nPOLL=true\nERL_MAX_PORTS=32000\nERL_PROCESSES=250000\nERL_MAX_ETS_TABLES=1400\nFIR"
  },
  {
    "path": ".github/lock.yml",
    "chars": 1151,
    "preview": "# Configuration for Lock Threads - https://github.com/dessant/lock-threads\n\n# Number of days of inactivity before a clos"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 11433,
    "preview": "name: CI\n\non:\n  push:\n    paths-ignore:\n    - '.devcontainer/**'\n    - 'lib/**'\n    - 'man/**'\n    - 'priv/**'\n    - '**"
  },
  {
    "path": ".github/workflows/container.yml",
    "chars": 3402,
    "preview": "name: Container\n\non:\n  push:\n    paths-ignore:\n    - '.devcontainer/**'\n    - 'lib/**'\n    - 'man/**'\n    - 'priv/**'\n  "
  },
  {
    "path": ".github/workflows/installers.yml",
    "chars": 3359,
    "preview": "name: Installers\n\non:\n  push:\n    paths-ignore:\n    - '.devcontainer/**'\n    - 'lib/**'\n    - 'man/**'\n    - 'priv/**'\n "
  },
  {
    "path": ".github/workflows/runtime.yml",
    "chars": 7034,
    "preview": "name: Runtime\n\non:\n  push:\n    paths:\n    - '*'\n    - '!*.md'\n    - '.github/workflows/runtime.yml'\n    - 'checkouts/**'"
  },
  {
    "path": ".github/workflows/weekly.yml",
    "chars": 4914,
    "preview": "name: Weekly\n\non:\n  schedule:\n    - cron: '30 9 * * 0'\n\njobs:\n\n  #######################################################"
  },
  {
    "path": ".gitignore",
    "chars": 777,
    "preview": "#\n# You can add personal rules in your file .git/info/exclude\n*.swp\n*~\n\\#*#\n.#*\n.edts\n.tool-versions\n*.dump\n/Makefile\n/d"
  },
  {
    "path": ".shellcheckrc",
    "chars": 98,
    "preview": "disable=SC2016,SC2086,SC2089,SC2090\nexternal-sources=true\nsource=ejabberdctl.cfg.example\nshell=sh\n"
  },
  {
    "path": ".vscode/extensions.json",
    "chars": 80,
    "preview": "{\n        \"recommendations\": [\n                \"erlang-ls.erlang-ls\"\n        ]\n}"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 2793,
    "preview": "{\n        \"version\": \"0.2.0\",\n        \"configurations\": [\n                {\n                        \"name\": \"Relive (Vim"
  },
  {
    "path": ".vscode/relive.sh",
    "chars": 108,
    "preview": "[ ! -f Makefile ] \\\n    && ./autogen.sh \\\n    && ./configure --with-rebar=rebar3 \\\n    && make\n\nmake relive\n"
  },
  {
    "path": ".vscode/settings.json",
    "chars": 601,
    "preview": "{\n    \"editor.tabSize\": 8,\n    \"remote.portsAttributes\": {\n        \"1883\": {\"label\": \"MQTT\", \"onAutoForward\": \"silent\"},"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 95756,
    "preview": "## Version 26.02\n\n- Fixes issue with adding hats data in presences send by group chats ([#4516](https://github.com/proce"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 4601,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "COMPILE.md",
    "chars": 3065,
    "preview": "Compile and Install ejabberd\n============================\n\nThis document explains how to compile and install ejabberd\nfr"
  },
  {
    "path": "CONTAINER.md",
    "chars": 35299,
    "preview": "\n[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/processone/ejabberd?sort=semver&logo=embarcadero&lab"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 6396,
    "preview": "# Contributing to ejabberd\n\nWe'd love for you to contribute to our source code and to make ejabberd even better than it "
  },
  {
    "path": "CONTRIBUTORS.md",
    "chars": 778,
    "preview": "# Contributors\n\nWe would like to thanks official ejabberd source code contributors:\n\n- Sergey Abramyan\n- Badlop\n- Ludovi"
  },
  {
    "path": "COPYING",
    "chars": 18132,
    "preview": "As a special exception, the authors give permission to link this program\nwith the OpenSSL library and distribute the res"
  },
  {
    "path": "Makefile.in",
    "chars": 22058,
    "preview": "#.\n#' definitions\n#\n\n# Only required for Erlang/OTP 25\nMAYBE=ERL_FLAGS=\"-enable-feature maybe_expr\"\n\nESCRIPT = @ESCRIPT@"
  },
  {
    "path": "README.md",
    "chars": 6775,
    "preview": "\n<p align=\"center\">\n    <img src=\"https://www.process-one.net/wp-content/uploads/2022/05/ejabberd-logo-rounded-index.png"
  },
  {
    "path": "SECURITY.md",
    "chars": 2102,
    "preview": "# Security Policy\n\n## Supported Versions\n\nWe recommend that all users always use the latest version of ejabberd.\n\nTo ens"
  },
  {
    "path": "_checkouts/configure_deps/rebar.config",
    "chars": 37,
    "preview": "{erl_opts, [debug_info]}.\n{deps, []}."
  },
  {
    "path": "_checkouts/configure_deps/src/configure_deps.app.src",
    "chars": 232,
    "preview": "{application, configure_deps,\n [{description, \"A rebar3 plugin to explicitly run configure on dependencies\"},\n  {vsn, \"0"
  },
  {
    "path": "_checkouts/configure_deps/src/configure_deps.erl",
    "chars": 184,
    "preview": "-module(configure_deps).\n\n-export([init/1]).\n\n-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.\ninit(State) ->\n    {"
  },
  {
    "path": "_checkouts/configure_deps/src/configure_deps_prv.erl",
    "chars": 2232,
    "preview": "-module(configure_deps_prv).\n\n-export([init/1, do/1, format_error/1]).\n\n-define(PROVIDER, 'configure-deps').\n-define(DEP"
  },
  {
    "path": "autogen.sh",
    "chars": 52,
    "preview": "# generate a new autoconf\naclocal -I m4\nautoconf -f\n"
  },
  {
    "path": "config/ejabberd.exs",
    "chars": 3263,
    "preview": "defmodule Ejabberd.ConfigFile do\n  use Ejabberd.Config\n\n  def start do\n    [loglevel: 4,\n     log_rotate_size: 10485760,"
  },
  {
    "path": "config/runtime.exs",
    "chars": 418,
    "preview": "import Config\n\nrootdefault = case System.get_env(\"RELIVE\", \"false\") do\n  \"true\" -> \"_build/relive\"\n  \"false\" -> \"\"\nend\n\n"
  },
  {
    "path": "configure.ac",
    "chars": 12820,
    "preview": "#                                               -*- Autoconf -*-\n# Process this file with autoconf to produce a configur"
  },
  {
    "path": "configure.bat",
    "chars": 303,
    "preview": "\n@if \"x%1\"==\"x--help\" goto usage\n\n@set arg=dynamic\n@if \"x%1\"==\"x--static\" set arg=static\n\n@echo Configuring for %arg% bu"
  },
  {
    "path": "cover.spec",
    "chars": 114,
    "preview": "{level, details}.\n{incl_dirs, [\"src\", \"ebin\"]}.\n{excl_mods, [eldap, 'ELDAPv3']}.\n{export, \"logs/all.coverdata\"}.\n\n"
  },
  {
    "path": "ejabberd.doap",
    "chars": 33214,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n         xmlns=\""
  },
  {
    "path": "ejabberd.init.template",
    "chars": 1352,
    "preview": "#! /bin/sh\n\n### BEGIN INIT INFO\n# Provides:          ejabberd\n# Required-Start:    $remote_fs $network $named $time\n# Re"
  },
  {
    "path": "ejabberd.service.template",
    "chars": 491,
    "preview": "[Unit]\nDescription=XMPP Server\nAfter=network.target\n\n[Service]\nType=notify\nUser=@installuser@\nGroup=@installuser@\nLimitN"
  },
  {
    "path": "ejabberd.yml.example",
    "chars": 5441,
    "preview": "###\n###              ejabberd configuration file\n###\n### The parameters used in this configuration file are explained at"
  },
  {
    "path": "ejabberdctl.cfg.example",
    "chars": 6642,
    "preview": "#\n# In this file you can configure options that are passed by ejabberdctl\n# to the erlang runtime system when starting e"
  },
  {
    "path": "ejabberdctl.template",
    "chars": 19710,
    "preview": "#!/bin/sh\n\n# define default configuration\nPOLL=true\nERL_MAX_PORTS=32000\nERL_PROCESSES=250000\nERL_MAX_ETS_TABLES=1400\nFIR"
  },
  {
    "path": "elvis.config",
    "chars": 2450,
    "preview": "[\n {\n  elvis,\n  [\n   {config,\n    [#{dirs => [\"src\"],\n       filter => \"*.erl\",\n       ignore => ['ELDAPv3', eldap_filte"
  },
  {
    "path": "erlang_ls.config",
    "chars": 608,
    "preview": "#otp_path: \"/usr/lib/erlang\"\n#plt_path: \"_build/default/rebar3_24.3.3_plt\"\n#code_reload:\n#  node: ejabberd@localhost\napp"
  },
  {
    "path": "include/ELDAPv3.hrl",
    "chars": 2166,
    "preview": "%% Generated by the Erlang ASN.1 compiler version:2.0.1\n%% Purpose: Erlang record definitions for each named and unnamed"
  },
  {
    "path": "include/bosh.hrl",
    "chars": 1690,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_auth.hrl",
    "chars": 1110,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_commands.hrl",
    "chars": 6167,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_ctl.hrl",
    "chars": 1052,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_db_serialize.hrl",
    "chars": 2093,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_http.hrl",
    "chars": 2471,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_oauth.hrl",
    "chars": 1478,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_router.hrl",
    "chars": 233,
    "preview": "-define(ROUTES_CACHE, routes_cache).\n\n-type local_hint() :: integer() | {apply, atom(), atom()}.\n\n-record(route, {domain"
  },
  {
    "path": "include/ejabberd_sm.hrl",
    "chars": 1488,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_sql.hrl",
    "chars": 2832,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_sql_pt.hrl",
    "chars": 1019,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/ejabberd_web_admin.hrl",
    "chars": 4009,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/eldap.hrl",
    "chars": 3408,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/http_bind.hrl",
    "chars": 1677,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/logger.hrl",
    "chars": 2792,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_announce.hrl",
    "chars": 1156,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_antispam.hrl",
    "chars": 1504,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_caps.hrl",
    "chars": 1087,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_invites.hrl",
    "chars": 1331,
    "preview": "-define(INVITE_TOKEN_EXPIRE_SECONDS_DEFAULT, 5*86400).\n-define(INVITE_TOKEN_LENGTH_DEFAULT, 24).\n\n-define(NS_INVITE_INVI"
  },
  {
    "path": "include/mod_last.hrl",
    "chars": 1128,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_mam.hrl",
    "chars": 1690,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_matrix_gw.hrl",
    "chars": 1594,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_muc.hrl",
    "chars": 1737,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_muc_room.hrl",
    "chars": 6435,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_offline.hrl",
    "chars": 1364,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_privacy.hrl",
    "chars": 1863,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_private.hrl",
    "chars": 1165,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_proxy65.hrl",
    "chars": 1865,
    "preview": "%%%----------------------------------------------------------------------\n%%% RFC 1928 constants.\n%%%\n%%%\n%%% ejabberd, "
  },
  {
    "path": "include/mod_push.hrl",
    "chars": 1295,
    "preview": "%%%----------------------------------------------------------------------\n%%% ejabberd, Copyright (C) 2017-2026 ProcessO"
  },
  {
    "path": "include/mod_roster.hrl",
    "chars": 1863,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_shared_roster.hrl",
    "chars": 1226,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mod_vcard.hrl",
    "chars": 1292,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/mqtt.hrl",
    "chars": 8944,
    "preview": "%%%-------------------------------------------------------------------\n%%% @author Evgeny Khramtsov <ekhramtsov@process-"
  },
  {
    "path": "include/pubsub.hrl",
    "chars": 5508,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "include/translate.hrl",
    "chars": 22,
    "preview": "-define(T(S), <<S>>).\n"
  },
  {
    "path": "inetrc",
    "chars": 111,
    "preview": "{lookup,[\"file\",\"native\"]}.\n{host,{127,0,0,1}, [\"localhost\",\"hostalias\"]}.\n{file, resolv, \"/etc/resolv.conf\"}.\n"
  },
  {
    "path": "install-sh",
    "chars": 13663,
    "preview": "#!/bin/sh\n# install - install a program, script, or datafile\n\nscriptversion=2009-04-28.21; # UTC\n\n# This originates from"
  },
  {
    "path": "lib/ejabberd/config/attr.ex",
    "chars": 4128,
    "preview": "defmodule Ejabberd.Config.Attr do\n  @moduledoc \"\"\"\n  Module used to work with the attributes parsed from\n  an elixir blo"
  },
  {
    "path": "lib/ejabberd/config/config.ex",
    "chars": 3794,
    "preview": "defmodule Ejabberd.Config do\n  @moduledoc \"\"\"\n  Base module for configuration file.\n\n  Imports macros for the config DSL"
  },
  {
    "path": "lib/ejabberd/config/ejabberd_hook.ex",
    "chars": 509,
    "preview": "defmodule Ejabberd.Config.EjabberdHook do\n  @moduledoc \"\"\"\n  Module containing functions for manipulating\n  ejabberd hoo"
  },
  {
    "path": "lib/ejabberd/config/ejabberd_module.ex",
    "chars": 2054,
    "preview": "defmodule Ejabberd.Config.EjabberdModule do\n  @moduledoc \"\"\"\n  Module representing a module block in the configuration f"
  },
  {
    "path": "lib/ejabberd/config/logger/ejabberd_logger.ex",
    "chars": 1348,
    "preview": "defmodule Ejabberd.Config.EjabberdLogger do\n  @moduledoc \"\"\"\n  Module used to log validation errors given validated modu"
  },
  {
    "path": "lib/ejabberd/config/opts_formatter.ex",
    "chars": 1310,
    "preview": "defmodule Ejabberd.Config.OptsFormatter do\n  @moduledoc \"\"\"\n  Module for formatting options parsed into the format\n  eja"
  },
  {
    "path": "lib/ejabberd/config/store.ex",
    "chars": 1286,
    "preview": "defmodule Ejabberd.Config.Store do\n  @moduledoc \"\"\"\n    Module used for storing the modules parsed from\n    the configur"
  },
  {
    "path": "lib/ejabberd/config/validator/validation.ex",
    "chars": 1241,
    "preview": "defmodule Ejabberd.Config.Validation do\n  @moduledoc \"\"\"\n  Module used to validate a list of modules.\n  \"\"\"\n\n  alias Eja"
  },
  {
    "path": "lib/ejabberd/config/validator/validator_attrs.ex",
    "chars": 879,
    "preview": "defmodule Ejabberd.Config.Validator.Attrs do\n  @moduledoc \"\"\"\n  Validator module used to validate attributes.\n  \"\"\"\n\n  i"
  },
  {
    "path": "lib/ejabberd/config/validator/validator_dependencies.ex",
    "chars": 1004,
    "preview": "defmodule Ejabberd.Config.Validator.Dependencies do\n  @moduledoc \"\"\"\n  Validator module used to validate dependencies sp"
  },
  {
    "path": "lib/ejabberd/config/validator/validator_utility.ex",
    "chars": 780,
    "preview": "defmodule Ejabberd.Config.ValidatorUtility do\n  @moduledoc \"\"\"\n  Module used as a base validator for validation modules."
  },
  {
    "path": "lib/ejabberd/config_util.ex",
    "chars": 428,
    "preview": "defmodule Ejabberd.ConfigUtil do\n  @moduledoc \"\"\"\n  Module containing utility functions for\n  the config file.\n  \"\"\"\n\n  "
  },
  {
    "path": "lib/ejabberd/hooks.ex",
    "chars": 371,
    "preview": "defmodule Ejabberd.Hooks do\n\n  # Generic hook setting features\n  def add(hook_name, host, module, function, priority) do"
  },
  {
    "path": "lib/ejabberd/logger.ex",
    "chars": 390,
    "preview": "defmodule Ejabberd.Logger do\n\n  def critical(message, args \\\\ []), do: :logger.critical(message, args)\n  def error(messa"
  },
  {
    "path": "lib/ejabberd_auth_example.ex",
    "chars": 1044,
    "preview": "defmodule Ejabberd.Auth.Example do\n\n  @moduledoc \"\"\"\n  Example ejabberd auth method written in Elixir.\n\n  This is an exa"
  },
  {
    "path": "lib/mix/tasks/deps.tree.ex",
    "chars": 3021,
    "preview": "defmodule Mix.Tasks.Ejabberd.Deps.Tree do\n  use Mix.Task\n\n  alias Ejabberd.Config.EjabberdModule\n\n  @shortdoc \"Lists all"
  },
  {
    "path": "lib/mod_example.ex",
    "chars": 967,
    "preview": "defmodule Ejabberd.Module.Example do\n\n  @moduledoc \"\"\"\n  Example ejabberd module written in Elixir.\n\n  This is an exampl"
  },
  {
    "path": "m4/ax_lib_sqlite3.m4",
    "chars": 5117,
    "preview": "# ===========================================================================\n#      http://www.gnu.org/software/autocon"
  },
  {
    "path": "m4/erlang-extra.m4",
    "chars": 2447,
    "preview": "dnl erlang-extra.m4\n\nAC_DEFUN([ERLANG_SUBST_LIB_VER],\n[AC_ERLANG_CHECK_LIB([$1])\nERLANG_LIB_VER_SUBST=\"$ERLANG_LIB_VER_S"
  },
  {
    "path": "man/ejabberd.yml.5",
    "chars": 253036,
    "preview": "'\\\" t\n.\\\"     Title: ejabberd.yml\n.\\\"    Author: [see the \"AUTHOR\" section]\n.\\\" Generator: DocBook XSL Stylesheets vsnap"
  },
  {
    "path": "mix.exs",
    "chars": 13328,
    "preview": "defmodule Ejabberd.MixProject do\n  use Mix.Project\n\n  def project do\n    [app: :ejabberd,\n     source_url: \"https://gith"
  },
  {
    "path": "package.json",
    "chars": 585,
    "preview": "{\n  \"name\": \"ejabberd\",\n  \"version\": \"1.0.0\",\n  \"dependencies\": {\n    \"bootstrap\": \"^5.3.8\",\n    \"jquery\": \"^4.0.0\"\n  },"
  },
  {
    "path": "plugins/configure_deps.erl",
    "chars": 107,
    "preview": "-module(configure_deps).\n-export(['configure-deps'/2]).\n\n'configure-deps'(Config, Vals) ->\n  {ok, Config}.\n"
  },
  {
    "path": "plugins/deps_erl_opts.erl",
    "chars": 548,
    "preview": "-module(deps_erl_opts).\n-export([preprocess/2]).\n\npreprocess(Config, Dirs) ->\n    ExtraOpts = rebar_config:get(Config, d"
  },
  {
    "path": "plugins/override_deps_versions2.erl",
    "chars": 4037,
    "preview": "-module(override_deps_versions2).\n-export([preprocess/2, 'pre_update-deps'/2, new_replace/1, new_replace/0]).\n\npreproces"
  },
  {
    "path": "plugins/override_opts.erl",
    "chars": 1367,
    "preview": "-module(override_opts).\n-export([preprocess/2]).\n\noverride_opts(override, Config, Opts) ->\n    lists:foldl(fun({Opt, Val"
  },
  {
    "path": "priv/css/admin.css",
    "chars": 5231,
    "preview": "html,body {\n  margin: 0;\n  padding: 0;\n  height: 100%;\n  background: #f9f9f9;\n  font-family: sans-serif;\n}\nbody {\n  min-"
  },
  {
    "path": "priv/css/bosh.css",
    "chars": 879,
    "preview": "body {\n    margin: 0;\n    padding: 0;\n    font-family: sans-serif;\n    color: #fff;\n}\nh1 {\n    font-size: 3em;\n    color"
  },
  {
    "path": "priv/css/muc.css",
    "chars": 2027,
    "preview": ".ts {color: #AAAAAA; text-decoration: none;}\n.mrcm {color: #009900; font-style: italic; font-weight: bold;}\n.msc {color:"
  },
  {
    "path": "priv/css/oauth.css",
    "chars": 1587,
    "preview": "body {\n    margin: 0;\n    padding: 0;\n\n    font-family: sans-serif;\n    color: #fff;\n}\n\nh1 {\n    font-size: 3em;\n    col"
  },
  {
    "path": "priv/css/register.css",
    "chars": 934,
    "preview": "@viewport {\n  width: device-width;\n  zoom: 1.0;\n}\n\nhtml,body {\n  font-family: sans-serif;\n  background: white;\n\n  paddin"
  },
  {
    "path": "priv/js/admin.js",
    "chars": 360,
    "preview": "\nfunction selectAll() {\n  for(i=0;i<document.forms[0].elements.length;i++)\n  { var e = document.forms[0].elements[i];\n  "
  },
  {
    "path": "priv/js/muc.js",
    "chars": 534,
    "preview": "// Show/Hide an element\nfunction sh(e) {\n    if (document.getElementById(e).style.display=='none') {\n\tdocument.getElemen"
  },
  {
    "path": "priv/lua/redis_sm.lua",
    "chars": 557,
    "preview": "redis.replicate_commands()\nlocal cursor = redis.call('GET', KEYS[3]) or 0\nlocal scan_result = redis.call('HSCAN', KEYS[1"
  },
  {
    "path": "priv/mod_invites/apps.html",
    "chars": 1902,
    "preview": "        <div class=\"container mb-0\">\n            <div class=\"row\">\n                {% for item in apps %}\n              "
  },
  {
    "path": "priv/mod_invites/apps.json",
    "chars": 7661,
    "preview": "[\n    {\n        \"download\": {\n            \"buttons\": [\n                {\n                    \"image\": \"{{ static }}/logo"
  },
  {
    "path": "priv/mod_invites/base.html",
    "chars": 2073,
    "preview": "{% extends \"base_min.html\" %}\n\n{% block rel_alternate %}<link rel=\"alternate\" href=\"{{ uri }}\">{% endblock %}\n\n{% block "
  },
  {
    "path": "priv/mod_invites/base_min.html",
    "chars": 1832,
    "preview": "<!DOCTYPE html>\n<html lang=\"{{ lang }}\">\n  <head>\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=de"
  },
  {
    "path": "priv/mod_invites/client.html",
    "chars": 5305,
    "preview": "{% extends \"base.html\" %}\n{% block title %}{% trans \"Create Account\" %} | {{ app.name }} | {{ site_name }}{% endblock %}"
  },
  {
    "path": "priv/mod_invites/copyright",
    "chars": 945,
    "preview": "The file static/illus-empty.svg is included under the following\nlicense:\n\nCopyright 2020 Katerina Limpitsouni\n\nAll image"
  },
  {
    "path": "priv/mod_invites/invite.html",
    "chars": 1235,
    "preview": "{% extends \"base.html\" %}\n{% block title %}{% trans \"Create Account\" %} | {{ site_name }}{% endblock %}\n{% block content"
  },
  {
    "path": "priv/mod_invites/invite_invalid.html",
    "chars": 540,
    "preview": "{% extends \"base_min.html\" %}\n{% block title %}{% trans \"Invite Expired\" %} | {{ site_name }}{% endblock %}\n{% block for"
  },
  {
    "path": "priv/mod_invites/register.html",
    "chars": 5695,
    "preview": "{% extends \"base_min.html\" %}\n\n{% block title %}{% if error %}{% trans \"Registration Error\" %}{% else %}{% trans \"Regist"
  },
  {
    "path": "priv/mod_invites/register_error.html",
    "chars": 404,
    "preview": "{% extends \"base_min.html\" %}\n{% block form_class %}container col-md-8 col-md-offset-2 col-sm-8 cold-sm-offset-2 col-lg-"
  },
  {
    "path": "priv/mod_invites/register_success.html",
    "chars": 5363,
    "preview": "{% extends \"base_min.html\" %}\n{% block form_class %}container col-md-8 col-md-offset-2 col-sm-8 cold-sm-offset-2 col-lg-"
  },
  {
    "path": "priv/mod_invites/roster.html",
    "chars": 1985,
    "preview": "{% extends \"base.html\" %}\n\n{% block title %}{% trans \"Add Contact\" %} | {{ site_name }}{% endblock %}\n{% block h1 %}{% b"
  },
  {
    "path": "priv/mod_invites/static/invite.css",
    "chars": 1166,
    "preview": "#qr-invite-page{\n    width: 256px;\n}\n.invite-download-button {\n    max-width: 160px;\n}\n.clear-both {\n    clear: both;\n}\n"
  },
  {
    "path": "priv/mod_invites/static/invite.js",
    "chars": 6114,
    "preview": "(function () {\n    document.documentElement.setAttribute('data-bs-theme', (window.matchMedia('(prefers-color-scheme: dar"
  },
  {
    "path": "priv/msgs/ar.msg",
    "chars": 454,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/bg.msg",
    "chars": 62194,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/ca.msg",
    "chars": 63201,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/cs.msg",
    "chars": 33451,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/de.msg",
    "chars": 63428,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/el.msg",
    "chars": 49927,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/eo.msg",
    "chars": 21958,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/es.msg",
    "chars": 64097,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/fr.msg",
    "chars": 43557,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/gl.msg",
    "chars": 26499,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/he.msg",
    "chars": 19979,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/hu.msg",
    "chars": 28726,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/id.msg",
    "chars": 31102,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/it.msg",
    "chars": 48553,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/ja.msg",
    "chars": 21591,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/nl.msg",
    "chars": 19568,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/no.msg",
    "chars": 20604,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/pl.msg",
    "chars": 26145,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/pt-br.msg",
    "chars": 47811,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/pt.msg",
    "chars": 48190,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/ru.msg",
    "chars": 46866,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/sk.msg",
    "chars": 17950,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/sq.msg",
    "chars": 17777,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/sv.msg",
    "chars": 14520,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/ta.msg",
    "chars": 48431,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/th.msg",
    "chars": 11402,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/tr.msg",
    "chars": 18186,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/uk.msg",
    "chars": 46884,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/vi.msg",
    "chars": 12303,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/wa.msg",
    "chars": 20473,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "priv/msgs/zh.msg",
    "chars": 42159,
    "preview": "%% Generated automatically\n%% DO NOT EDIT: run `make translations` instead\n%% To improve translations please read:\n%%   "
  },
  {
    "path": "rebar.config",
    "chars": 14375,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "rebar.config.script",
    "chars": 14058,
    "preview": "%%%----------------------------------------------------------------------\n%%%\n%%% ejabberd, Copyright (C) 2002-2026   Pr"
  },
  {
    "path": "rel/files/erl",
    "chars": 1118,
    "preview": "#!/bin/sh\n\n## This script replaces the default \"erl\" in erts-VSN/bin. This is necessary\n## as escript depends on erl and"
  },
  {
    "path": "rel/files/install_upgrade.escript",
    "chars": 1710,
    "preview": "#!/usr/bin/env escript\n%%! -noshell -noinput\n%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-\n%% ex:"
  },
  {
    "path": "rel/relive.config",
    "chars": 224,
    "preview": "[{mnesia, [{dir, \"_build/relive/database\"}]},\n {sync,[{src_dirs, {replace, [{\"ejabberd/src\", []}]}}]},\n {ejabberd, [{con"
  },
  {
    "path": "rel/relive.escript",
    "chars": 849,
    "preview": "#!/usr/bin/env escript\n\nmain(Args) ->\n    Target = case Args of\n        [\"ctl\"] -> \"relivectl\";\n        _ -> \"relive\"\n  "
  },
  {
    "path": "rel/reltool.config.script",
    "chars": 4268,
    "preview": "%%%-------------------------------------------------------------------\n%%% @author Evgeniy Khramtsov <ekhramtsov@process"
  },
  {
    "path": "rel/setup-dev.sh",
    "chars": 1163,
    "preview": "printf \"===> Preparing dev configuration files: \"\n\nPWD_DIR=$(pwd)\nREL_DIR=$PWD_DIR/_build/dev/rel/ejabberd/\nCON_DIR=$REL"
  },
  {
    "path": "rel/setup-relive.sh",
    "chars": 1255,
    "preview": "#!/bin/sh\n\nPWD_DIR=$(pwd)\nTARGET=$1\nREL_DIR=$PWD_DIR/_build/$TARGET/\nCON_DIR=$REL_DIR/conf/\n\n[ -z \"$REL_DIR_TEMP\" ] && R"
  },
  {
    "path": "rel/sys.config",
    "chars": 93,
    "preview": "[{ejabberd, [{config, \"conf/ejabberd.yml\"},\n             {log_path, \"logs/ejabberd.log\"}]}].\n"
  },
  {
    "path": "rel/vm.args",
    "chars": 1002,
    "preview": "## Name of the node\n-sname ejabberd@localhost\n\n## Cookie for distributed erlang\n#-setcookie ejabberd\n\n-mnesia dir \\\"data"
  },
  {
    "path": "sql/lite.new.sql",
    "chars": 15386,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "sql/lite.sql",
    "chars": 13714,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "sql/mssql.new.sql",
    "chars": 28904,
    "preview": "--\r\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\r\n--\r\n-- This program is free software; you can redistribute it and"
  },
  {
    "path": "sql/mssql.sql",
    "chars": 26999,
    "preview": "--\r\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\r\n--\r\n-- This program is free software; you can redistribute it and"
  },
  {
    "path": "sql/mysql.new.sql",
    "chars": 20459,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "sql/mysql.old-to-new.sql",
    "chars": 13727,
    "preview": "SET @DEFAULT_HOST = ''; -- Please fill with name of your current host name\n\nBEGIN;\nDELIMITER ##\nCREATE PROCEDURE update_"
  },
  {
    "path": "sql/mysql.sql",
    "chars": 18322,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "sql/pg.new.sql",
    "chars": 24920,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "sql/pg.sql",
    "chars": 14900,
    "preview": "--\n-- ejabberd, Copyright (C) 2002-2026   ProcessOne\n--\n-- This program is free software; you can redistribute it and/or"
  },
  {
    "path": "src/ELDAPv3.erl",
    "chars": 106724,
    "preview": "%% Generated by the Erlang ASN.1 BER_V2-compiler version, utilizing bit-syntax:2.0.1\n%% Purpose: encoder and decoder to "
  },
  {
    "path": "src/acl.erl",
    "chars": 12899,
    "preview": "%%%----------------------------------------------------------------------\n%%% ejabberd, Copyright (C) 2002-2026   Proces"
  },
  {
    "path": "src/econf.erl",
    "chars": 20644,
    "preview": "%%%----------------------------------------------------------------------\n%%% File    : econf.erl\n%%% Purpose : Validato"
  },
  {
    "path": "src/ejabberd.app.src.script",
    "chars": 1802,
    "preview": "{Vars, ElixirApps} = case file:consult(filename:join([filename:dirname(SCRIPT), \"..\", \"vars.config\"])) of\n    {ok, Terms"
  },
  {
    "path": "src/ejabberd.erl",
    "chars": 7305,
    "preview": "%%%----------------------------------------------------------------------\n%%% File    : ejabberd.erl\n%%% Author  : Alexe"
  },
  {
    "path": "src/ejabberd_access_permissions.erl",
    "chars": 12750,
    "preview": "%%%-------------------------------------------------------------------\n%%% File    : ejabberd_access_permissions.erl\n%%%"
  },
  {
    "path": "src/ejabberd_acme.erl",
    "chars": 23200,
    "preview": "%%%----------------------------------------------------------------------\n%%% ejabberd, Copyright (C) 2002-2026   Proces"
  },
  {
    "path": "src/ejabberd_admin.erl",
    "chars": 65846,
    "preview": "%%%-------------------------------------------------------------------\n%%% File    : ejabberd_admin.erl\n%%% Author  : Mi"
  },
  {
    "path": "src/ejabberd_app.erl",
    "chars": 6840,
    "preview": "%%%----------------------------------------------------------------------\n%%% File    : ejabberd_app.erl\n%%% Author  : A"
  },
  {
    "path": "src/ejabberd_auth.erl",
    "chars": 35994,
    "preview": "%%%----------------------------------------------------------------------\n%%% File    : ejabberd_auth.erl\n%%% Author  : "
  }
]

// ... and 392 more files (download for full content)

About this extraction

This page contains the full source code of the processone/ejabberd GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 592 files (7.5 MB), approximately 2.0M tokens, and a symbol index with 780 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!