Software /
code /
prosody-modules
Changeset
6209:d611ed13df7e draft
Merge
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Tue, 18 Mar 2025 00:16:25 +0700 |
parents | 6208:e20901443eae |
children | 6210:24316a399978 |
files | misc/README.markdown misc/README.md mod_addressing/README.markdown mod_addressing/README.md mod_adhoc_account_management/README.markdown mod_adhoc_account_management/README.md mod_adhoc_blacklist/README.markdown mod_adhoc_blacklist/README.md mod_adhoc_dataforms_demo/README.markdown mod_adhoc_dataforms_demo/README.md mod_adhoc_groups/README.markdown mod_adhoc_groups/README.md mod_adhoc_oauth2_client/README.markdown mod_adhoc_oauth2_client/README.md mod_admin_blocklist/README.markdown mod_admin_blocklist/README.md mod_admin_message/README.markdown mod_admin_message/README.md mod_admin_notify/README.markdown mod_admin_notify/README.md mod_admin_probe/README.markdown mod_admin_probe/README.md mod_admin_web/README.markdown mod_admin_web/README.md mod_alias/README.markdown mod_alias/README.md mod_anti_spam/README.markdown mod_atom/README.markdown mod_atom/README.md mod_auth_any/README.markdown mod_auth_any/README.md mod_auth_ccert/README.markdown mod_auth_ccert/README.md mod_auth_custom_http/README.markdown mod_auth_custom_http/README.md mod_auth_dovecot/README.markdown mod_auth_dovecot/README.md mod_auth_external_insecure/README.markdown mod_auth_external_insecure/README.md mod_auth_ha1/README.markdown mod_auth_ha1/README.md mod_auth_http/README.markdown mod_auth_http/README.md mod_auth_http_async/README.markdown mod_auth_http_async/README.md mod_auth_http_cookie/README.markdown mod_auth_http_cookie/README.md mod_auth_imap/README.markdown mod_auth_imap/README.md mod_auth_internal_yubikey/README.markdown mod_auth_internal_yubikey/README.md mod_auth_joomla/README.markdown mod_auth_joomla/README.md mod_auth_ldap/README.markdown mod_auth_ldap/README.md mod_auth_ldap2/README.markdown mod_auth_ldap2/README.md mod_auth_oauthbearer/README.markdown mod_auth_oauthbearer/README.md mod_auth_pam/README.markdown mod_auth_pam/README.md mod_auth_phpbb3/README.markdown mod_auth_phpbb3/README.md mod_auth_sql/README.markdown mod_auth_sql/README.md mod_auth_token/README.markdown mod_auth_token/README.md mod_auth_wordpress/README.markdown mod_auth_wordpress/README.md mod_auto_accept_subscriptions/README.markdown mod_auto_accept_subscriptions/README.md mod_auto_activate_hosts/README.markdown mod_auto_activate_hosts/README.md mod_auto_answer_disco_info/README.markdown mod_auto_answer_disco_info/README.md mod_auto_moved/README.markdown mod_auto_moved/README.md mod_aws_profile/README.markdown mod_aws_profile/README.md mod_benchmark_storage/README.markdown mod_benchmark_storage/README.md mod_bidi/README.markdown mod_bidi/README.md mod_bidi/mod_bidi.lua mod_bind2/README.md mod_bind2/mod_bind2.lua mod_block_outgoing/README.markdown mod_block_outgoing/README.md mod_block_registrations/README.markdown mod_block_registrations/README.md mod_blocking/README.markdown mod_blocking/README.md mod_bob/README.markdown mod_bob/README.md mod_bookmarks/README.markdown mod_bookmarks/README.md mod_bookmarks/mod_bookmarks.lua mod_bookmarks2/README.markdown mod_bookmarks2/README.md mod_bookmarks2/mod_bookmarks2.lua mod_bookmarks2/tests/bookmarks2.scs mod_bookmarks2/tests/conversion.scs mod_broadcast/README.markdown mod_broadcast/README.md mod_c2s_conn_throttle/README.markdown mod_c2s_conn_throttle/README.md mod_c2s_limit_sessions/README.markdown mod_c2s_limit_sessions/README.md mod_cache_c2s_caps/README.markdown mod_cache_c2s_caps/README.md mod_captcha_registration/README.markdown mod_captcha_registration/README.md mod_carbons/README.markdown mod_carbons/README.md mod_checkcerts/README.markdown mod_checkcerts/README.md mod_client_certs/README.markdown mod_client_certs/README.md mod_client_proxy/README.markdown mod_client_proxy/README.md mod_cloud_notify/README.markdown mod_cloud_notify/README.md mod_cloud_notify/business_rules.markdown mod_cloud_notify/business_rules.md mod_cloud_notify_extensions/README.markdown mod_cloud_notify_extensions/README.md mod_compat_dialback/README.markdown mod_compat_dialback/README.md mod_compat_muc_admin/README.markdown mod_compat_muc_admin/README.md mod_compat_roles/README.markdown mod_compat_roles/README.md mod_compliance_2023/README.md mod_compliance_2023/mod_compliance_2023.lua mod_compliance_latest/README.md mod_compliance_latest/mod_compliance_latest.lua mod_component_client/README.markdown mod_component_client/README.md mod_component_http/README.markdown mod_component_http/README.md mod_component_roundrobin/README.markdown mod_component_roundrobin/README.md mod_compression_unsafe/README.markdown mod_compression_unsafe/README.md mod_conformance_restricted/README.markdown mod_conformance_restricted/README.md mod_conversejs/README.markdown mod_conversejs/README.md mod_couchdb/README.markdown mod_couchdb/README.md mod_csi/README.markdown mod_csi/README.md mod_csi_battery_saver/README.markdown mod_csi_battery_saver/README.md mod_csi_compat/README.markdown mod_csi_compat/README.md mod_csi_grace_period/README.markdown mod_csi_grace_period/README.md mod_csi_muc_priorities/README.markdown mod_csi_muc_priorities/README.md mod_csi_simple_compat/README.markdown mod_csi_simple_compat/README.md mod_data_access/README.markdown mod_data_access/README.md mod_debug_omemo/README.markdown mod_debug_omemo/README.md mod_debug_traceback/README.markdown mod_debug_traceback/README.md mod_default_bookmarks/README.markdown mod_default_bookmarks/README.md mod_default_vcard/README.markdown mod_default_vcard/README.md mod_delegation/README.markdown mod_delegation/README.md mod_devices/README.markdown mod_devices/README.md mod_disable_tls/README.markdown mod_disable_tls/README.md mod_discodot/README.markdown mod_discodot/README.md mod_discoitems/README.markdown mod_discoitems/README.md mod_dnsbl/README.markdown mod_dnsbl/mod_dnsbl.lua mod_dnsupdate/README.markdown mod_dnsupdate/README.md mod_dwd/README.markdown mod_dwd/README.md mod_e2e_policy/README.markdown mod_e2e_policy/README.md mod_easy_invite/README.markdown mod_easy_invite/README.md mod_email_pass/README.markdown mod_email_pass/README.md mod_extdisco/README.markdown mod_extdisco/README.md mod_external_services/README.markdown mod_external_services/README.md mod_file_management/README.markdown mod_file_management/README.md mod_filter_chatstates/README.markdown mod_filter_chatstates/README.md mod_firewall/README.markdown mod_firewall/README.md mod_flash_policy/README.markdown mod_flash_policy/README.md mod_graceful_shutdown/README.markdown mod_graceful_shutdown/README.md mod_group_bookmarks/README.markdown mod_group_bookmarks/README.md mod_groups_internal/README.md mod_host_guard/README.markdown mod_host_guard/README.md mod_host_status_check/README.markdown mod_host_status_check/README.md mod_host_status_heartbeat/README.markdown mod_host_status_heartbeat/README.md mod_http_admin_api/README.md mod_http_altconnect/README.markdown mod_http_auth_check/README.markdown mod_http_authentication/README.markdown mod_http_avatar/README.markdown mod_http_avatar/README.md mod_http_dir_listing/README.markdown mod_http_dir_listing/README.md mod_http_dir_listing2/README.markdown mod_http_dir_listing2/README.md mod_http_favicon/README.markdown mod_http_favicon/README.md mod_http_host_status_check/README.markdown mod_http_host_status_check/README.md mod_http_index/README.markdown mod_http_index/README.md mod_http_index/html/http_index.html mod_http_index/http_index.html mod_http_libjs/README.markdown mod_http_libjs/README.md mod_http_logging/README.markdown mod_http_muc_log/README.markdown mod_http_oauth2/README.markdown mod_http_pep_avatar/README.markdown mod_http_pep_avatar/README.md mod_http_prebind/README.markdown mod_http_prebind/README.md mod_http_rest/README.markdown mod_http_rest/README.md mod_http_roster_admin/README.markdown mod_http_roster_admin/README.md mod_http_stats_stream/README.markdown mod_http_stats_stream/README.md mod_http_upload/README.markdown mod_http_upload/mod_http_upload.lua mod_http_upload_external/README.markdown mod_http_upload_external/README.md mod_idlecompat/README.markdown mod_ignore_host_chatstates/README.markdown mod_incidents_handling/README.markdown mod_inject_ecaps2/README.markdown mod_inject_ecaps2/README.md mod_invite/README.markdown mod_invite/README.md mod_invites/README.markdown mod_invites/README.md mod_invites_adhoc/README.markdown mod_invites_adhoc/README.md mod_invites_register/README.markdown mod_invites_register/README.md mod_ipcheck/README.markdown mod_isolate_host/README.markdown mod_jid_prep/README.markdown mod_json_streams/README.markdown mod_json_streams/mod_json_streams.lua mod_json_streams/strophe.jsonstreams.js mod_jsxc/README.markdown mod_jsxc/README.md mod_lastlog/README.markdown mod_lastlog/README.md mod_lastlog2/README.markdown mod_lastlog2/README.md mod_latex/README.markdown mod_latex/README.md mod_limit_auth/README.markdown mod_limits/README.markdown mod_limits/README.md mod_limits_exception/README.markdown mod_list_active/README.markdown mod_list_inactive/README.markdown mod_listusers/README.markdown mod_log_auth/README.markdown mod_log_events_by_cpu_usage/README.markdown mod_log_events_by_cpu_usage/README.md mod_log_events_by_memory/README.markdown mod_log_events_by_memory/README.md mod_log_http/README.markdown mod_log_http/README.md mod_log_json/README.markdown mod_log_json/README.md mod_log_mark/README.markdown mod_log_messages_sql/README.markdown mod_log_messages_sql/README.md mod_log_rate/README.markdown mod_log_ringbuffer/README.markdown mod_log_slow_events/README.markdown mod_mam/README.markdown mod_mam_adhoc/README.markdown mod_mam_archive/README.markdown mod_mam_archive/README.md mod_mam_muc/README.markdown mod_manifesto/README.markdown mod_manifesto/README.md mod_map/README.markdown mod_map/README.md mod_measure_client_features/README.markdown mod_measure_client_features/README.md mod_measure_client_identities/README.markdown mod_measure_client_identities/README.md mod_measure_client_presence/README.markdown mod_measure_cpu/README.markdown mod_measure_malloc/README.markdown mod_measure_malloc/README.md mod_measure_memory/README.markdown mod_measure_message_e2ee/README.markdown mod_measure_message_e2ee/README.md mod_measure_message_length/README.markdown mod_measure_muc/README.markdown mod_measure_process/README.markdown mod_measure_registration/README.markdown mod_measure_stanza_counts/README.markdown mod_measure_storage/README.markdown mod_message_logging/README.markdown mod_migrate/README.markdown mod_migrate_http_upload/README.markdown mod_migrate_http_upload/README.md mod_minimix/README.markdown mod_motd_sequential/README.markdown mod_motd_sequential/README.md mod_muc_adhoc_bots/README.markdown mod_muc_archive/README.markdown mod_muc_auto_member/README.markdown mod_muc_badge/README.markdown mod_muc_ban_ip/README.markdown mod_muc_ban_ip/README.md mod_muc_batched_probe/README.markdown mod_muc_block_pm/README.markdown mod_muc_block_pm/README.md mod_muc_bot/README.markdown mod_muc_cloud_notify/README.markdown mod_muc_config_restrict/README.markdown mod_muc_defaults/README.markdown mod_muc_eventsource/README.markdown mod_muc_gc10/README.markdown mod_muc_hats_adhoc/README.markdown mod_muc_hats_api/README.markdown mod_muc_hide_media/README.markdown mod_muc_hide_media/README.md mod_muc_http_defaults/README.markdown mod_muc_inject_mentions/README.markdown mod_muc_intercom/README.markdown mod_muc_lang/README.markdown mod_muc_limits/README.markdown mod_muc_local_only/README.markdown mod_muc_log/README.markdown mod_muc_log/mod_muc_log.lua mod_muc_log_http/README.markdown mod_muc_log_http/muc_log_http/mod_muc_log_http.lua mod_muc_log_http/muc_log_http/themes/prosody/components_bit.html mod_muc_log_http/muc_log_http/themes/prosody/components_body.html mod_muc_log_http/muc_log_http/themes/prosody/day_bann.html mod_muc_log_http/muc_log_http/themes/prosody/day_body.html mod_muc_log_http/muc_log_http/themes/prosody/day_bodynp.html mod_muc_log_http/muc_log_http/themes/prosody/day_dayLink.html mod_muc_log_http/muc_log_http/themes/prosody/day_kick.html mod_muc_log_http/muc_log_http/themes/prosody/day_message.html mod_muc_log_http/muc_log_http/themes/prosody/day_messageMe.html mod_muc_log_http/muc_log_http/themes/prosody/day_presence_join.html mod_muc_log_http/muc_log_http/themes/prosody/day_presence_leave.html mod_muc_log_http/muc_log_http/themes/prosody/day_presence_statusChange.html mod_muc_log_http/muc_log_http/themes/prosody/day_presence_statusText.html mod_muc_log_http/muc_log_http/themes/prosody/day_reason.html mod_muc_log_http/muc_log_http/themes/prosody/day_time.html mod_muc_log_http/muc_log_http/themes/prosody/day_title.html mod_muc_log_http/muc_log_http/themes/prosody/day_titleChange.html mod_muc_log_http/muc_log_http/themes/prosody/days_bit.html mod_muc_log_http/muc_log_http/themes/prosody/days_body.html mod_muc_log_http/muc_log_http/themes/prosody/days_rooms_bit.html mod_muc_log_http/muc_log_http/themes/prosody/doc.html mod_muc_log_http/muc_log_http/themes/prosody/month_day.html mod_muc_log_http/muc_log_http/themes/prosody/month_emptyDay.html mod_muc_log_http/muc_log_http/themes/prosody/month_footer.html mod_muc_log_http/muc_log_http/themes/prosody/month_header.html mod_muc_log_http/muc_log_http/themes/prosody/month_weekDay.html mod_muc_log_http/muc_log_http/themes/prosody/rooms_bit.html mod_muc_log_http/muc_log_http/themes/prosody/rooms_body.html mod_muc_log_http/muc_log_http/themes/prosody/year_title.html mod_muc_mam_hints/README.markdown mod_muc_mam_markers/README.markdown mod_muc_markers/README.markdown mod_muc_media_metadata/README.markdown mod_muc_media_metadata/README.md mod_muc_mention_notifications/README.markdown mod_muc_moderation/README.markdown mod_muc_notifications/README.markdown mod_muc_occupant_id/README.markdown mod_muc_occupant_id/mod_muc_occupant_id.lua mod_muc_ping/README.markdown mod_muc_rai/README.markdown mod_muc_rai/README.md mod_muc_require_tos/README.markdown mod_muc_reserve_nick_pattern/README.markdown mod_muc_restrict_media/README.markdown mod_muc_restrict_nick/README.markdown mod_muc_restrict_pm/README.markdown mod_muc_restrict_rooms/README.markdown mod_muc_webchat_url/README.markdown mod_munin/README.markdown mod_net_dovecotauth/README.markdown mod_net_dovecotauth/README.md mod_net_proxy/README.markdown mod_nodeinfo2/README.markdown mod_nooffline_noerror/README.markdown mod_offline_email/README.markdown mod_offline_hints/README.markdown mod_ogp/README.markdown mod_omemo_all_access/README.markdown mod_onhold/README.markdown mod_onions/README.markdown mod_openid/README.markdown mod_openid/mod_openid.lua mod_password_reset/README.markdown mod_pastebin/README.markdown mod_pep_vcard_avatar/README.markdown mod_pep_vcard_png_avatar/README.markdown mod_persisthosts/README.markdown mod_poke_strangers/README.markdown mod_post_msg/README.markdown mod_presence_cache/README.markdown mod_presence_dedup/README.markdown mod_privacy_lists/README.markdown mod_private_adhoc/README.markdown mod_privilege/README.markdown mod_proctitle/README.markdown mod_profile/README.markdown mod_prometheus/README.markdown mod_proxy65_whitelist/README.markdown mod_pubsub_eventsource/README.markdown mod_pubsub_feeds/README.markdown mod_pubsub_github/README.markdown mod_pubsub_hub/README.markdown mod_pubsub_mqtt/README.markdown mod_pubsub_post/README.markdown mod_pubsub_serverinfo/README.markdown mod_pubsub_stats/README.markdown mod_pubsub_subscription/README.markdown mod_pubsub_text_interface/README.markdown mod_pubsub_twitter/README.markdown mod_push2/README.markdown mod_push2/push2.markdown mod_query_client_ver/README.markdown mod_rawdebug/README.markdown mod_readonly/README.markdown mod_register_apps/README.markdown mod_register_dnsbl/README.markdown mod_register_dnsbl_warn/README.markdown mod_register_json/README.markdown mod_register_oob_url/README.markdown mod_register_redirect/README.markdown mod_register_web/README.markdown mod_reload_components/README.markdown mod_reload_modules/README.markdown mod_remote_roster/README.markdown mod_report_forward/README.markdown mod_require_otr/README.markdown mod_rest/README.markdown mod_restrict_xmpp/README.markdown mod_roster_allinall/README.markdown mod_roster_command/README.markdown mod_s2s_auth_compat/README.markdown mod_s2s_auth_dane/README.markdown mod_s2s_auth_fingerprint/README.markdown mod_s2s_auth_monkeysphere/README.markdown mod_s2s_auth_posh/README.markdown mod_s2s_blacklist/README.markdown mod_s2s_idle_timeout/README.markdown mod_s2s_keepalive/README.markdown mod_s2s_keysize_policy/README.markdown mod_s2s_log_certs/README.markdown mod_s2s_never_encrypt_blacklist/README.markdown mod_s2s_reload_newcomponent/README.markdown mod_s2s_status/README.markdown mod_s2s_whitelist/README.markdown mod_s2soutinjection/README.markdown mod_sasl_oauthbearer/README.markdown mod_sasl_ssdp/README.markdown mod_saslname/README.markdown mod_seclabels/README.markdown mod_secure_interfaces/README.markdown mod_server_status/README.markdown mod_service_outage_status/README.markdown mod_sift/README.markdown mod_slack_webhooks/README.markdown mod_smacks/README.markdown mod_smacks/mod_smacks.lua mod_smacks_noerror/README.markdown mod_smacks_offline/README.markdown mod_smacks_offline/mod_smacks_offline.lua mod_sms_clickatell/README.markdown mod_sms_free/README.markdown mod_spam_reporting/README.markdown mod_srvinjection/README.markdown mod_stanza_counter/README.markdown mod_stanzadebug/README.markdown mod_statistics_statsman/README.markdown mod_stats39/README.markdown mod_storage_appendmap/README.markdown mod_storage_ejabberdsql_readonly/README.markdown mod_storage_gdbm/README.markdown mod_storage_ldap/README.markdown mod_storage_lmdb/README.markdown mod_storage_memory/README.markdown mod_storage_memory/mod_storage_memory.lua mod_storage_mongodb/README.markdown mod_storage_muc_log/README.markdown mod_storage_muconference_readonly/README.markdown mod_storage_multi/README.markdown mod_storage_xmlarchive/README.markdown mod_strict_https/README.markdown mod_support_contact/README.markdown mod_support_room/README.markdown mod_swedishchef/README.markdown mod_tcpproxy/README.markdown mod_telnet_tlsinfo/README.markdown mod_test_data/README.markdown mod_throttle_presence/README.markdown mod_throttle_unsolicited/README.markdown mod_tls_policy/README.markdown mod_tlsfail/README.markdown mod_traceback/README.markdown mod_track_muc_joins/README.markdown mod_turn_external/README.markdown mod_turn_external/mod_turn_external.lua mod_turncredentials/README.markdown mod_twitter/README.markdown mod_uptime_presence/README.markdown mod_vcard_muc/README.markdown mod_vjud/README.markdown mod_warn_legacy_tls/README.markdown mod_watch_spam_reports/README.markdown mod_watchuntrusted/README.markdown mod_welcome_page/README.markdown mod_xhtmlim/README.markdown |
diffstat | 550 files changed, 7984 insertions(+), 20932 deletions(-) [+] |
line wrap: on
line diff
--- a/misc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -Miscellaneous modules -===================== - -Things that aren't really Prosody plugins go here, e.g. RunScripts.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,4 @@ +Miscellaneous modules +===================== + +Things that aren't really Prosody plugins go here, e.g. RunScripts.
--- a/mod_addressing/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0033: Extended Stanza Addressing' -... - -Introduction -============ - -This module is a partial implementation of [XEP-0033: Extended Stanza -Addressing](http://xmpp.org/extensions/xep-0033.html). - -TODO -==== - -Query external servers for support.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_addressing/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,16 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'XEP-0033: Extended Stanza Addressing' +... + +Introduction +============ + +This module is a partial implementation of [XEP-0033: Extended Stanza +Addressing](http://xmpp.org/extensions/xep-0033.html). + +TODO +==== + +Query external servers for support.
--- a/mod_adhoc_account_management/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Personal account management command -... - -Introduction -============ - -This module adds an ad-hoc command that lets an user change their -password. This is useful for clients that don't have support for -[XEP-0077](http://xmpp.org/extensions/xep-0077.html) style password -changing. In the future, it may provide other account management -commands. - -Configuration -============= - - modules_enabled = { - -- other modules -- - "adhoc_account_management", - - } - - close_sessions_on_password_change = true - require_current_password = true - require_confirm_password = true - - Option Default Description - --------------------------------------- --------- ---------------------------------------------------------------- - close\_sessions\_on\_password\_change true Changing password invalidates other sessions the user may have - require\_current\_password true Add a field for the current password - require\_confirm\_password true Add a field for confirming the current password - -Todo -==== - -Suggestions welcome,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_adhoc_account_management/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,38 @@ +--- +labels: +- 'Stage-Alpha' +summary: Personal account management command +... + +Introduction +============ + +This module adds an ad-hoc command that lets an user change their +password. This is useful for clients that don't have support for +[XEP-0077](http://xmpp.org/extensions/xep-0077.html) style password +changing. In the future, it may provide other account management +commands. + +Configuration +============= + + modules_enabled = { + -- other modules -- + "adhoc_account_management", + + } + + close_sessions_on_password_change = true + require_current_password = true + require_confirm_password = true + + Option Default Description + --------------------------------------- --------- ---------------------------------------------------------------- + close\_sessions\_on\_password\_change true Changing password invalidates other sessions the user may have + require\_current\_password true Add a field for the current password + require\_confirm\_password true Add a field for confirming the current password + +Todo +==== + +Suggestions welcome,
--- a/mod_adhoc_blacklist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ ---- -summary: 'Block remote servers via ad-hoc command' -... - -Introduction -============ - -This module provides the *Edit Blacklist* ad-hoc command described in -[XEP-0133](http://xmpp.org/extensions/xep-0133.html#edit-blacklist) and -also performs the actual blocking of incoming and outgoing -server-to-server connections. - -Using -===== - -In your client, simply select the Edit Blacklist command from the list -of adhoc commands. E.g. in Pidgin, this is under *Accounts -\> (your -account)* in the menu. -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_adhoc_blacklist/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,19 @@ +--- +summary: 'Block remote servers via ad-hoc command' +... + +Introduction +============ + +This module provides the *Edit Blacklist* ad-hoc command described in +[XEP-0133](http://xmpp.org/extensions/xep-0133.html#edit-blacklist) and +also performs the actual blocking of incoming and outgoing +server-to-server connections. + +Using +===== + +In your client, simply select the Edit Blacklist command from the list +of adhoc commands. E.g. in Pidgin, this is under *Accounts -\> (your +account)* in the menu. +
--- a/mod_adhoc_dataforms_demo/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -summary: 'Module for testing ad-hoc commands and dataforms rendering' ---- - -# Introduction - -This module provides [Ad-Hoc commands][xep0050] for testing [data -form][xep0004] that includes all kinds of fields. It's meant to help -debug both Prosodys -[`util.dataforms`][doc:developers:util:dataforms] library and -clients, eg seeing how various field types are rendered. - -# Configuration - -Simply add it to [`modules_enabled`][doc:modules_enabled] like any -other module. - -``` {.lua} -modules_enabled = { - -- All your other modules etc - "adhoc_dataforms_demo"; -} -``` - -# Usage - -In your Ad-Hoc capable client, first look for "Execute command". You -should see a form with various kinds of fields. - -Dataforms Demo -: A simple command that provides a dataform with every possible field - type, suitable for testing rending of dataforms. - -Multi-step command demo -: A command that has multiple steps, suitable for testing back and - forwards navigation.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_adhoc_dataforms_demo/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +summary: 'Module for testing ad-hoc commands and dataforms rendering' +--- + +# Introduction + +This module provides [Ad-Hoc commands][xep0050] for testing [data +form][xep0004] that includes all kinds of fields. It's meant to help +debug both Prosodys +[`util.dataforms`][doc:developers:util:dataforms] library and +clients, eg seeing how various field types are rendered. + +# Configuration + +Simply add it to [`modules_enabled`][doc:modules_enabled] like any +other module. + +``` {.lua} +modules_enabled = { + -- All your other modules etc + "adhoc_dataforms_demo"; +} +``` + +# Usage + +In your Ad-Hoc capable client, first look for "Execute command". You +should see a form with various kinds of fields. + +Dataforms Demo +: A simple command that provides a dataform with every possible field + type, suitable for testing rending of dataforms. + +Multi-step command demo +: A command that has multiple steps, suitable for testing back and + forwards navigation.
--- a/mod_adhoc_groups/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ ---- -summary: Lets users create and join roster groups ---- - -Introduction -============ - -This module lets you join groups using an ad-hoc command. When a user -joins a group, everyone in the group is added to their roster, and they -are added to the rosters of existing members. - -TODO -==== - -- Leaving groups -- Add a roster group/tag when adding roster items (tricky with current - rostermanager API)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_adhoc_groups/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,17 @@ +--- +summary: Lets users create and join roster groups +--- + +Introduction +============ + +This module lets you join groups using an ad-hoc command. When a user +joins a group, everyone in the group is added to their roster, and they +are added to the rosters of existing members. + +TODO +==== + +- Leaving groups +- Add a roster group/tag when adding roster items (tricky with current + rostermanager API)
--- a/mod_adhoc_oauth2_client/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: 'Create OAuth2 clients via ad-hoc command' -rockspec: - dependencies: - - mod_http_oauth2 -... - -Introduction -============ - -[Ad-Hoc command][XEP-0050] interface to -[dynamic OAuth2 registration](https://oauth.net/2/dynamic-client-registration/) -provided by [mod_http_oauth2]. - -Compatibility -============= - -Same as [mod_http_oauth2] -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_adhoc_oauth2_client/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,21 @@ +--- +labels: +- Stage-Alpha +summary: 'Create OAuth2 clients via ad-hoc command' +rockspec: + dependencies: + - mod_http_oauth2 +... + +Introduction +============ + +[Ad-Hoc command][XEP-0050] interface to +[dynamic OAuth2 registration](https://oauth.net/2/dynamic-client-registration/) +provided by [mod_http_oauth2]. + +Compatibility +============= + +Same as [mod_http_oauth2] +
--- a/mod_admin_blocklist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -summary: Block s2s connections based on admin blocklists -... - -This module uses the blocklists set by admins for blocking s2s -connections. - -So if an admin blocks a bare domain using [Blocking Command][xep191] -via [mod\_blocklist][doc:modules:mod_blocklist] then no s2s connections -will be allowed to or from that domain. - -# Configuring - -## Prosody 0.12 - -Starting with Prosody 0.12, the role or roles that determine whether a -particular users blocklist is used can be configured: - -```lua --- This is the default: -admin_blocklist_roles = { "prosody:operator", "prosody:admin" } -``` - -## Prosody 0.11 - -In Prosody 0.11 the [`admins`][doc:admins] setting is used.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_blocklist/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,31 @@ +--- +summary: Block s2s connections based on admin blocklist +labels: +- 'Stage-Beta' +... + +This module uses the blocklists set by admins for blocking s2s +connections. + +So if an admin blocks a bare domain using [Blocking Command][xep191] +via [mod\_blocklist][doc:modules:mod_blocklist] then no s2s connections +will be allowed to or from that domain. + +# Configuring + +The role or roles that determine whether a +particular users blocklist is used can be configured: + +```lua +-- This is the default: +admin_blocklist_roles = { "prosody:operator", "prosody:admin" } +``` + +# Compatibility + + Prosody-Version Status + --------------- ------ + trunk* Works + 0.12 Works + +*as of 2024-12-21
--- a/mod_admin_message/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'IM-based administration console' -... - -Introduction -============ - -This module provides a console over XMPP. All the commands of the -mod\_admin\_telnet module are available from an XMPP client. - -Only the Prosody admins (see the *admins* list in the Prosody -configuration file) can use this console. - -Installation -============ - -Copy the mod\_admin\_message directory into a directory Prosody will -check for plugins (cf. [Installing -modules](http://prosody.im/doc/installing_modules)) and set up a -component: - - Component "console@example.com" "admin_message" - -"console@example.com" is the identifier of the XMPP console. - -Compatibility -============= - - --------- --------------- - trunk Doesn't work (uses is_admin) - 0.9 Works - \<= 0.8 Not supported - --------- ---------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_message/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,35 @@ +--- +labels: +- 'Stage-Beta' +summary: 'IM-based administration console' +... + +Introduction +============ + +This module provides a console over XMPP. All the commands of the +mod\_admin\_telnet module are available from an XMPP client. + +Only the Prosody admins (see the *admins* list in the Prosody +configuration file) can use this console. + +Installation +============ + +Copy the mod\_admin\_message directory into a directory Prosody will +check for plugins (cf. [Installing +modules](http://prosody.im/doc/installing_modules)) and set up a +component: + + Component "console@example.com" "admin_message" + +"console@example.com" is the identifier of the XMPP console. + +Compatibility +============= + + --------- --------------- + trunk Doesn't work (uses is_admin) + 0.9 Works + \<= 0.8 Not supported + --------- ---------------
--- a/mod_admin_notify/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: API to notify server admins ---- - -# Introduction - -This module provides an API for other module developers to send -notification messages to host admins. - -# Configuration - -None required. - -# Developers - -Example: - -``` -local notify_admins = module:depends("admin_notify").notify; - -notify("This is an important message for you, admins") -``` - -# Compatibility - -Prosody trunk or later. Incompatible with 0.11 or lower.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_notify/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,28 @@ +--- +labels: +- 'Stage-Alpha' +summary: API to notify server admins +--- + +# Introduction + +This module provides an API for other module developers to send +notification messages to host admins. + +# Configuration + +None required. + +# Developers + +Example: + +``` +local notify_admins = module:depends("admin_notify").notify; + +notify("This is an important message for you, admins") +``` + +# Compatibility + +Prosody trunk or later. Incompatible with 0.11 or lower.
--- a/mod_admin_probe/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ---- -summary: Allow server administrators to probe any user -... - -This module lets server administrators send `<presence type="probe"/>` -to any local user and receive their presence in response, bypassing -roster checks. - -Compatibility -============= - - ------- -------------- - trunk Doesn't work (uses is_admin) - 0.12 Works? - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_probe/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,15 @@ +--- +summary: Allow server administrators to probe any user +... + +This module lets server administrators send `<presence type="probe"/>` +to any local user and receive their presence in response, bypassing +roster checks. + +Compatibility +============= + + ------- -------------- + trunk Doesn't work (uses is_admin) + 0.12 Works? + ------- --------------
--- a/mod_admin_web/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Web administration interface -rockspec: - build: - platforms: - unix: - type: command - build_command: cd admin_web; sh get_deps.sh - install_command: | - cd admin_web; - cp mod_admin_web.lua $(LUADIR); - cp -r www_files $(PREFIX); -... - -Introduction -============ - -This module provides a basic web administration interface. It currently -gives you access to Ad-Hoc commands on any virtual host or component -that you are set as an administrator for in the Prosody config file. It -also provides a live list of all S2S and C2S connections. - -Installation -============ - -1. Copy the admin\_web directory into a directory Prosody will check - for plugins. (cf. [Installing - modules](http://prosody.im/doc/installing_modules)) -2. Execute the contained `get_deps.sh` script from within the admin\_web - directory. (Requires wget, tar, and a basic shell) - -Configuration Details -===================== - -"admin\_web" needs to be added to the modules\_enabled table of the host -you want to load this module on. - -By default the interface will then be reachable under -`http://example.com:5280/admin`, or `https://example.com:5281/admin`. - -The module will automatically enable two other modules if they aren't -already: mod\_bosh (used to connect to the server from the web), and -mod\_admin\_adhoc (which provides admin commands over XMPP). - - VirtualHost "example.com" - modules_enabled = { - ..... - "admin_web"; - ..... - } - -Compatibility -============= - - --------- --------------- - trunk Works - 0.9 Works - \<= 0.8 Not supported - --------- ---------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_admin_web/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,61 @@ +--- +labels: +- 'Stage-Beta' +summary: Web administration interface +rockspec: + build: + platforms: + unix: + type: command + build_command: cd admin_web; sh get_deps.sh + install_command: | + cd admin_web; + cp mod_admin_web.lua $(LUADIR); + cp -r www_files $(PREFIX); +... + +Introduction +============ + +This module provides a basic web administration interface. It currently +gives you access to Ad-Hoc commands on any virtual host or component +that you are set as an administrator for in the Prosody config file. It +also provides a live list of all S2S and C2S connections. + +Installation +============ + +1. Copy the admin\_web directory into a directory Prosody will check + for plugins. (cf. [Installing + modules](http://prosody.im/doc/installing_modules)) +2. Execute the contained `get_deps.sh` script from within the admin\_web + directory. (Requires wget, tar, and a basic shell) + +Configuration Details +===================== + +"admin\_web" needs to be added to the modules\_enabled table of the host +you want to load this module on. + +By default the interface will then be reachable under +`http://example.com:5280/admin`, or `https://example.com:5281/admin`. + +The module will automatically enable two other modules if they aren't +already: mod\_bosh (used to connect to the server from the web), and +mod\_admin\_adhoc (which provides admin commands over XMPP). + + VirtualHost "example.com" + modules_enabled = { + ..... + "admin_web"; + ..... + } + +Compatibility +============= + + --------- --------------- + trunk Works + 0.9 Works + \<= 0.8 Not supported + --------- ---------------
--- a/mod_alias/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ ---- -summary: Point alias accounts or domains to correct XMPP user -... - -Introduction -============ - -This module allows you to set up aliases that alert people who try to -contact them or add them to their roster what your actual JID is. This -is useful for changing JIDs, or just in the case where you own both -example.com and example.net, and want people who contact you@example.com -to be alerted to contact you at you@example.net instead. - -This type of aliasing is well supported in the email world, but very hard -to handle with XMPP, this module sidesteps all the hard problems by just -sending the user a helpful message, requiring humans to decide what they -actually want to do. - -This doesn't require any special support on other clients or servers, -just the ability to receive messages. - -Configuration -============= - -Add the module to the `modules_enabled` list. - - modules_enabled = { - ... - "alias"; - } - -Then set up your list of aliases, aliases can be full or bare JIDs, -or hosts: - - aliases = { - ["old@example.net"] = "new@example.net"; - ["you@example.com"] = "you@example.net"; - ["conference.example.com"] = "conference.example.net"; - } - -You can also set up a custom response, by default it is: - - alias_response = "User $alias can be contacted at $target"; - -A script named mod_alias_postfixadmin.sh is included in this directory to -generate the aliases array directly from a postfixadmin MySQL database. -Instructions for use are included in the script. - -Compatibility -============= - - ------- -------------- - trunk Works - 0.10 Works - 0.9 Unknown - 0.8 Unknown - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_alias/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,58 @@ +--- +summary: "Point alias accounts or domains to correct XMPP user" +labels: +- 'Stage-Alpha' +... + +Introduction +============ + +This module allows you to set up aliases that alert people who try to +contact them or add them to their roster what your actual JID is. This +is useful for changing JIDs, or just in the case where you own both +example.com and example.net, and want people who contact you@example.com +to be alerted to contact you at you@example.net instead. + +This type of aliasing is well supported in the email world, but very hard +to handle with XMPP, this module sidesteps all the hard problems by just +sending the user a helpful message, requiring humans to decide what they +actually want to do. + +This doesn't require any special support on other clients or servers, +just the ability to receive messages. + +Configuration +============= + +Add the module to the `modules_enabled` list. + + modules_enabled = { + ... + "alias"; + } + +Then set up your list of aliases, aliases can be full or bare JIDs, +or hosts: + + aliases = { + ["old@example.net"] = "new@example.net"; + ["you@example.com"] = "you@example.net"; + ["conference.example.com"] = "conference.example.net"; + } + +You can also set up a custom response, by default it is: + + alias_response = "User $alias can be contacted at $target"; + +A script named mod_alias_postfixadmin.sh is included in this directory to +generate the aliases array directly from a postfixadmin MySQL database. +Instructions for use are included in the script. + +Compatibility +============= + + ------------–---------- -------------- + trunk as of 2024-10-22 Works + 0.12 Works + ----------------------- -------------- +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_anti_spam/README.markdown Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,98 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Spam filtering' +rockspec: + build: + modules: + mod_anti_spam.rtbl: rtbl.lib.lua + mod_anti_spam.trie: trie.lib.lua +depends: + - mod_pubsub_subscription +--- + +This module aims to provide an all-in-one spam filter for any kind of Prosody +deployment. + +## What is spam? + +You're lucky if you have to ask! But it's worth explaining, so we can be clear +about what the module does (and does not). + +Similar to every other popular communication network, there are people who try +to exploit XMPP for sending unsolicited messages - usually advertisements +for products and services. These people have gathered large lists of user +addresses, e.g. by searching and "scraping" websites for contact info. + +If your address has not been discovered by the spammers, you won't receive any +spam. Prosody does not reveal user addresses (except, obviously, to people who +you communicate with). So to avoid it being picked up by spammers, be careful +about posting it unprotected on websites, etc. + +However, if get unlucky and your address is discovered by spammers, you may +receive dozens of spam messages per day. mod_anti_spam is designed to filter +these annoying messages to prevent them from reaching you. + +## How does it work? + +mod_anti_spam uses a variety of techniques to identify likely spam. Just as +the behaviour of spammers changes, The exact methods used to detect spam may +evolve over time in future updates. + +If the sender is in the recipient's contact list already, no filtering will be +performed. + +Otherwise, if the sender is a "stranger" to the recipient, the module will +perform some checks, and decide whether to let the message or contact request +through. + +### Shared block lists + +mod_anti_spam can subscribe to Real-Time Block Lists (RTBLs) such as those +published by [xmppbl.org](https://xmppbl.org). This is a highly effective +measure to reduce spam from the network. + +To enable this feature, you need to specify one or more compatible spam +services in the config file: + +```lua +anti_spam_services = { "xmppbl.org" } +``` + +### Content filters + +mod_anti_spam also supports optionally filtering messages with specific +content or matching certain patterns. + +A list of strings to block can be specified in the config file like so: + +```lua +anti_spam_block_strings = { + -- Block messages containing the text "exploit" + "exploit"; +} +``` + +Alternatively, you can specify a list of [Lua patterns](https://lua.org/manual/5.4/manual.html#6.4.1). +These are similar to regular expressions you may be familiar with from tools +like grep, but differ in a number of ways. Lua patterns are faster, but have +fewer features. The syntax is not fully compatible with other more widely-used +regular expression syntaxes. Read the Lua manual for full details. + +```lua +anti_spam_block_patterns = { + -- Block OTR handshake greetings (modern XMPP clients do not use OTR) + "^%?OTRv2?3?%?"; +} +``` + +There are no string or pattern filters in the module by default. + +## Handling reports + +We recommend setting up Prosody to allow spam reporting, in case any spam +still gets through. Documentation can be found on [xmppbl.org's site](https://xmppbl.org/reports#server-operators). + +## Compatibility + +Compatible with Prosody 0.12 and later.
--- a/mod_atom/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -# Introduction - -This module exposes users [microblogging][xep277] on Prosodys built-in HTTP server. - -# Usage - -With default HTTP settings, the microblog of `user@example.com` would be -seen at `https://example.com:5281/atom/user`. - -# Configuration - -The module itself has no options. However it uses the access control -mechanisms in PubSub, so users must reconfigure their microblogging node -to allow access, by setting `access_model` to `open`. -E.g. Gajim has UI for this, look for "Personal Events" → "Configure -services". -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_atom/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,17 @@ +# Introduction + +This module exposes users [microblogging][xep277] on Prosodys built-in HTTP server. + +# Usage + +With default HTTP settings, the microblog of `user@example.com` would be +seen at `https://example.com:5281/atom/user`. + +# Configuration + +The module itself has no options. However it uses the access control +mechanisms in PubSub, so users must reconfigure their microblogging node +to allow access, by setting `access_model` to `open`. +E.g. Gajim has UI for this, look for "Personal Events" → "Configure +services". +
--- a/mod_auth_any/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -labels: -- 'Stage-Merged' -- 'Type-Auth' -summary: Authentication module that accepts any username and password -... - -Introduction -============ - -This module accepts any username and password, which can be useful for -testing. - -Configuration -============= - - authentication = "any" - -Compatibility -============= - -Should work with 0.8 and above.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_any/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,22 @@ +--- +labels: +- 'Stage-Merged' +- 'Type-Auth' +summary: Authentication module that accepts any username and password +... + +Introduction +============ + +This module accepts any username and password, which can be useful for +testing. + +Configuration +============= + + authentication = "any" + +Compatibility +============= + +Should work with 0.8 and above.
--- a/mod_auth_ccert/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: Client Certificate authentication module -... - -Introduction -============ - -This module implements PKI-style client certificate authentication. You -will therefore need your own Certificate Authority. How to set that up -is beyond the current scope of this document. - -Configuration -============= - - - authentication = "ccert" - certificate_match = "xmppaddr" -- or "email" - - c2s_ssl = { - cafile = "/path/to/your/ca.pem"; - capath = false; -- Disable capath inherited from built-in default - verify = {"peer"; "client_once"}; -- Ask for client certificate - verifyext = { - -- Don't validate client certs as if they were server certs - lsec_ignore_purpose = false - } - } - - -Compatibility -============= - - ----------------- -------------- - trunk Works - 0.10 and later Works - 0.9 and earlier Doesn't work - ----------------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_ccert/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,40 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: Client Certificate authentication module +... + +Introduction +============ + +This module implements PKI-style client certificate authentication. You +will therefore need your own Certificate Authority. How to set that up +is beyond the current scope of this document. + +Configuration +============= + + + authentication = "ccert" + certificate_match = "xmppaddr" -- or "email" + + c2s_ssl = { + cafile = "/path/to/your/ca.pem"; + capath = false; -- Disable capath inherited from built-in default + verify = {"peer"; "client_once"}; -- Ask for client certificate + verifyext = { + -- Don't validate client certs as if they were server certs + lsec_ignore_purpose = false + } + } + + +Compatibility +============= + + ----------------- -------------- + trunk Works + 0.10 and later Works + 0.9 and earlier Doesn't work + ----------------- --------------
--- a/mod_auth_custom_http/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -summary: HTTP Authentication using custom JSON protocol -... - -Introduction -============ - -To authenticate users, this module does a `POST` request to a configured -URL with a JSON payload. It is not async so requests block the server -until answered. - -Configuration -============= - -``` lua -VirtualHost "example.com" -authentication = "custom_http" -auth_custom_http = { - post_url = "http://api.example.com/auth"; -} -``` - -Protocol -======== - -The JSON payload consists of an object with `username` and `password` -members: - - {"username":"john","password":"secr1t"} - -The module expects the response body to be exactly `true` if the -username and password are correct.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_custom_http/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +--- +summary: HTTP Authentication using custom JSON protocol +... + +Introduction +============ + +To authenticate users, this module does a `POST` request to a configured +URL with a JSON payload. It is not async so requests block the server +until answered. + +Configuration +============= + +``` lua +VirtualHost "example.com" +authentication = "custom_http" +auth_custom_http = { + post_url = "http://api.example.com/auth"; +} +``` + +Protocol +======== + +The JSON payload consists of an object with `username` and `password` +members: + + {"username":"john","password":"secr1t"} + +The module expects the response body to be exactly `true` if the +username and password are correct.
--- a/mod_auth_dovecot/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: Dovecot authentication module -rockspec: - build: - modules: - mod_auth_dovecot: auth_dovecot/mod_auth_dovecot.lua - mod_auth_dovecot.sasl_dovecot: auth_dovecot/sasl_dovecot.lib.lua -... - -Introduction -============ - -This is a Prosody authentication plugin which uses Dovecot as the -backend. - -Configuration -============= - -As with all auth modules, there is no need to add this to -modules\_enabled. Simply add in the global section, or for the relevant -hosts: - - authentication = "dovecot" - -These options are used by mod\_auth\_dovecot: - - Name Description Default value - ----------------------- ----------------------------------------- ------------------------------- - dovecot\_auth\_socket Path to the Dovecot auth socket "/var/run/dovecot/auth-login" - auth\_append\_host If true, sends the bare JID as authzid. false - -The Dovecot user and group must have access to connect to this socket. -You can create a new dedicated socket for Prosody too. Add the below to -the *socket listen* section of /etc/dovecot/dovecot.conf, and match the -socket path in Prosody's dovecot\_auth\_socket setting. - - service auth { - unix_listener /var/spool/prosody/private/auth-client { - mode = 0660 - user = prosody - group = prosody - } - } - -Make sure the socket directories exist and are owned by the Prosody -user. - -Note: Dovecot uses UNIX sockets by default. luasocket is compiled with -UNIX socket on debian/ubuntu by default, but is not on many other -platforms. If you run into this issue, you would need to either -recompile luasocket with UNIX socket support, or use Dovecot 2.x's TCP -socket support. - -TCP socket support for Dovecot 2.x ----------------------------------- - -Dovecot 2.x includes TCP socket support. These are the relevant -mod\_auth\_dovecot options: - - Name Description Default value - --------------------- ------------------------- ---------------------------- - dovecot\_auth\_host Hostname to connect to. "127.0.0.1" - dovecot\_auth\_port Port to connect to. *(this value is required)* - -Compatibility -============= - - ------- ------- - trunk Works - 0.8 Works - ------- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_dovecot/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,74 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: Dovecot authentication module +rockspec: + build: + modules: + mod_auth_dovecot: auth_dovecot/mod_auth_dovecot.lua + mod_auth_dovecot.sasl_dovecot: auth_dovecot/sasl_dovecot.lib.lua +... + +Introduction +============ + +This is a Prosody authentication plugin which uses Dovecot as the +backend. + +Configuration +============= + +As with all auth modules, there is no need to add this to +modules\_enabled. Simply add in the global section, or for the relevant +hosts: + + authentication = "dovecot" + +These options are used by mod\_auth\_dovecot: + + Name Description Default value + ----------------------- ----------------------------------------- ------------------------------- + dovecot\_auth\_socket Path to the Dovecot auth socket "/var/run/dovecot/auth-login" + auth\_append\_host If true, sends the bare JID as authzid. false + +The Dovecot user and group must have access to connect to this socket. +You can create a new dedicated socket for Prosody too. Add the below to +the *socket listen* section of /etc/dovecot/dovecot.conf, and match the +socket path in Prosody's dovecot\_auth\_socket setting. + + service auth { + unix_listener /var/spool/prosody/private/auth-client { + mode = 0660 + user = prosody + group = prosody + } + } + +Make sure the socket directories exist and are owned by the Prosody +user. + +Note: Dovecot uses UNIX sockets by default. luasocket is compiled with +UNIX socket on debian/ubuntu by default, but is not on many other +platforms. If you run into this issue, you would need to either +recompile luasocket with UNIX socket support, or use Dovecot 2.x's TCP +socket support. + +TCP socket support for Dovecot 2.x +---------------------------------- + +Dovecot 2.x includes TCP socket support. These are the relevant +mod\_auth\_dovecot options: + + Name Description Default value + --------------------- ------------------------- ---------------------------- + dovecot\_auth\_host Hostname to connect to. "127.0.0.1" + dovecot\_auth\_port Port to connect to. *(this value is required)* + +Compatibility +============= + + ------- ------- + trunk Works + 0.8 Works + ------- -------
--- a/mod_auth_external_insecure/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ ---- -labels: -- 'Stage-Deprecated' -- 'Type-Auth' -summary: 'Authentication via external script/process (DEPRECATED)' -... - -Introduction -============ - -Allow client authentication to be handled by an external script/process. - -:::{.alert .alert-warning} -**Warning:** This module is not currently maintained, and may be buggy and insecure in -certain configurations/environments. It is **not** recommended for production use. Please -use one of the [many other authentication modules](/type_auth). -::: - -Installation -============ - -mod\_auth\_external\_insecure depends on a Lua module called -[lpty](http://www.tset.de/lpty/). You can install it on many platforms -using [LuaRocks](http://luarocks.org/), for example: - - sudo luarocks install lpty - -Configuration -============= - -As with all auth modules, there is no need to add this to -modules\_enabled. Simply add in the global section, or for the relevant -hosts: - - authentication = "external_insecure" - -These options are specific to mod\_auth\_external\_insecure: - - -------------------------- ------------------------------------------------------------------------------------------------------------------------- - external\_auth\_protocol May be "generic" or "ejabberd" (the latter for compatibility with ejabberd external auth scripts. Default is "generic". - external\_auth\_command The command/script to execute. - -------------------------- ------------------------------------------------------------------------------------------------------------------------- - -Two other options are also available, depending on whether the module is -running in 'blocking' or 'non-blocking' mode: - - --------------------------- -------------- ------------------------------------------------------------------------------------------------------------------ - external\_auth\_timeout blocking The number of seconds to wait for a response from the auth process. Default is 5. - external\_auth\_processes non-blocking The number of concurrent processes to spawn. Default is 1, increase to handle high connection rates efficiently. - --------------------------- -------------- ------------------------------------------------------------------------------------------------------------------ - -Blocking vs non-blocking ------------------------- - -Non-blocking mode is experimental and is disabled by default. - -Enable at your own risk if you fulfil these conditions: - -- Running Prosody trunk ([nightly](http://prosody.im/nightly/) build - 414+) or Prosody 0.11.x. -- [libevent](http://prosody.im/doc/libevent) is enabled in the config, - and LuaEvent is available. -- lpty (see installation above) is version 1.0.1 or later. - -```lua -external_auth_blocking = false; -``` - -Protocol -======== - -Prosody executes the given command/script, and sends it queries. - -Your auth script should simply read a line from standard input, and -write the result to standard output. It must do this in a loop, until -there's nothing left to read. Prosody can keep sending more lines to the -script, with a command on each line. - -Each command is one line, and the response is expected to be a single -line containing "0" for failure or "1" for success. Your script must -respond with "0" for anything it doesn't understand. - -There are three commands used at the moment: - -auth ----- - -Check if a user's password is valid. - -Example: `auth:username:example.com:abc123` - -Note: The password can contain colons. Make sure to handle that. - -isuser ------- - -Check if a user exists. - -Example: `isuser:username:example.com` - -setpass -------- - -Set a new password for the user. Implementing this is optional. - -Example: `setpass:username:example.com:abc123` - -Note: The password can contain colons. Make sure to handle that. - -ejabberd compatibility ---------------------- - -ejabberd implements a similar protocol. The main difference is that -Prosody's protocol is line-based, while ejabberd's is length-prefixed. - -Add this to your config if you need to use an ejabberd auth script: - - external_auth_protocol = "ejabberd" - -Compatibility -============= - - ----- ------- - 0.8 Works - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_external_insecure/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,126 @@ +--- +labels: +- 'Stage-Deprecated' +- 'Type-Auth' +summary: 'Authentication via external script/process (DEPRECATED)' +... + +Introduction +============ + +Allow client authentication to be handled by an external script/process. + +:::{.alert .alert-warning} +**Warning:** This module is not currently maintained, and may be buggy and insecure in +certain configurations/environments. It is **not** recommended for production use. Please +use one of the [many other authentication modules](/type_auth). +::: + +Installation +============ + +mod\_auth\_external\_insecure depends on a Lua module called +[lpty](http://www.tset.de/lpty/). You can install it on many platforms +using [LuaRocks](http://luarocks.org/), for example: + + sudo luarocks install lpty + +Configuration +============= + +As with all auth modules, there is no need to add this to +modules\_enabled. Simply add in the global section, or for the relevant +hosts: + + authentication = "external_insecure" + +These options are specific to mod\_auth\_external\_insecure: + + -------------------------- ------------------------------------------------------------------------------------------------------------------------- + external\_auth\_protocol May be "generic" or "ejabberd" (the latter for compatibility with ejabberd external auth scripts. Default is "generic". + external\_auth\_command The command/script to execute. + -------------------------- ------------------------------------------------------------------------------------------------------------------------- + +Two other options are also available, depending on whether the module is +running in 'blocking' or 'non-blocking' mode: + + --------------------------- -------------- ------------------------------------------------------------------------------------------------------------------ + external\_auth\_timeout blocking The number of seconds to wait for a response from the auth process. Default is 5. + external\_auth\_processes non-blocking The number of concurrent processes to spawn. Default is 1, increase to handle high connection rates efficiently. + --------------------------- -------------- ------------------------------------------------------------------------------------------------------------------ + +Blocking vs non-blocking +------------------------ + +Non-blocking mode is experimental and is disabled by default. + +Enable at your own risk if you fulfil these conditions: + +- Running Prosody trunk ([nightly](http://prosody.im/nightly/) build + 414+) or Prosody 0.11.x. +- [libevent](http://prosody.im/doc/libevent) is enabled in the config, + and LuaEvent is available. +- lpty (see installation above) is version 1.0.1 or later. + +```lua +external_auth_blocking = false; +``` + +Protocol +======== + +Prosody executes the given command/script, and sends it queries. + +Your auth script should simply read a line from standard input, and +write the result to standard output. It must do this in a loop, until +there's nothing left to read. Prosody can keep sending more lines to the +script, with a command on each line. + +Each command is one line, and the response is expected to be a single +line containing "0" for failure or "1" for success. Your script must +respond with "0" for anything it doesn't understand. + +There are three commands used at the moment: + +auth +---- + +Check if a user's password is valid. + +Example: `auth:username:example.com:abc123` + +Note: The password can contain colons. Make sure to handle that. + +isuser +------ + +Check if a user exists. + +Example: `isuser:username:example.com` + +setpass +------- + +Set a new password for the user. Implementing this is optional. + +Example: `setpass:username:example.com:abc123` + +Note: The password can contain colons. Make sure to handle that. + +ejabberd compatibility +--------------------- + +ejabberd implements a similar protocol. The main difference is that +Prosody's protocol is line-based, while ejabberd's is length-prefixed. + +Add this to your config if you need to use an ejabberd auth script: + + external_auth_protocol = "ejabberd" + +Compatibility +============= + + ----- ------- + 0.8 Works + 0.9 Works + ----- -------
--- a/mod_auth_ha1/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Type-Auth' -summary: | - Authentication module for 'HA1' hashed credentials in a text file, as - used by reTurnServer -... - -Introduction -============ - -This module authenticates users against hashed credentials stored in a -plain text file. The format is the same as that used by reTurnServer. - -Configuration -============= - - Name Default Description - ----------------- ---------- --------------------------------- - auth\_ha1\_file auth.txt Path to the authentication file - -Prosody reads the auth file at startup and on reload (e.g. SIGHUP). - -File Format -=========== - -The file format is text, with one user per line. Each line is broken -into four fields separated by colons (':'): - - username:ha1:host:status - - Field Description - ---------- ---------------------------------------------------------------------------------- - username The user's login name - ha1 An MD5 hash of "username:host:password" - host The XMPP hostname - status The status of the account. Prosody expects this to be just the text "authorized" - -More info can be found -[here](https://github.com/resiprocate/resiprocate/blob/master/reTurn/users.txt). - -Example -------- - - john:2a236a1a68765361c64da3b502d4e71c:example.com:authorized - mary:4ed7cf9cbe81e02dbfb814de6f84edf1:example.com:authorized - charlie:83002e42eb4515ec0070489339f2114c:example.org:authorized - -Constructing the hashes can be done manually using any MD5 utility, such -as md5sum. For example the user 'john' has the password 'hunter2', and -his hash can be calculated like this: - - echo -n "john:example.com:hunter2" | md5sum - - -Compatibility -============= - - ------ ------- - 0.9 Works - 0.10 Works - ------ -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_ha1/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,62 @@ +--- +labels: +- 'Stage-Beta' +- 'Type-Auth' +summary: | + Authentication module for 'HA1' hashed credentials in a text file, as + used by reTurnServer +... + +Introduction +============ + +This module authenticates users against hashed credentials stored in a +plain text file. The format is the same as that used by reTurnServer. + +Configuration +============= + + Name Default Description + ----------------- ---------- --------------------------------- + auth\_ha1\_file auth.txt Path to the authentication file + +Prosody reads the auth file at startup and on reload (e.g. SIGHUP). + +File Format +=========== + +The file format is text, with one user per line. Each line is broken +into four fields separated by colons (':'): + + username:ha1:host:status + + Field Description + ---------- ---------------------------------------------------------------------------------- + username The user's login name + ha1 An MD5 hash of "username:host:password" + host The XMPP hostname + status The status of the account. Prosody expects this to be just the text "authorized" + +More info can be found +[here](https://github.com/resiprocate/resiprocate/blob/master/reTurn/users.txt). + +Example +------- + + john:2a236a1a68765361c64da3b502d4e71c:example.com:authorized + mary:4ed7cf9cbe81e02dbfb814de6f84edf1:example.com:authorized + charlie:83002e42eb4515ec0070489339f2114c:example.org:authorized + +Constructing the hashes can be done manually using any MD5 utility, such +as md5sum. For example the user 'john' has the password 'hunter2', and +his hash can be calculated like this: + + echo -n "john:example.com:hunter2" | md5sum - + +Compatibility +============= + + ------ ------- + 0.9 Works + 0.10 Works + ------ -------
--- a/mod_auth_http/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: "Authenticate users against an external HTTP API" -... - -# Overview - -This authentication module allows Prosody to authenticate users against -an external HTTP service. - -# Configuration - -``` lua -VirtualHost "example.com" - authentication = "http" - http_auth_url = "http://example.com/auth" -``` - -If the API requires Prosody to authenticate, you can provide static -credentials using HTTP Basic authentication, like so: - -``` -http_auth_credentials = "prosody:secret-password" -``` - -# Developers - -This section contains information for developers who wish to implement a -HTTP service that Prosody can use for authentication. - -## Protocol - -Prosody will make a HTTP request to the configured API URL with an -appended `/METHOD` where `METHOD` is one of the methods described below. - -GET methods must expect a series of URL-encoded query parameters, while -POST requests will receive an URL-encoded form (i.e. -`application/x-www-form-urlencoded`). - -## Parameters - -user -: The username, e.g. `stephanie` for the JID `stephanie@example.com`. - -server -: The host part of the user's JID, e.g. `example.com` for the JID - `stephanie@example.com`. - -pass -: For methods that verify or set a user's password, the password will - be supplied in this parameter, otherwise it is not set. - -## Methods - -The only mandatory methods that the service must implement are `check_password` -and `user_exists`. Unsupported methods should return a HTTP status code -of `501 Not Implemented`, but other error codes will also be handled by -Prosody. - - Method HTTP method Success codes Error codes Response - -------- ---- --- ----------------- ----------------------------------------------------------------- - register POST 201 409 (user exists) - check\_password GET 200 A text string of `true` if the user exists, or `false` otherwise. - user\_exists GET 200 A text string of `true` if the user exists, or `false` otherwise. - set\_password POST 200, 201 or 204 - remove\_user POST 200, 201 or 204 - -## Examples - -With the following configuration: - -``` -authentication = "http" -http_auth_url = "https://auth.example.net/api" - -If a user connects and tries to log in to Prosody as "romeo@example.net" -with the password "iheartjuliet", Prosody would make the following HTTP -request: - -``` -https://auth.example.net/api/check_password?user=romeo&server=example.net&pass=iheartjuliet -``` - -# Compatibility - -Requires Prosody 0.11.0 or later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_http/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,86 @@ +--- +labels: +- Stage-Alpha +summary: "Authenticate users against an external HTTP API" +... + +# Overview + +This authentication module allows Prosody to authenticate users against +an external HTTP service. + +# Configuration + +``` lua +VirtualHost "example.com" + authentication = "http" + http_auth_url = "http://example.com/auth" +``` + +If the API requires Prosody to authenticate, you can provide static +credentials using HTTP Basic authentication, like so: + +``` +http_auth_credentials = "prosody:secret-password" +``` + +# Developers + +This section contains information for developers who wish to implement a +HTTP service that Prosody can use for authentication. + +## Protocol + +Prosody will make a HTTP request to the configured API URL with an +appended `/METHOD` where `METHOD` is one of the methods described below. + +GET methods must expect a series of URL-encoded query parameters, while +POST requests will receive an URL-encoded form (i.e. +`application/x-www-form-urlencoded`). + +## Parameters + +user +: The username, e.g. `stephanie` for the JID `stephanie@example.com`. + +server +: The host part of the user's JID, e.g. `example.com` for the JID + `stephanie@example.com`. + +pass +: For methods that verify or set a user's password, the password will + be supplied in this parameter, otherwise it is not set. + +## Methods + +The only mandatory methods that the service must implement are `check_password` +and `user_exists`. Unsupported methods should return a HTTP status code +of `501 Not Implemented`, but other error codes will also be handled by +Prosody. + + Method HTTP method Success codes Error codes Response + -------- ---- --- ----------------- ----------------------------------------------------------------- + register POST 201 409 (user exists) + check\_password GET 200 A text string of `true` if the user exists, or `false` otherwise. + user\_exists GET 200 A text string of `true` if the user exists, or `false` otherwise. + set\_password POST 200, 201 or 204 + remove\_user POST 200, 201 or 204 + +## Examples + +With the following configuration: + +``` lua +authentication = "http" +http_auth_url = "https://auth.example.net/api" +``` + +If a user connects and tries to log in to Prosody as "romeo@example.net" +with the password "iheartjuliet", Prosody would make the following HTTP +request: + + https://auth.example.net/api/check_password?user=romeo&server=example.net&pass=iheartjuliet + +# Compatibility + +Requires Prosody 0.11.0 or later.
--- a/mod_auth_http_async/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- Stage-Alpha -... - -Introduction -============ - -This is an experimental authentication module that does an asynchronous -HTTP call to verify username and password. - -Details -======= - -When a user attempts to authenticate to Prosody, this module takes the -username and password and does a HTTP GET request with [Basic -authentication][rfc7617] to the configured `http_auth_url`. - -Configuration -============= - -``` lua -VirtualHost "example.com" -authentication = "http_async" -http_auth_url = "http://example.com/auth" -``` - -Compatibility -============= - -Requires Prosody trunk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_http_async/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,31 @@ +--- +labels: +- Stage-Alpha +... + +Introduction +============ + +This is an experimental authentication module that does an asynchronous +HTTP call to verify username and password. + +Details +======= + +When a user attempts to authenticate to Prosody, this module takes the +username and password and does a HTTP GET request with [Basic +authentication][rfc7617] to the configured `http_auth_url`. + +Configuration +============= + +``` lua +VirtualHost "example.com" +authentication = "http_async" +http_auth_url = "http://example.com/auth" +``` + +Compatibility +============= + +Requires Prosody trunk
--- a/mod_auth_http_cookie/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ ---- -labels: -- Stage-Alpha -... - -Introduction -============ - -This is an experimental authentication module that does an asynchronous -HTTP call to verify username and password. - -This is a (possibly temporary) fork of mod_http_auth_async that adds -support for authentication using a cookie and SASL EXTERNAL. - -Details -======= - -When a user attempts to authenticate to Prosody, this module takes the -username and password and does a HTTP GET request with [Basic -authentication][rfc7617] to the configured `http_auth_url`. - -Configuration -============= - -``` lua -VirtualHost "example.com" - authentication = "http_auth_cookie" - http_auth_url = "http://example.com/auth" - http_cookie_auth_url = "https://example.com/testcookie.php?user=$user" -``` - -Cookie Authentication -===================== - -It is possible to link authentication to an existing web application. This -has the benefit that the user logging into the web application in their -browser will automatically log them into their XMPP account. - -There are some prerequisites for this to work: - - - The BOSH or Websocket requests must include the application's cookie in - the headers sent to Prosody. This typically means the web chat code needs - to be served from the same domain as the web application. - - - The web application must have a URL that returns 200 OK when called with - a valid cookie, and returns a different status code if the cookie is invalid - or not currently logged in. - - - The XMPP username for the user must be passed to Prosody by the client, or - returned in the 200 response from the web application. - -Set `http_cookie_auth_url` to the web application URL that is used to check the -cookie. You may use the variables `$host` for the XMPP host and `$user` for the -XMPP username. - -If the `$user` variable is included in the URL, the client must provide the username -via the "authzid" in the SASL EXTERNAL authentication mechanism. - -If the `$user` variable is *not* included in the URL, Prosody expects the web application's response to be the username instead, as UTF-8 text/plain. - -Compatibility -============= - -Requires Prosody trunk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_http_cookie/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,64 @@ +--- +labels: +- Stage-Alpha +... + +Introduction +============ + +This is an experimental authentication module that does an asynchronous +HTTP call to verify username and password. + +This is a (possibly temporary) fork of mod_http_auth_async that adds +support for authentication using a cookie and SASL EXTERNAL. + +Details +======= + +When a user attempts to authenticate to Prosody, this module takes the +username and password and does a HTTP GET request with [Basic +authentication][rfc7617] to the configured `http_auth_url`. + +Configuration +============= + +``` lua +VirtualHost "example.com" + authentication = "http_auth_cookie" + http_auth_url = "http://example.com/auth" + http_cookie_auth_url = "https://example.com/testcookie.php?user=$user" +``` + +Cookie Authentication +===================== + +It is possible to link authentication to an existing web application. This +has the benefit that the user logging into the web application in their +browser will automatically log them into their XMPP account. + +There are some prerequisites for this to work: + + - The BOSH or Websocket requests must include the application's cookie in + the headers sent to Prosody. This typically means the web chat code needs + to be served from the same domain as the web application. + + - The web application must have a URL that returns 200 OK when called with + a valid cookie, and returns a different status code if the cookie is invalid + or not currently logged in. + + - The XMPP username for the user must be passed to Prosody by the client, or + returned in the 200 response from the web application. + +Set `http_cookie_auth_url` to the web application URL that is used to check the +cookie. You may use the variables `$host` for the XMPP host and `$user` for the +XMPP username. + +If the `$user` variable is included in the URL, the client must provide the username +via the "authzid" in the SASL EXTERNAL authentication mechanism. + +If the `$user` variable is *not* included in the URL, Prosody expects the web application's response to be the username instead, as UTF-8 text/plain. + +Compatibility +============= + +Requires Prosody trunk
--- a/mod_auth_imap/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: IMAP authentication module -rockspec: - build: - modules: - mod_auth_imap: auth_imap/mod_auth_imap.lua - mod_auth_imap.sasl_imap: auth_imap/sasl_imap.lib.lua -... - -Introduction -============ - -This is a Prosody authentication plugin which uses a generic IMAP server -as the backend. - -Configuration -============= - - option type default - --------------------------------- --------- -------------------------------- - imap\_auth\_host string localhost - imap\_auth\_port number nil - imap\_auth\_realm string Same as the sasl\_realm option - imap\_auth\_service\_name string nil - auth\_append\_host boolean false - auth\_imap\_verify\_certificate boolean true - auth\_imap\_ssl table A SSL/TLS config
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_imap/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,30 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: IMAP authentication module +rockspec: + build: + modules: + mod_auth_imap: auth_imap/mod_auth_imap.lua + mod_auth_imap.sasl_imap: auth_imap/sasl_imap.lib.lua +... + +Introduction +============ + +This is a Prosody authentication plugin which uses a generic IMAP server +as the backend. + +Configuration +============= + + option type default + --------------------------------- --------- -------------------------------- + imap\_auth\_host string localhost + imap\_auth\_port number nil + imap\_auth\_realm string Same as the sasl\_realm option + imap\_auth\_service\_name string nil + auth\_append\_host boolean false + auth\_imap\_verify\_certificate boolean true + auth\_imap\_ssl table A SSL/TLS config
--- a/mod_auth_internal_yubikey/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Type-Auth' -summary: 'Two-factor authentication using Yubikeys' -... - -Introduction -============ - -A [YubiKey](http://www.yubico.com/yubikey) is a small USB -one-time-password (OTP) generator. - -The idea behind one-time-passwords is that they can, well, only be used -once. After authenticating with an OTP the only way to log in again is -to calculate another one and use that. The only (practical) way to -generate this is by inserting the (correct) Yubikey and pressing its -button. Acting as a USB keyboard it then "types" the OTP into the -password prompt of your XMPP client. - -Details -======= - -This self-contained module handles all the authentication of Yubikeys, -it does not for example depend on the Yubico authentication service, or -on any external system service such as PAM. - -When this module is enabled, only PLAIN authentication is enabled on the -server (because Prosody needs to receive the full password from the -client to decode it, not a hash), so connection encryption will -automatically be enforced by Prosody. - -Even if the password is intercepted it is of little use to the attacker -as it expires as soon as it is used. Additionally the data stored in -Prosody's DB is not enough to authenticate as the user if stolen by the -attacker. - -When this module is in use each user can either use normal password -authentication, or instead have their account associated with a -Yubikey - at which point only the key will work. - -Installation -============ - -Requires bitlib for Lua, and yubikey-lua from -http://code.matthewwild.co.uk/yubikey-lua . When properly installed, the -command `lua -lbit -lyubikey` should give you a Lua prompt with no -errors. - -Configuration -============= - -Associating keys ----------------- - -Each Yubikey is configured with several pieces of information that -Prosody needs to know. This information is shown in the Yubikey -personalization tool (the *yubikey-personalization* package in -Debian/Ubuntu). - -To associate a Yubikey with a user, run the following prosodyctl -command: - - prosodyctl mod_auth_internal_yubikey associate user@example.com - -This will run you through a series of questions about the information -Prosody requires about the key configuration. - -**NOTE:** All keys used with the server (rather, with a given host) must -all have a "public ID" (uid) of the same length. This length must be set -in the Prosody config with the 'yubikey\_prefix\_length' option. - -Instead of entering the information interactively it is also possible to -specify each option on the command-line (useful for automation) -via --option="value". The valid options are: - - password The user's password (may be blank) - ---------- -------------------------------------------------------------------------------------------- - fixed The public ID that the Yubikey prefixes to the OTP - uid The private ID that the Yubikey encrypts in the OTP - key The AES key that the Yubikey uses (may be blank if a global shared key is used, see below) - -If a password is configured for the user (recommended) they must enter -this into the password box immediately before the OTP. This password -doesn't have to be incredibly long or secure, but it prevents the -Yubikey being used for authentication if it is stolen and the password -isn't known. - -Configuring Prosody -------------------- - -To use this module for authentication, set in the config: - - authentication = "internal_yubikey" - -Module-specific options: - - yubikey\_prefix\_length (**REQUIRED**) The length of the public ID prefixed to the OTPs - ------------------------- ------------------------------------------------------------------------------------------------------------------- - yubikey\_global\_key If all Yubikeys use the same AES key, you can specify it here. Pass --key="" to prosodyctl when associating keys. - -If switching from a plaintext storage auth module then users without -Yubikeys associated with their account can continue to use their -existing passwords as normal, otherwise password resets are required. - -Compatibility -============= - - ----- ------- - 0.8 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_internal_yubikey/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,111 @@ +--- +labels: +- 'Stage-Beta' +- 'Type-Auth' +summary: 'Two-factor authentication using Yubikeys' +... + +Introduction +============ + +A [YubiKey](http://www.yubico.com/yubikey) is a small USB +one-time-password (OTP) generator. + +The idea behind one-time-passwords is that they can, well, only be used +once. After authenticating with an OTP the only way to log in again is +to calculate another one and use that. The only (practical) way to +generate this is by inserting the (correct) Yubikey and pressing its +button. Acting as a USB keyboard it then "types" the OTP into the +password prompt of your XMPP client. + +Details +======= + +This self-contained module handles all the authentication of Yubikeys, +it does not for example depend on the Yubico authentication service, or +on any external system service such as PAM. + +When this module is enabled, only PLAIN authentication is enabled on the +server (because Prosody needs to receive the full password from the +client to decode it, not a hash), so connection encryption will +automatically be enforced by Prosody. + +Even if the password is intercepted it is of little use to the attacker +as it expires as soon as it is used. Additionally the data stored in +Prosody's DB is not enough to authenticate as the user if stolen by the +attacker. + +When this module is in use each user can either use normal password +authentication, or instead have their account associated with a +Yubikey - at which point only the key will work. + +Installation +============ + +Requires bitlib for Lua, and yubikey-lua from +http://code.matthewwild.co.uk/yubikey-lua . When properly installed, the +command `lua -lbit -lyubikey` should give you a Lua prompt with no +errors. + +Configuration +============= + +Associating keys +---------------- + +Each Yubikey is configured with several pieces of information that +Prosody needs to know. This information is shown in the Yubikey +personalization tool (the *yubikey-personalization* package in +Debian/Ubuntu). + +To associate a Yubikey with a user, run the following prosodyctl +command: + + prosodyctl mod_auth_internal_yubikey associate user@example.com + +This will run you through a series of questions about the information +Prosody requires about the key configuration. + +**NOTE:** All keys used with the server (rather, with a given host) must +all have a "public ID" (uid) of the same length. This length must be set +in the Prosody config with the 'yubikey\_prefix\_length' option. + +Instead of entering the information interactively it is also possible to +specify each option on the command-line (useful for automation) +via --option="value". The valid options are: + + password The user's password (may be blank) + ---------- -------------------------------------------------------------------------------------------- + fixed The public ID that the Yubikey prefixes to the OTP + uid The private ID that the Yubikey encrypts in the OTP + key The AES key that the Yubikey uses (may be blank if a global shared key is used, see below) + +If a password is configured for the user (recommended) they must enter +this into the password box immediately before the OTP. This password +doesn't have to be incredibly long or secure, but it prevents the +Yubikey being used for authentication if it is stolen and the password +isn't known. + +Configuring Prosody +------------------- + +To use this module for authentication, set in the config: + + authentication = "internal_yubikey" + +Module-specific options: + + yubikey\_prefix\_length (**REQUIRED**) The length of the public ID prefixed to the OTPs + ------------------------- ------------------------------------------------------------------------------------------------------------------- + yubikey\_global\_key If all Yubikeys use the same AES key, you can specify it here. Pass --key="" to prosodyctl when associating keys. + +If switching from a plaintext storage auth module then users without +Yubikeys associated with their account can continue to use their +existing passwords as normal, otherwise password resets are required. + +Compatibility +============= + + ----- ------- + 0.8 Works + ----- -------
--- a/mod_auth_joomla/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: Joomla authentication module -... - -Introduction -============ - -This module allows you to authenticate against an Joomla database. - -Configuration -============= - -SQL connection paramaters are identical to those of [SQL -storage](https://prosody.im/doc/modules/mod_storage_sql) except for an -additional 'prefix' parameter that defaults to 'jos\_'.\_ - - authentication = "joomla" - sql = { -- See documentation for SQL storage - driver = "MySQL"; - database = "joomla"; - host = "localhost"; - username = "prosody"; - password = "secretpassword"; - - prefix = "jos_"; - } - -Compatibility -============= - -Prosody 0.8+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_joomla/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,34 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: Joomla authentication module +... + +Introduction +============ + +This module allows you to authenticate against an Joomla database. + +Configuration +============= + +SQL connection paramaters are identical to those of [SQL +storage](https://prosody.im/doc/modules/mod_storage_sql) except for an +additional 'prefix' parameter that defaults to 'jos\_'.\_ + + authentication = "joomla" + sql = { -- See documentation for SQL storage + driver = "MySQL"; + database = "joomla"; + host = "localhost"; + username = "prosody"; + password = "secretpassword"; + + prefix = "jos_"; + } + +Compatibility +============= + +Prosody 0.8+
--- a/mod_auth_ldap/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ ---- -labels: -- 'Stage-Merged' -- 'Type-Auth' -summary: LDAP authentication module -... - -Introduction -============ - -This is a Prosody authentication plugin which uses LDAP as the backend. - -Dependecies -=========== - -This module depends on [LuaLDAP](https://github.com/lualdap/lualdap) -for connecting to an LDAP server. - -Configuration -============= - -Copy the module to the prosody modules/plugins directory. - -In Prosody's configuration file, under the desired host section, add: - -``` {.lua} -authentication = "ldap" -ldap_base = "ou=people,dc=example,dc=com" -``` - -Further LDAP options are: - - Name Description Default value - --------------------- ---------------------------------------------------------------------------------------------------------------------- -------------------- - ldap\_base LDAP base directory which stores user accounts **Required field** - ldap\_server Space-separated list of hostnames or IPs, optionally with port numbers (e.g. "localhost:8389") `"localhost"` - ldap\_rootdn The distinguished name to auth against `""` (anonymous) - ldap\_password Password for rootdn `""` - ldap\_filter Search filter, with `$user` and `$host` substituted for user- and hostname `"(uid=$user)"` - ldap\_scope Search scope. other values: "base" and "onelevel" `"subtree"` - ldap\_tls Enable TLS (StartTLS) to connect to LDAP (can be true or false). The non-standard 'LDAPS' protocol is not supported. `false` - ldap\_mode How passwords are validated. `"bind"` - ldap\_admin\_filter Search filter to match admins, works like ldap\_filter - -**Note:** lua-ldap reads from `/etc/ldap/ldap.conf` and other files like -`~prosody/.ldaprc` if they exist. Users wanting to use a particular TLS -root certificate can specify it in the normal way using TLS\_CACERT in -the OpenLDAP config file. - -Modes -===== - -The `"getpasswd"` mode requires plain text access to passwords in LDAP -and feeds them into Prosodys authentication system. This enables more -secure authentication mechanisms but does not work for all deployments. - -The `"bind"` mode performs an LDAP bind, does not require plain text -access to passwords but limits you to the PLAIN authentication -mechanism. - -Compatibility -============= - -Works with 0.8 and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_ldap/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,64 @@ +--- +labels: +- 'Stage-Merged' +- 'Type-Auth' +summary: LDAP authentication module +... + +Introduction +============ + +This is a Prosody authentication plugin which uses LDAP as the backend. + +Dependecies +=========== + +This module depends on [LuaLDAP](https://github.com/lualdap/lualdap) +for connecting to an LDAP server. + +Configuration +============= + +Copy the module to the prosody modules/plugins directory. + +In Prosody's configuration file, under the desired host section, add: + +``` {.lua} +authentication = "ldap" +ldap_base = "ou=people,dc=example,dc=com" +``` + +Further LDAP options are: + + Name Description Default value + --------------------- ---------------------------------------------------------------------------------------------------------------------- -------------------- + ldap\_base LDAP base directory which stores user accounts **Required field** + ldap\_server Space-separated list of hostnames or IPs, optionally with port numbers (e.g. "localhost:8389") `"localhost"` + ldap\_rootdn The distinguished name to auth against `""` (anonymous) + ldap\_password Password for rootdn `""` + ldap\_filter Search filter, with `$user` and `$host` substituted for user- and hostname `"(uid=$user)"` + ldap\_scope Search scope. other values: "base" and "onelevel" `"subtree"` + ldap\_tls Enable TLS (StartTLS) to connect to LDAP (can be true or false). The non-standard 'LDAPS' protocol is not supported. `false` + ldap\_mode How passwords are validated. `"bind"` + ldap\_admin\_filter Search filter to match admins, works like ldap\_filter + +**Note:** lua-ldap reads from `/etc/ldap/ldap.conf` and other files like +`~prosody/.ldaprc` if they exist. Users wanting to use a particular TLS +root certificate can specify it in the normal way using TLS\_CACERT in +the OpenLDAP config file. + +Modes +===== + +The `"getpasswd"` mode requires plain text access to passwords in LDAP +and feeds them into Prosodys authentication system. This enables more +secure authentication mechanisms but does not work for all deployments. + +The `"bind"` mode performs an LDAP bind, does not require plain text +access to passwords but limits you to the PLAIN authentication +mechanism. + +Compatibility +============= + +Works with 0.8 and later.
--- a/mod_auth_ldap2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Type-Auth' -summary: Another take on LDAP authentication -... - -Introduction -============ - -See [mod\_lib\_ldap](mod_lib_ldap.html) for more information. - -Installation -============ - -You must install [mod\_lib\_ldap](mod_lib_ldap.html) to use this module. -After that, you need only copy mod\_auth\_ldap2.lua to your Prosody -installation's plugins directory. - -Configuration -============= - -In addition to the configuration that [mod\_lib\_ldap](mod_lib_ldap.html) -itself requires, this plugin also requires the following fields in the -ldap section: - -- user.filter -- admin (optional) - -See the README.html distributed with [mod\_lib\_ldap](mod_lib_ldap.html) for -details.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_ldap2/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,30 @@ +--- +labels: +- 'Type-Auth' +summary: Another take on LDAP authentication +... + +Introduction +============ + +See [mod\_lib\_ldap](mod_lib_ldap.html) for more information. + +Installation +============ + +You must install [mod\_lib\_ldap](mod_lib_ldap.html) to use this module. +After that, you need only copy mod\_auth\_ldap2.lua to your Prosody +installation's plugins directory. + +Configuration +============= + +In addition to the configuration that [mod\_lib\_ldap](mod_lib_ldap.html) +itself requires, this plugin also requires the following fields in the +ldap section: + +- user.filter +- admin (optional) + +See the README.html distributed with [mod\_lib\_ldap](mod_lib_ldap.html) for +details.
--- a/mod_auth_oauthbearer/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Type-Auth' -summary: OAuth authentication -... - -Introduction -============ - -This is an authentication module for the SASL OAUTHBEARER mechanism, as provided by `mod_sasl_oauthbearer`. - -You can use this to log in via OAuth, for example if you want your user's to log in with Github, Twitter, Reddit etc. - -The XMPP client needs get an OAuth token from the provider (e.g. Github) and send that to Prosody. -This module will then verify that token by calling the `oauth_url` you've configured. - -Configuration -============= - -Per VirtualHost, you'll need to supply your OAuth client Id, secret and the URL which -Prosody must call in order to verify the OAuth token it receives from the XMPP client. - -For example, for Github: - - oauth_client_id = "13f8e9cc8928b3409822" - oauth_client_secret = "983161fd3ah608ea7ef35382668aad1927463978" - oauth_url = "https://api.github.com/applications/{{oauth_client_id}}/tokens/{{password}}"; - - authentication = "oauthbearer"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_oauthbearer/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +labels: +- 'Type-Auth' +summary: OAuth authentication +... + +Introduction +============ + +This is an authentication module for the SASL OAUTHBEARER mechanism, as provided by `mod_sasl_oauthbearer`. + +You can use this to log in via OAuth, for example if you want your user's to log in with Github, Twitter, Reddit etc. + +The XMPP client needs get an OAuth token from the provider (e.g. Github) and send that to Prosody. +This module will then verify that token by calling the `oauth_url` you've configured. + +Configuration +============= + +Per VirtualHost, you'll need to supply your OAuth client Id, secret and the URL which +Prosody must call in order to verify the OAuth token it receives from the XMPP client. + +For example, for Github: + + oauth_client_id = "13f8e9cc8928b3409822" + oauth_client_secret = "983161fd3ah608ea7ef35382668aad1927463978" + oauth_url = "https://api.github.com/applications/{{oauth_client_id}}/tokens/{{password}}"; + + authentication = "oauthbearer"
--- a/mod_auth_pam/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: PAM authentication module ---- - -Introduction -============ - -This module makes Prosody authenticate users against PAM (Linux -Pluggable Authentication Modules) - -Dependencies -============ - -The module depends on [lua-pam](https://github.com/devurandom/lua-pam) -and [LuaPosix](https://github.com/luaposix/luaposix). - -Setup -===== - -Create a `/etc/pam.d/xmpp` with something like this: - - auth [success=1 default=ignore] pam_unix.so obscure sha512 nodelay - auth requisite pam_deny.so - auth required pam_permit.so - -And switch authentication provider in the Prosody config: - - authentication = "pam" - -Compatibility -============= - -Compatible with 0.9 and up
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_pam/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: PAM authentication module +--- + +Introduction +============ + +This module makes Prosody authenticate users against PAM (Linux +Pluggable Authentication Modules) + +Dependencies +============ + +The module depends on [lua-pam](https://github.com/devurandom/lua-pam) +and [LuaPosix](https://github.com/luaposix/luaposix). + +Setup +===== + +Create a `/etc/pam.d/xmpp` with something like this: + + auth [success=1 default=ignore] pam_unix.so obscure sha512 nodelay + auth requisite pam_deny.so + auth required pam_permit.so + +And switch authentication provider in the Prosody config: + + authentication = "pam" + +Compatibility +============= + +Compatible with 0.9 and up
--- a/mod_auth_phpbb3/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: PHPBB3 authentication module -... - -Introduction -============ - -This module allows you to authenticate against an PHPBB3 database. - -To support the `bcrypt` password hashing algorithm, install -[bcrypt](https://luarocks.org/modules/mikejsavage/bcrypt) from luarocks: - - luarocks install bcrypt - -Configuration -============= - -SQL connection paramaters are identical to those of [SQL -storage](https://prosody.im/doc/modules/mod_storage_sql). - - authentication = "phpbb3" - sql = { -- See documentation for SQL storage - driver = "MySQL"; - database = "phpbb3"; - host = "localhost"; - username = "prosody"; - password = "secretpassword"; - } - -Compatibility -============= - -Prosody 0.8+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_phpbb3/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: PHPBB3 authentication module +... + +Introduction +============ + +This module allows you to authenticate against an PHPBB3 database. + +To support the `bcrypt` password hashing algorithm, install +[bcrypt](https://luarocks.org/modules/mikejsavage/bcrypt) from luarocks: + + luarocks install bcrypt + +Configuration +============= + +SQL connection paramaters are identical to those of [SQL +storage](https://prosody.im/doc/modules/mod_storage_sql). + + authentication = "phpbb3" + sql = { -- See documentation for SQL storage + driver = "MySQL"; + database = "phpbb3"; + host = "localhost"; + username = "prosody"; + password = "secretpassword"; + } + +Compatibility +============= + +Prosody 0.8+
--- a/mod_auth_sql/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ ---- -labels: -- 'Type-Auth' -- 'Stage-Stable' -summary: SQL Database authentication module -... - -Introduction -============ - -Allow client authentication to be handled by an SQL database query. - -Unlike mod\_storage\_sql (which is supplied with Prosody) this module -allows for custom schemas (though currently it is required to edit the -source). - -Configuration -============= - -As with all auth modules, there is no need to add this to -modules\_enabled. Simply add in the global section, or for the relevant -hosts: - - authentication = "sql" - -This module reuses the database configuration of -[mod\_storage\_sql](http://prosody.im/doc/modules/mod_storage_sql) (the -'sql' option), which you can set even if you are not using SQL as -Prosody's primary storage backend. - -The query is currently hardcoded in the module, so you will need to edit -the module to change it. The default query is compatible with jabberd2 -DB schema. - -Compatibility -============= - - ----- ------- - 0.8 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_sql/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,40 @@ +--- +labels: +- 'Type-Auth' +- 'Stage-Stable' +summary: SQL Database authentication module +... + +Introduction +============ + +Allow client authentication to be handled by an SQL database query. + +Unlike mod\_storage\_sql (which is supplied with Prosody) this module +allows for custom schemas (though currently it is required to edit the +source). + +Configuration +============= + +As with all auth modules, there is no need to add this to +modules\_enabled. Simply add in the global section, or for the relevant +hosts: + + authentication = "sql" + +This module reuses the database configuration of +[mod\_storage\_sql](http://prosody.im/doc/modules/mod_storage_sql) (the +'sql' option), which you can set even if you are not using SQL as +Prosody's primary storage backend. + +The query is currently hardcoded in the module, so you will need to edit +the module to change it. The default query is compatible with jabberd2 +DB schema. + +Compatibility +============= + + ----- ------- + 0.8 Works + ----- -------
--- a/mod_auth_token/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -# mod_auth_token - -This module enables Prosody to authenticate time-based one-time-pin (TOTP) HMAC tokens. - -This is an alternative to "external authentication" which avoids the need to -make a blocking HTTP call to the external authentication service (usually a web application backend). - -Instead, the application generates the HMAC token, which is then sent to -Prosody via the XMPP client and Prosody verifies the authenticity of this -token. - -If the token is verified, then the user is authenticated. - -## Luarocks dependencies - -You'll need to install the following luarocks - - otp 0.1-5 - luatz 0.3-1 - -## How to generate the TOTP seed and shared signing secret - -You'll need a shared OTP_SEED value for generating time-based one-time-pin -(TOTP) values and a shared private key for signing the HMAC token. - -You can generate the OTP_SEED value with Python, like so: - - >>> import pyotp - >>> pyotp.random_base32() - u'XVGR73KMZH2M4XMY' - -and the shared secret key as follows: - - >>> import pyotp - >>> pyotp.random_base32(length=32) - u'JYXEX4IQOEYFYQ2S3MC5P4ZT4SDHYEA7' - -## Configuration - -Firest you need to enable the relevant modules to your Prosody.cfg file. - -Look for the line `modules_enabled` (either globally or for your -particular `VirtualHost`), and then add the following to tokens: - - modules_enabled = { - -- Token authentication - "auth_token"; - "sasl_token"; - } - -The previously generated token values also need to go into your Prosody.cfg file: - - authentication = "token"; - token_secret = "JYXEX4IQOEYFYQ2S3MC5P4ZT4SDHYEA7"; - otp_seed = "XVGR73KMZH2M4XMY"; - -The application that generates the tokens also needs access to these values. - -For an example on how to generate a token, take a look at the `generate_token` -function in the `test_token_auth.lua` file inside this directory. - -## Custom SASL auth - -This module depends on a custom SASL auth mechanism called X-TOKEN and which -is provided by the file `mod_sasl_token.lua`. - -Prosody doesn't automatically pick up this file, so you'll need to update your -configuration file's `plugin_paths` to link to this subdirectory (for example -to `/usr/lib/prosody-modules/mod_auth_token/`). - -## Generating the token - -Here's a Python snippet showing how you can generate the token that Prosody -will then verify: - - import base64 - import pyotp - import random - - # Constants - OTP_INTERVAL = 30 - OTP_DIGITS = 8 - - jid = '{}@{}'.format(username, domain) - - otp_service = pyotp.TOTP( - OTP_SEED, # OTP_SEED must be set to the value generated previously (see above) - digits=OTP_DIGITS, - interval=OTP_INTERVAL - ) - otp = otp_service.generate_otp(otp_service.timecode(datetime.utcnow())) - - nonce = ''.join([str(random.randint(0, 9)) for i in range(32)]) - string_to_sign = otp + nonce + jid - signature = hmac.new(token_secret, string_to_sign, hashlib.sha256).digest() - token = u"{} {}".format(otp+nonce, base64.b64encode(signature)) -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_token/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,97 @@ +# mod_auth_token + +This module enables Prosody to authenticate time-based one-time-pin (TOTP) HMAC tokens. + +This is an alternative to "external authentication" which avoids the need to +make a blocking HTTP call to the external authentication service (usually a web application backend). + +Instead, the application generates the HMAC token, which is then sent to +Prosody via the XMPP client and Prosody verifies the authenticity of this +token. + +If the token is verified, then the user is authenticated. + +## Luarocks dependencies + +You'll need to install the following luarocks + + otp 0.1-5 + luatz 0.3-1 + +## How to generate the TOTP seed and shared signing secret + +You'll need a shared OTP_SEED value for generating time-based one-time-pin +(TOTP) values and a shared private key for signing the HMAC token. + +You can generate the OTP_SEED value with Python, like so: + + >>> import pyotp + >>> pyotp.random_base32() + u'XVGR73KMZH2M4XMY' + +and the shared secret key as follows: + + >>> import pyotp + >>> pyotp.random_base32(length=32) + u'JYXEX4IQOEYFYQ2S3MC5P4ZT4SDHYEA7' + +## Configuration + +Firest you need to enable the relevant modules to your Prosody.cfg file. + +Look for the line `modules_enabled` (either globally or for your +particular `VirtualHost`), and then add the following to tokens: + + modules_enabled = { + -- Token authentication + "auth_token"; + "sasl_token"; + } + +The previously generated token values also need to go into your Prosody.cfg file: + + authentication = "token"; + token_secret = "JYXEX4IQOEYFYQ2S3MC5P4ZT4SDHYEA7"; + otp_seed = "XVGR73KMZH2M4XMY"; + +The application that generates the tokens also needs access to these values. + +For an example on how to generate a token, take a look at the `generate_token` +function in the `test_token_auth.lua` file inside this directory. + +## Custom SASL auth + +This module depends on a custom SASL auth mechanism called X-TOKEN and which +is provided by the file `mod_sasl_token.lua`. + +Prosody doesn't automatically pick up this file, so you'll need to update your +configuration file's `plugin_paths` to link to this subdirectory (for example +to `/usr/lib/prosody-modules/mod_auth_token/`). + +## Generating the token + +Here's a Python snippet showing how you can generate the token that Prosody +will then verify: + + import base64 + import pyotp + import random + + # Constants + OTP_INTERVAL = 30 + OTP_DIGITS = 8 + + jid = '{}@{}'.format(username, domain) + + otp_service = pyotp.TOTP( + OTP_SEED, # OTP_SEED must be set to the value generated previously (see above) + digits=OTP_DIGITS, + interval=OTP_INTERVAL + ) + otp = otp_service.generate_otp(otp_service.timecode(datetime.utcnow())) + + nonce = ''.join([str(random.randint(0, 9)) for i in range(32)]) + string_to_sign = otp + nonce + jid + signature = hmac.new(token_secret, string_to_sign, hashlib.sha256).digest() + token = u"{} {}".format(otp+nonce, base64.b64encode(signature)) +
--- a/mod_auth_wordpress/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Auth' -summary: Wordpress authentication module -... - -Introduction -============ - -This module allows you to authenticate against an Wordpress database. - -Configuration -============= - -SQL connection paramaters are identical to those of [SQL -storage](https://prosody.im/doc/modules/mod_storage_sql). - - authentication = "wordpress" - wordpress_table_prefix = "wp_" -- default table prefix - sql = { -- See documentation for SQL storage - driver = "MySQL"; - database = "my_wordpress"; - host = "localhost"; - username = "prosody"; - password = "secretpassword"; - } - -Compatibility -============= - -Prosody 0.8+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auth_wordpress/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Auth' +summary: Wordpress authentication module +... + +Introduction +============ + +This module allows you to authenticate against an Wordpress database. + +Configuration +============= + +SQL connection paramaters are identical to those of [SQL +storage](https://prosody.im/doc/modules/mod_storage_sql). + + authentication = "wordpress" + wordpress_table_prefix = "wp_" -- default table prefix + sql = { -- See documentation for SQL storage + driver = "MySQL"; + database = "my_wordpress"; + host = "localhost"; + username = "prosody"; + password = "secretpassword"; + } + +Compatibility +============= + +Prosody 0.8+
--- a/mod_auto_accept_subscriptions/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Automatically accept incoming subscription requests on behalf of users -... - -Introduction -============ - -In some environments where all users on the system have mutual trust in -each other, it's sometimes fine to skip the usual authorization process -to add someone to your contact list and see their status. - -This module sets Prosody to automatically accept incoming subscription -authorization requests, and add the contact to the user's contact list, -without intervention from the user. - -Configuration -============= - -Simply add the module to your modules\_enabled list like any other -module: - - modules_enabled = { - ... - "auto_accept_subscriptions"; - ... - } - -This module has no further configuration. - -Compatibility -============= - - ------- ------- - trunk Works - 0.9 Works - 0.8 Works - ------- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auto_accept_subscriptions/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,39 @@ +--- +labels: +- 'Stage-Beta' +summary: Automatically accept incoming subscription requests on behalf of users +... + +Introduction +============ + +In some environments where all users on the system have mutual trust in +each other, it's sometimes fine to skip the usual authorization process +to add someone to your contact list and see their status. + +This module sets Prosody to automatically accept incoming subscription +authorization requests, and add the contact to the user's contact list, +without intervention from the user. + +Configuration +============= + +Simply add the module to your modules\_enabled list like any other +module: + + modules_enabled = { + ... + "auto_accept_subscriptions"; + ... + } + +This module has no further configuration. + +Compatibility +============= + + Prosody-Version Status + --------------- ----------- + 0.12 Should work + 0.11 Should work + 0.10 Works
--- a/mod_auto_activate_hosts/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Automatically activate/deactivate hosts on reload' -... - -Introduction -============ - -By default Prosody does not automatically activate/deactivate hosts when -they are added to and removed from the configuration. - -This module will activate/deactivate hosts as necessary when the -configuration is reloaded. - -This module was sponsored by [Exa Networks](http://exa-networks.co.uk/). - -Configuration -============= - -Add the module to your **global** modules\_enabled list: - - modules_enabled = { - ... - "auto_activate_hosts"; - ... - } - -There are no configuration options for this module. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auto_activate_hosts/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +labels: +- 'Stage-Beta' +summary: 'Automatically activate/deactivate hosts on reload' +... + +Introduction +============ + +By default Prosody does not automatically activate/deactivate hosts when +they are added to and removed from the configuration. + +This module will activate/deactivate hosts as necessary when the +configuration is reloaded. + +This module was sponsored by [Exa Networks](http://exa-networks.co.uk/). + +Configuration +============= + +Add the module to your **global** modules\_enabled list: + + modules_enabled = { + ... + "auto_activate_hosts"; + ... + } + +There are no configuration options for this module. + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_auto_answer_disco_info/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ ---- -summary: Answers disco#info queries on the behalf of the recipient -rockspec: - dependencies: - - mod_cache_c2s_caps ---- - -Description -=========== - -This module intercepts disco#info queries and checks if we already know the -capabilities of this session, in which case we don’t transmit the iq and answer -it ourselves.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auto_answer_disco_info/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,13 @@ +--- +summary: Answers disco#info queries on the behalf of the recipient +rockspec: + dependencies: + - mod_cache_c2s_caps +--- + +Description +=========== + +This module intercepts disco#info queries and checks if we already know the +capabilities of this session, in which case we don’t transmit the iq and answer +it ourselves.
--- a/mod_auto_moved/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -summary: "XEP-0283: Moved" -labels: -- 'Stage-Alpha' -... - -Introduction -============ - -This module implements [XEP-0283: Moved](http://xmpp.org/extensions/xep-0283.html), -a way for contacts to notify you that they have moved to a new address. - -This module is not necessary to generate such notifications - that can be done -by clients, for example. What this module does is automatically verify such -notifications and, if verification is successful, automatically update your -roster (contact list). - -Configuration -============= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -Compatibility -============= - - ----- ------- - 0.11 Does not work - ----- ------- - trunk Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_auto_moved/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +summary: "XEP-0283: Moved" +labels: +- 'Stage-Alpha' +... + +Introduction +============ + +This module implements [XEP-0283: Moved](http://xmpp.org/extensions/xep-0283.html), +a way for contacts to notify you that they have moved to a new address. + +This module is not necessary to generate such notifications - that can be done +by clients, for example. What this module does is automatically verify such +notifications and, if verification is successful, automatically update your +roster (contact list). + +Configuration +============= + +There is no configuration for this module, just add it to `modules_enabled` as normal. + +Compatibility +============= + + Prosody-Version Status + --------------- ----------- + trunk Should Work + 0.12 Works
--- a/mod_aws_profile/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -# Introduction - -This module adds support for reading AWS IAM access credentials from EC2 instance metadata, -to allow Prosody modules to gain role-based access to AWS services. - -# Configuring - -``` {.lua} -modules_enabled = { - "aws_profile"; -} -``` - -There is no other configuration. - -# Usage in other modules - -Other modules can import the credentials as a shared table: - -``` {.lua} -local aws_credentials = module:shared("/*/aws_profile/credentials"); -do_something(aws_credentials.access_key, aws_credentials.secret_key); -``` - -Note that credentials are time-limited, and will change periodically. The -shared table will automatically be updated. If you need to know when this -happens, you can also hook the `'aws_profile/credentials-refreshed'` event: - -``` {.lua} -module:hook_global("aws_profile/credentials-refreshed", function (new_credentials) - -- do something with new_credentials.access_key/secret_key -end); -``` - -# Compatibility - -Meant for use with Prosody 0.11.x, may work in older versions.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_aws_profile/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,37 @@ +# Introduction + +This module adds support for reading AWS IAM access credentials from EC2 instance metadata, +to allow Prosody modules to gain role-based access to AWS services. + +# Configuring + +``` {.lua} +modules_enabled = { + "aws_profile"; +} +``` + +There is no other configuration. + +# Usage in other modules + +Other modules can import the credentials as a shared table: + +``` {.lua} +local aws_credentials = module:shared("/*/aws_profile/credentials"); +do_something(aws_credentials.access_key, aws_credentials.secret_key); +``` + +Note that credentials are time-limited, and will change periodically. The +shared table will automatically be updated. If you need to know when this +happens, you can also hook the `'aws_profile/credentials-refreshed'` event: + +``` {.lua} +module:hook_global("aws_profile/credentials-refreshed", function (new_credentials) + -- do something with new_credentials.access_key/secret_key +end); +``` + +# Compatibility + +Meant for use with Prosody 0.11.x, may work in older versions.
--- a/mod_benchmark_storage/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -summary: Storage benchmark tool -... - -To benchmark `mod_storage_internal`: - - prosodyctl mod_benchmark_storage internal - -Does a number (aprox. 10000) of reads and writes and to the selected -storage driver and tells you how long it took.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_benchmark_storage/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,10 @@ +--- +summary: Storage benchmark tool +... + +To benchmark `mod_storage_internal`: + + prosodyctl mod_benchmark_storage internal + +Does a number (aprox. 10000) of reads and writes and to the selected +storage driver and tells you how long it took.
--- a/mod_bidi/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -labels: -- Stage-Stable -summary: "XEP-0288: Bidirectional Server-to-Server Connections" ---- - -::: {.alert .alert-warning} -This module is unreliable when used with Prosody 0.12, switch to -[mod_s2s_bidi][doc:modules:mod_s2s_bidi] -::: - -# Introduction - -This module implements [XEP-0288: Bidirectional Server-to-Server -Connections](http://xmpp.org/extensions/xep-0288.html). It allows -servers to use a single connection for sending stanzas to each other, -instead of two connections (one for stanzas in each direction). - -Install and enable it like any other module. It has no configuration. - -# Compatibility - - ------ ------------------------------------------- - 0.12 Bidi available natively with [mod_s2s_bidi][doc:modules:mod_s2s_bidi] - 0.11 Works - ------ -------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_bidi/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,10 @@ +--- +labels: +- Stage-Obsolete +superseded_by: mod_s2s_bidi +summary: "XEP-0288: Bidirectional Server-to-Server Connections" +--- + +::: {.alert .alert-warning} +This module is obsolete, see [mod_s2s_bidi][doc:modules:mod_s2s_bidi] that is shipped with prosody since Version 0.12. +:::
--- a/mod_bidi/mod_bidi.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ --- Bidirectional Server-to-Server Connections --- http://xmpp.org/extensions/xep-0288.html --- Copyright (C) 2013 Kim Alvefur --- --- This file is MIT/X11 licensed. --- -local add_filter = require "util.filters".add_filter; -local st = require "util.stanza"; -local jid_split = require"util.jid".prepped_split; -local core_process_stanza = prosody.core_process_stanza; -local traceback = debug.traceback; -local hosts = hosts; -local xmlns_bidi_feature = "urn:xmpp:features:bidi" -local xmlns_bidi = "urn:xmpp:bidi"; -local secure_only = module:get_option_boolean("secure_bidi_only", true); -local disable_bidi_for = module:get_option_set("no_bidi_with", { }); -local bidi_sessions = module:shared"sessions-cache"; - -local function handleerr(err) log("error", "Traceback[s2s]: %s: %s", tostring(err), traceback()); end -local function handlestanza(session, stanza) - if stanza.attr.xmlns == "jabber:client" then --COMPAT: Prosody pre-0.6.2 may send jabber:client - stanza.attr.xmlns = nil; - end - -- stanza = session.filter("stanzas/in", stanza); - if stanza then - return xpcall(function () return core_process_stanza(session, stanza) end, handleerr); - end -end - -local function new_bidi(origin) - if origin.type == "s2sin" then -- then we create an "outgoing" bidirectional session - local conflicting_session = hosts[origin.to_host].s2sout[origin.from_host] - if conflicting_session then - conflicting_session.log("info", "We already have an outgoing connection to %s, closing it...", origin.from_host); - conflicting_session:close{ condition = "conflict", text = "Replaced by bidirectional stream" } - end - bidi_sessions[origin.from_host] = origin; - origin.is_bidi = true; - origin.outgoing = true; - elseif origin.type == "s2sout" then -- handle incoming stanzas correctly - local bidi_session = { - type = "s2sin"; direction = "incoming"; - incoming = true; - is_bidi = true; orig_session = origin; - to_host = origin.from_host; - from_host = origin.to_host; - hosts = {}; - } - origin.bidi_session = bidi_session; - setmetatable(bidi_session, { __index = origin }); - module:fire_event("s2s-authenticated", { session = bidi_session, host = origin.to_host }); - local remote_host = origin.to_host; - add_filter(origin, "stanzas/in", function(stanza) - if stanza.attr.xmlns ~= nil then return stanza end - local _, host = jid_split(stanza.attr.from); - if host ~= remote_host then return stanza end - handlestanza(bidi_session, stanza); - end, 1); - end -end - -module:hook("route/remote", function(event) - local from_host, to_host, stanza = event.from_host, event.to_host, event.stanza; - if from_host ~= module.host then return end - local to_session = bidi_sessions[to_host]; - if not to_session or to_session.type ~= "s2sin" then return end - if to_session.sends2s(stanza) then return true end -end, -2); - --- Incoming s2s -module:hook("s2s-stream-features", function(event) - local origin, features = event.origin, event.features; - if not origin.is_bidi and not origin.bidi_session and not origin.do_bidi - and not hosts[module.host].s2sout[origin.from_host] - and not disable_bidi_for:contains(origin.from_host) - and (not secure_only or (origin.cert_chain_status == "valid" - and origin.cert_identity_status == "valid")) then - if origin.incoming == true then - module:log("warn", "This module can now be replaced by mod_s2s_bidi which is included with Prosody"); - end - module:log("debug", "Announcing support for bidirectional streams"); - features:tag("bidi", { xmlns = xmlns_bidi_feature }):up(); - end -end); - -module:hook("stanza/urn:xmpp:bidi:bidi", function(event) - local origin = event.session or event.origin; - if not origin.is_bidi and not origin.bidi_session - and not disable_bidi_for:contains(origin.from_host) - and (not secure_only or origin.cert_chain_status == "valid" - and origin.cert_identity_status == "valid") then - module:log("debug", "%s requested bidirectional stream", origin.from_host); - origin.do_bidi = true; - return true; - end -end); - --- Outgoing s2s -module:hook("stanza/http://etherx.jabber.org/streams:features", function(event) - local origin = event.session or event.origin; - if not ( origin.bidi_session or origin.is_bidi or origin.do_bidi) - and not disable_bidi_for:contains(origin.to_host) - and event.stanza:get_child("bidi", xmlns_bidi_feature) - and (not secure_only or origin.cert_chain_status == "valid" - and origin.cert_identity_status == "valid") then - if origin.outgoing == true then - module:log("warn", "This module can now be replaced by mod_s2s_bidi which is included with Prosody"); - end - module:log("debug", "%s supports bidirectional streams", origin.to_host); - origin.sends2s(st.stanza("bidi", { xmlns = xmlns_bidi })); - origin.do_bidi = true; - end -end, 160); - -function enable_bidi(event) - local session = event.session; - if session.do_bidi and not ( session.is_bidi or session.bidi_session ) then - session.do_bidi = nil; - new_bidi(session); - end -end - -module:hook("s2sin-established", enable_bidi); -module:hook("s2sout-established", enable_bidi); - -function disable_bidi(event) - local session = event.session; - if session.type == "s2sin" then - bidi_sessions[session.from_host] = nil; - end -end - -module:hook("s2sin-destroyed", disable_bidi); -module:hook("s2sout-destroyed", disable_bidi); -
--- a/mod_bind2/README.md Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- Stage-Alpha -rockspec: - dependencies: - - mod_sasl2 -summary: "XEP-0386: Bind 2.0" ---- - -Experimental early implementation of [XEP-0386: Bind 2.0] for use with -[mod_sasl2].
--- a/mod_bind2/mod_bind2.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -local mm = require "core.modulemanager"; -local sm = require "core.sessionmanager"; - -local xmlns_sasl2 --[[<const>]] = "urn:xmpp:sasl:1"; -local xmlns_bind2 --[[<const>]] = "urn:xmpp:bind2:0"; -local xmlns_carbons --[[<const>]] = "urn:xmpp:carbons:2"; - -module:depends("sasl2"); -module:depends("carbons"); - -module:hook("stream-features", function(event) - local origin, features = event.origin, event.features; - if origin.type ~= "c2s_unauthed" then return end - features:tag("bind", xmlns_bind2):up(); -end); - -module:hook_tag(xmlns_sasl2, "authenticate", function (session, auth) - session.bind2 = auth:get_child("bind", xmlns_bind2); -end, 1); - -module:hook("sasl2/c2s/success", function (event) - local session = event.session; - if not session.bind2 then return end - - -- When it receives a bind 2.0 on an authenticated not-yet-bound session, the - -- server MUST: - - -- Clear the offline messages for this user, if any, without sending them (as - -- they will be provided by MAM). - if mm.is_loaded(module.host, "offline") then -- luacheck: ignore 542 - -- TODO - end - - -- Perform resource binding to a random resource (see 6120) - if not sm.bind_resource(session, nil) then - -- FIXME How should this be handled even? - session:close("reset"); - return true; - end - - -- Work out which contacts have unread messages in the user's MAM archive, - -- how many, and what the id of the last read message is - -- XXX How do we know what the last read message was? - -- TODO archive:summary(session.username, { after = ??? }); - - -- Get the id of the newest stanza in the user's MAM archive - -- TODO archive:find(session.username, { reverse = true, limit = 1 }); - - -- Silently enable carbons for this session - session.carbons = xmlns_carbons; - - -- After processing the bind stanza, as above, the server MUST respond with - -- an element of type 'bound' in the namespace 'urn:xmpp:bind2:0', as in the - -- below example - event.success:tag("bound", xmlns_bind2):text_tag("jid", session.full_jid):up(); - - session.bind2 = nil; -end);
--- a/mod_block_outgoing/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ ---- -summary: 'Block outgoing stanzas from users' -... - -Introduction -============ - -This module blocks all outgoing stanzas from a list of users. - -Using -===== - -Add mod_block_outgoing to the enabled modules in your config file: -``` {.lua} -modules_enabled = { - -- ... - "block_outgoing", - -- ... -} -``` - -Either in a section for a certain host or the global section define which users and what stanzas to block: -``` {.lua} -block_outgoing_users = { "romeo@example.com", "juliet@example.com" } -block_outgoing_stanzas = { "message", "iq", "presence" } -``` - -block_outgoing_stanzas defaults to "message" if not specified. - -Compatibility -============= - - ------- -------------- - trunk Doesn't work (uses is_admin) - 0.12 Works - 0.11 Works - ------- -------------- -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_block_outgoing/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,38 @@ +--- +summary: 'Block outgoing stanzas from users' +... + +Introduction +============ + +This module blocks all outgoing stanzas from a list of users. + +Using +===== + +Add mod_block_outgoing to the enabled modules in your config file: +``` {.lua} +modules_enabled = { + -- ... + "block_outgoing", + -- ... +} +``` + +Either in a section for a certain host or the global section define which users and what stanzas to block: +``` {.lua} +block_outgoing_users = { "romeo@example.com", "juliet@example.com" } +block_outgoing_stanzas = { "message", "iq", "presence" } +``` + +block_outgoing_stanzas defaults to "message" if not specified. + +Compatibility +============= + + ------- -------------- + trunk Doesn't work (uses is_admin) + 0.12 Works + 0.11 Works + ------- -------------- +
--- a/mod_block_registrations/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ -Introduction -============ - -On a server with public registration it is usually desirable to prevent -registration of certain "reserved" accounts, such as "admin". - -This plugin allows you to reserve individual usernames, or those -matching certain patterns. It also allows you to ensure that usernames -conform to a certain pattern. - -Configuration -============= - -Enable the module as any other: - - modules_enabled = { - "block_registrations"; - } - -You can then set some options to configure your desired policy: - - Option Default Description - ------------------------------ ------------------- ----------------------------------------------------------------------------------------------------------------------------------------------- - block_registrations_users *See source code* A list of reserved usernames - block_registrations_matching `{ }` A list of [Lua patterns](http://www.lua.org/manual/5.1/manual.html#5.4.1) matching reserved usernames (slower than block_registrations_users) - block_registrations_require `nil` A pattern that registered user accounts MUST match to be allowed - -Some examples: - - block_registrations_users = { "admin", "root", "xmpp" } - block_registrations_matching = { - "master$" -- matches anything ending with master: postmaster, hostmaster, webmaster, etc. - } - block_registrations_require = "^[a-zA-Z0-9_.-]+$" -- Allow only simple ASCII characters in usernames - -Compatibility -============= - - ------ ------- - 0.12 Works - 0.11 Work - ------ -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_block_registrations/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,42 @@ +Introduction +============ + +On a server with public registration it is usually desirable to prevent +registration of certain "reserved" accounts, such as "admin". + +This plugin allows you to reserve individual usernames, or those +matching certain patterns. It also allows you to ensure that usernames +conform to a certain pattern. + +Configuration +============= + +Enable the module as any other: + + modules_enabled = { + "block_registrations"; + } + +You can then set some options to configure your desired policy: + + Option Default Description + ------------------------------ ------------------- ----------------------------------------------------------------------------------------------------------------------------------------------- + block_registrations_users *See source code* A list of reserved usernames + block_registrations_matching `{ }` A list of [Lua patterns](http://www.lua.org/manual/5.1/manual.html#5.4.1) matching reserved usernames (slower than block_registrations_users) + block_registrations_require `nil` A pattern that registered user accounts MUST match to be allowed + +Some examples: + + block_registrations_users = { "admin", "root", "xmpp" } + block_registrations_matching = { + "master$" -- matches anything ending with master: postmaster, hostmaster, webmaster, etc. + } + block_registrations_require = "^[a-zA-Z0-9_.-]+$" -- Allow only simple ASCII characters in usernames + +Compatibility +============= + + ------ ------- + 0.12 Works + 0.11 Work + ------ -------
--- a/mod_blocking/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ ---- -labels: -- Stage-Deprecated -rockspec: - dependencies: - - mod_privacy_lists -summary: "XEP-0191: Simple Communications Blocking support" ---- - -::: {.alert .alert-warning} -This module is deprecated as it depends on the deprecated -[mod_privacy_lists], use the core module -[mod_blocklist][doc:modules:mod_blocklist] instead. -::: - -Introduction -============ - -Privacy lists are a widely implemented protocol for instructing your -server on blocking communications with selected users and services. - -However experience has shown that the power and flexibility of the -rule-based system that privacy lists allow is very often much more -complex than the user needs, and that in most cases a simple block on -all communications to or from a list of specified JIDs would suffice. - -Such a protocol would also allow much simpler user interface design than -the current attempts at full privacy list interfaces. - -Details -======= - -Simple Communications Blocking was developed to solve the above issues, -and allows the client to manage a simple list of blocked JIDs. This -plugin implements support for that protocol in Prosody, however the -actual blocking is still managed by mod\_privacy, so it is **required** -for that plugin to be loaded (this may change in future). - -An XEP-0191 implementation without dependency on mod\_privacy is -available in Prosody 0.10 as [mod\_blocklist][doc:modules:mod_blocklist]. - -Configuration -============= - -Simply ensure that [mod_privacy_lists] and mod_blocking are loaded in -your modules_enabled list: - - modules_enabled = { - -- ... - "privacy_lists", - "blocking", - -- ... - -Compatibility -============= - - ------ --------------------------------------------- - 0.10 Works but will conflict with mod\_blocklist - 0.9 Works - 0.8 Works - 0.7 Works - 0.6 Doesn't work - ------ ---------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_blocking/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,63 @@ +--- +labels: +- Stage-Deprecated +rockspec: + dependencies: + - mod_privacy_lists +summary: "XEP-0191: Simple Communications Blocking support" +--- + +::: {.alert .alert-warning} +This module is deprecated as it depends on the deprecated +[mod_privacy_lists], use the core module +[mod_blocklist][doc:modules:mod_blocklist] instead. +::: + +Introduction +============ + +Privacy lists are a widely implemented protocol for instructing your +server on blocking communications with selected users and services. + +However experience has shown that the power and flexibility of the +rule-based system that privacy lists allow is very often much more +complex than the user needs, and that in most cases a simple block on +all communications to or from a list of specified JIDs would suffice. + +Such a protocol would also allow much simpler user interface design than +the current attempts at full privacy list interfaces. + +Details +======= + +Simple Communications Blocking was developed to solve the above issues, +and allows the client to manage a simple list of blocked JIDs. This +plugin implements support for that protocol in Prosody, however the +actual blocking is still managed by mod\_privacy, so it is **required** +for that plugin to be loaded (this may change in future). + +An XEP-0191 implementation without dependency on mod\_privacy is +available in Prosody 0.10 as [mod\_blocklist][doc:modules:mod_blocklist]. + +Configuration +============= + +Simply ensure that [mod_privacy_lists] and mod_blocking are loaded in +your modules_enabled list: + + modules_enabled = { + -- ... + "privacy_lists", + "blocking", + -- ... + +Compatibility +============= + + ------ --------------------------------------------- + 0.10 Works but will conflict with mod\_blocklist + 0.9 Works + 0.8 Works + 0.7 Works + 0.6 Doesn't work + ------ ---------------------------------------------
--- a/mod_bob/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ ---- -summary: Cache Bits of Binary on MUC services -rockspec: - dependencies: - - mod_cache_c2s_caps ---- - -Description -=========== - -This module extracts cid: URIs (as defined in XEP-0231) from messages, and -replies with their content whenever another client asks for the actual data. - -Usage -===== - -```lua -Component "rooms.example.org" "muc" - modules_enabled = { - "bob"; - ... - } -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_bob/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,23 @@ +--- +summary: Cache Bits of Binary on MUC services +rockspec: + dependencies: + - mod_cache_c2s_caps +--- + +Description +=========== + +This module extracts cid: URIs (as defined in XEP-0231) from messages, and +replies with their content whenever another client asks for the actual data. + +Usage +===== + +```lua +Component "rooms.example.org" "muc" + modules_enabled = { + "bob"; + ... + } +```
--- a/mod_bookmarks/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: Synchronise bookmarks between Private XML and PEP -... - -::: {.alert .alert-warning} -**WARNING:** This module is deprecated, please use -[mod\_bookmarks2] going forward or -[mod_bookmarks included with Prosody][doc:modules:mod_bookmarks] -:::
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_bookmarks/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,10 @@ +--- +superseded_by: mod_bookmarks +labels: +- 'Stage-Obsolete' +summary: Synchronise bookmarks between Private XML and PEP +... + +::: {.alert .alert-warning} +This module is obsolete, please use [mod_bookmarks included with Prosody][doc:modules:mod_bookmarks] +:::
--- a/mod_bookmarks/mod_bookmarks.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ ---% conflicts: mod_bookmarks -module:log("info", "mod_bookmarks has been deprecated, now loading mod_bookmarks2") -module:depends("bookmarks2")
--- a/mod_bookmarks2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -labels: -- 'Stage-Merged' -summary: Synchronise bookmarks between Private XML, legacy PEP, and PEP -... - -::: {.alert .alert-info} -**Deprecatation notice:** This module has been merged into Prosody as -[mod_bookmarks][doc:modules:mod_bookmarks]. Users of Prosody **0.12** -and later should switch to that. -::: - -Introduction ------------- - -This module fetches users’ bookmarks from Private XML (or legacy PEP) and -pushes them to PEP on login, and then redirects any Private XML query (or -legacy PEP) to PEP. This allows interoperability between older clients that -use [XEP-0048](https://xmpp.org/extensions/xep-0048.html) and recent clients -which use [XEP-0402](https://xmpp.org/extensions/xep-0402.html). - -Configuration -------------- - -Simply [enable it like most other -modules](https://prosody.im/doc/installing_modules#prosody-modules), no -further configuration is needed. - -Compatibility -------------- - - ------- ----------------------------------------- - 0.12 [Use the official mod_bookmarks module instead][doc:modules:mod_bookmarks] - 0.11 Works - 0.10 Does not work - 0.9 Does not work - ------- -----------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_bookmarks2/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,10 @@ +--- +labels: +- 'Stage-Merged' +summary: Synchronise bookmarks between Private XML, legacy PEP, and pep, XEP-0048 and XEP-0402 +... + +::: {.alert .alert-info} +This module has been merged into Prosody since version 0.12, +see [mod_bookmarks][doc:modules:mod_bookmarks]. +:::
--- a/mod_bookmarks2/mod_bookmarks2.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,429 +0,0 @@ - -local st = require "util.stanza"; -local jid_split = require "util.jid".split; - -local mod_pep = module:depends "pep"; -local private_storage = module:open_store("private", "map"); - -local namespace = "urn:xmpp:bookmarks:1"; -local namespace_private = "jabber:iq:private"; -local namespace_legacy = "storage:bookmarks"; - -local default_options = { - ["persist_items"] = true; - ["max_items"] = "max"; - ["send_last_published_item"] = "never"; - ["access_model"] = "whitelist"; -}; - -if not pcall(mod_pep.check_node_config, nil, nil, default_options) then - -- 0.11 or earlier not supporting max_items="max" trows an error here - module:log("debug", "Setting max_items=pep_max_items because 'max' is not supported in this version"); - default_options["max_items"] = module:get_option_number("pep_max_items", 256); - default_options["send_last_published_item"] = nil; -- not available in 0.11 -end - -module:hook("account-disco-info", function (event) - -- This Time it’s Serious! - event.reply:tag("feature", { var = namespace.."#compat" }):up(); - event.reply:tag("feature", { var = namespace.."#compat-pep" }):up(); -end); - --- This must be declared on the domain JID, not the account JID. Note that --- this isn’t defined in the XEP. -module:add_feature(namespace_private); - -local function generate_legacy_storage(items) - local storage = st.stanza("storage", { xmlns = namespace_legacy }); - for _, item_id in ipairs(items) do - local item = items[item_id]; - local bookmark = item:get_child("conference", namespace); - local conference = st.stanza("conference", { - jid = item.attr.id, - name = bookmark.attr.name, - autojoin = bookmark.attr.autojoin, - }); - local nick = bookmark:get_child_text("nick"); - if nick ~= nil then - conference:text_tag("nick", nick):up(); - end - local password = bookmark:get_child_text("password"); - if password ~= nil then - conference:text_tag("password", password):up(); - end - storage:add_child(conference); - end - - return storage; -end - -local function on_retrieve_legacy_pep(event) - local stanza, session = event.stanza, event.origin; - local pubsub = stanza:get_child("pubsub", "http://jabber.org/protocol/pubsub"); - if pubsub == nil then - return; - end - - local items = pubsub:get_child("items"); - if items == nil then - return; - end - - local node = items.attr.node; - if node ~= namespace_legacy then - return; - end - - local username = session.username; - local jid = username.."@"..session.host; - local service = mod_pep.get_pep_service(username); - local ok, ret = service:get_items(namespace, session.full_jid); - if not ok then - module:log("error", "Failed to retrieve PEP bookmarks of %s: %s", jid, ret); - session.send(st.error_reply(stanza, "cancel", ret, "Failed to retrive bookmarks from PEP")); - return true; - end - - local storage = generate_legacy_storage(ret); - - module:log("debug", "Sending back legacy PEP for %s: %s", jid, storage); - session.send(st.reply(stanza) - :tag("pubsub", {xmlns = "http://jabber.org/protocol/pubsub"}) - :tag("items", {node = namespace_legacy}) - :tag("item", {id = "current"}) - :add_child(storage)); - return true; -end - -local function on_retrieve_private_xml(event) - local stanza, session = event.stanza, event.origin; - local query = stanza:get_child("query", namespace_private); - if query == nil then - return; - end - - local bookmarks = query:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - module:log("debug", "Getting private bookmarks: %s", bookmarks); - - local username = session.username; - local jid = username.."@"..session.host; - local service = mod_pep.get_pep_service(username); - local ok, ret = service:get_items(namespace, session.full_jid); - if not ok then - if ret == "item-not-found" then - module:log("debug", "Got no PEP bookmarks item for %s, returning empty private bookmarks", jid); - session.send(st.reply(stanza):add_child(query)); - else - module:log("error", "Failed to retrieve PEP bookmarks of %s: %s", jid, ret); - session.send(st.error_reply(stanza, "cancel", ret, "Failed to retrive bookmarks from PEP")); - end - return true; - end - - local storage = generate_legacy_storage(ret); - - module:log("debug", "Sending back private for %s: %s", jid, storage); - session.send(st.reply(stanza):query(namespace_private):add_child(storage)); - return true; -end - -local function compare_bookmark2(a, b) - if a == nil or b == nil then - return false; - end - local a_conference = a:get_child("conference", namespace); - local b_conference = b:get_child("conference", namespace); - local a_nick = a_conference:get_child_text("nick"); - local b_nick = b_conference:get_child_text("nick"); - local a_password = a_conference:get_child_text("password"); - local b_password = b_conference:get_child_text("password"); - return (a.attr.id == b.attr.id and - a_conference.attr.name == b_conference.attr.name and - a_conference.attr.autojoin == b_conference.attr.autojoin and - a_nick == b_nick and - a_password == b_password); -end - -local function publish_to_pep(jid, bookmarks, synchronise) - local service = mod_pep.get_pep_service(jid_split(jid)); - - if #bookmarks.tags == 0 then - if synchronise then - -- If we set zero legacy bookmarks, purge the bookmarks 2 node. - module:log("debug", "No bookmark in the set, purging instead."); - local ok, err = service:purge(namespace, jid, true); - if not ok and err == "item-not-found" then - -- Nothing there already, all is well. - return true; - end - return ok, err; - else - return true; - end - end - - -- Retrieve the current bookmarks2. - module:log("debug", "Retrieving the current bookmarks 2."); - local has_bookmarks2, ret = service:get_items(namespace, jid); - local bookmarks2; - if not has_bookmarks2 and ret == "item-not-found" then - module:log("debug", "Got item-not-found, assuming it was empty until now, creating."); - local ok, err = service:create(namespace, jid, default_options); - if not ok then - module:log("error", "Creating bookmarks 2 node failed: %s", err); - return ok, err; - end - bookmarks2 = {}; - elseif not has_bookmarks2 then - module:log("debug", "Got %s error, aborting.", ret); - return false, ret; - else - module:log("debug", "Got existing bookmarks2."); - bookmarks2 = ret; - - local ok, err = service:get_node_config(namespace, jid); - if not ok then - module:log("error", "Retrieving bookmarks 2 node config failed: %s", err); - return ok, err; - end - - local options = err; - for key, value in pairs(default_options) do - if options[key] and options[key] ~= value then - module:log("warn", "Overriding bookmarks 2 configuration for %s, from %s to %s", jid, options[key], value); - options[key] = value; - end - end - - local ok, err = service:set_node_config(namespace, jid, options); - if not ok then - module:log("error", "Setting bookmarks 2 node config failed: %s", err); - return ok, err; - end - end - - -- Get a list of all items we may want to remove. - local to_remove = {}; - for i in ipairs(bookmarks2) do - to_remove[bookmarks2[i]] = true; - end - - for bookmark in bookmarks:childtags("conference", namespace_legacy) do - -- Create the new conference element by copying everything from the legacy one. - local conference = st.stanza("conference", { - xmlns = namespace, - name = bookmark.attr.name, - autojoin = bookmark.attr.autojoin, - }); - local nick = bookmark:get_child_text("nick"); - if nick ~= nil then - conference:text_tag("nick", nick):up(); - end - local password = bookmark:get_child_text("password"); - if password ~= nil then - conference:text_tag("password", password):up(); - end - - -- Create its wrapper. - local item = st.stanza("item", { xmlns = "http://jabber.org/protocol/pubsub", id = bookmark.attr.jid }) - :add_child(conference); - - -- Then publish it only if it’s a new one or updating a previous one. - if compare_bookmark2(item, bookmarks2[bookmark.attr.jid]) then - module:log("debug", "Item %s identical to the previous one, skipping.", item.attr.id); - to_remove[bookmark.attr.jid] = nil; - else - if bookmarks2[bookmark.attr.jid] == nil then - module:log("debug", "Item %s not existing previously, publishing.", item.attr.id); - else - module:log("debug", "Item %s different from the previous one, publishing.", item.attr.id); - to_remove[bookmark.attr.jid] = nil; - end - local ok, err = service:publish(namespace, jid, bookmark.attr.jid, item, default_options); - if not ok then - module:log("error", "Publishing item %s failed: %s", item.attr.id, err); - return ok, err; - end - end - end - - -- Now handle retracting items that have been removed. - if synchronise then - for id in pairs(to_remove) do - module:log("debug", "Item %s removed from bookmarks.", id); - local ok, err = service:retract(namespace, jid, id, st.stanza("retract", { id = id })); - if not ok then - module:log("error", "Retracting item %s failed: %s", id, err); - return ok, err; - end - end - end - return true; -end - --- Synchronise legacy PEP to PEP. -local function on_publish_legacy_pep(event) - local stanza, session = event.stanza, event.origin; - local pubsub = stanza:get_child("pubsub", "http://jabber.org/protocol/pubsub"); - if pubsub == nil then - return; - end - - local publish = pubsub:get_child("publish"); - if publish == nil or publish.attr.node ~= namespace_legacy then - return; - end - - local item = publish:get_child("item"); - if item == nil then - return; - end - - -- Here we ignore the item id, it’ll be generated as 'current' anyway. - - local bookmarks = item:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - -- We also ignore the publish-options. - - module:log("debug", "Legacy PEP bookmarks set by client, publishing to PEP."); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, true); - if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); - session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); - return true; - end - - session.send(st.reply(stanza)); - return true; -end - --- Synchronise Private XML to PEP. -local function on_publish_private_xml(event) - local stanza, session = event.stanza, event.origin; - local query = stanza:get_child("query", namespace_private); - if query == nil then - return; - end - - local bookmarks = query:get_child("storage", namespace_legacy); - if bookmarks == nil then - return; - end - - module:log("debug", "Private bookmarks set by client, publishing to PEP."); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, true); - if not ok then - module:log("error", "Failed to publish to PEP bookmarks for %s@%s: %s", session.username, session.host, err); - session.send(st.error_reply(stanza, "cancel", "internal-server-error", "Failed to store bookmarks to PEP")); - return true; - end - - session.send(st.reply(stanza)); - return true; -end - -local function migrate_legacy_bookmarks(event) - local session = event.session; - local username = session.username; - local service = mod_pep.get_pep_service(username); - local jid = username.."@"..session.host; - - local ok, ret = service:get_items(namespace_legacy, session.full_jid); - if ok then - module:log("debug", "Legacy PEP bookmarks found for %s, migrating.", jid); - local failed = false; - for _, item_id in ipairs(ret) do - local item = ret[item_id]; - if item.attr.id ~= "current" then - module:log("warn", "Legacy PEP bookmarks for %s isn’t using 'current' as its id: %s", jid, item.attr.id); - end - local bookmarks = item:get_child("storage", namespace_legacy); - module:log("debug", "Got legacy PEP bookmarks of %s: %s", jid, bookmarks); - - local ok, err = publish_to_pep(session.full_jid, bookmarks, false); - if not ok then - module:log("error", "Failed to store legacy PEP bookmarks to bookmarks 2 for %s, aborting migration: %s", jid, err); - failed = true; - break; - end - end - if not failed then - module:log("debug", "Successfully migrated legacy PEP bookmarks of %s to bookmarks 2, attempting deletion of the node.", jid); - local ok, err = service:delete(namespace_legacy, jid); - if not ok then - module:log("error", "Failed to delete legacy PEP bookmarks for %s: %s", jid, err); - end - end - end - - local data, err = private_storage:get(username, "storage:storage:bookmarks"); - if not data then - module:log("debug", "No existing legacy bookmarks for %s, migration already done: %s", jid, err); - local ok, ret2 = service:get_items(namespace, session.full_jid); - if not ok or not ret2 then - module:log("debug", "Additionally, no bookmarks 2 were existing for %s, assuming empty.", jid); - module:fire_event("bookmarks/empty", { session = session }); - end - return; - end - local bookmarks = st.deserialize(data); - module:log("debug", "Got legacy bookmarks of %s: %s", jid, bookmarks); - - module:log("debug", "Going to store legacy bookmarks to bookmarks 2 %s.", jid); - local ok, err = publish_to_pep(session.full_jid, bookmarks, false); - if not ok then - module:log("error", "Failed to store legacy bookmarks to bookmarks 2 for %s, aborting migration: %s", jid, err); - return; - end - module:log("debug", "Stored legacy bookmarks to bookmarks 2 for %s.", jid); - - local ok, err = private_storage:set(username, "storage:storage:bookmarks", nil); - if not ok then - module:log("error", "Failed to remove legacy bookmarks of %s: %s", jid, err); - return; - end - module:log("debug", "Removed legacy bookmarks of %s, migration done!", jid); -end - -local function on_node_created(event) - local service, node, actor = event.service, event.node, event.actor; - if node ~= namespace_legacy then - return; - end - - module:log("debug", "Something tried to create legacy PEP bookmarks for %s.", actor); - local ok, err = service:delete(namespace_legacy, actor); - if not ok then - module:log("error", "Failed to delete legacy PEP bookmarks for %s: %s", actor, err); - end - module:log("debug", "Legacy PEP bookmarks node of %s deleted.", actor); -end - -module:hook("iq/bare/jabber:iq:private:query", function (event) - if event.stanza.attr.type == "get" then - return on_retrieve_private_xml(event); - else - return on_publish_private_xml(event); - end -end, 1); -module:hook("iq/bare/http://jabber.org/protocol/pubsub:pubsub", function (event) - if event.stanza.attr.type == "get" then - return on_retrieve_legacy_pep(event); - else - return on_publish_legacy_pep(event); - end -end, 1); -module:hook("resource-bind", migrate_legacy_bookmarks); -module:handle_items("pep-service", function (event) - local service = event.item.service; - module:hook_object_event(service.events, "node-created", on_node_created); -end, function () end, true);
--- a/mod_bookmarks2/tests/bookmarks2.scs Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -# Pubsub: Bookmarks 2.0 - -[Client] Juliet - jid: admin@localhost - password: password - -// admin@localhost is assumed to have node creation privileges - ---------- - -Juliet connects - --- Generated with https://gitlab.com/xmpp-rs/xmpp-parsers: --- cargo run --example=generate-caps https://code.matthewwild.co.uk/scansion/ <<< "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' name='scansion' type='bot'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='urn:xmpp:bookmarks:1+notify'/></query>" -Juliet sends: - <presence id='presence0'> - <c xmlns='http://jabber.org/protocol/caps' - hash='sha-1' - node='https://code.matthewwild.co.uk/scansion/' - ver='CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - <c xmlns='urn:xmpp:caps'> - <hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>OTy9GPCvBZRvqzOHmD/ThA1WbBH3tNoeKbdqKQCRPHc=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='sha3-256'>f/rxDeTf6HyjQ382V3GEG/UfAs5IeclC05jBSBnVQCI=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='blake2b-256'>ucfqg/NrLj0omE+26hYMrbpcmxHcU4Z3hfAQIF+6tt0=</hash> - </c> - </presence> - -Juliet receives: - <iq from="${Juliet's JID}" id='disco' type='get'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - </iq> - -Juliet sends: - <iq to="${Juliet's JID}" id='disco' type='result'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='> - <identity category='client' name='scansion' type='bot'/> - <feature var='http://jabber.org/protocol/disco#info'/> - <feature var='urn:xmpp:bookmarks:1+notify'/> - </query> - </iq> - -Juliet sends: - <iq type='set' id='pub0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </publish> - <publish-options> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> - <value>http://jabber.org/protocol/pubsub#publish-options</value> - </field> - <field var='pubsub#persist_items'> - <value>true</value> - </field> - <field var='pubsub#max_items'> - <value>255</value> - </field> - <field var='pubsub#send_last_published_item'> - <value>never</value> - </field> - <field var='pubsub#access_model'> - <value>whitelist</value> - </field> - </x> - </publish-options> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit' publisher="${Juliet's JID}"> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='pub0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'/> - </publish> - </pubsub> - </iq> - -Juliet sends: - <iq type='set' id='pub1'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </publish> - <publish-options> - <x xmlns='jabber:x:data' type='submit'> - <field var='FORM_TYPE' type='hidden'> - <value>http://jabber.org/protocol/pubsub#publish-options</value> - </field> - <field var='pubsub#persist_items'> - <value>true</value> - </field> - <field var='pubsub#max_items'> - <value>255</value> - </field> - <field var='pubsub#send_last_published_item'> - <value>never</value> - </field> - <field var='pubsub#access_model'> - <value>whitelist</value> - </field> - </x> - </publish-options> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit' publisher="${Juliet's JID}"> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='pub1'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <publish node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'/> - </publish> - </pubsub> - </iq> - -Juliet sends: - <iq type='set' id='retract0'> - <pubsub xmlns='http://jabber.org/protocol/pubsub'> - <retract node='urn:xmpp:bookmarks:1' notify='1'> - <item id='theplay@conference.shakespeare.lit'/> - </retract> - </pubsub> - </iq> - -Juliet receives: - <message type='headline' from="${Juliet's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <retract id='theplay@conference.shakespeare.lit'/> - </items> - </event> - </message> - -Juliet receives: - <iq type='result' id='retract0'/> - -Juliet disconnects - -// vim: syntax=xml:
--- a/mod_bookmarks2/tests/conversion.scs Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,254 +0,0 @@ -# Pubsub: Bookmarks 2.0 - -[Client] Juliet-old - jid: admin@localhost - password: password - -[Client] Juliet-new - jid: admin@localhost - password: password - -// admin@localhost is assumed to have node creation privileges - ---------- - -Juliet-new connects - --- Generated with https://gitlab.com/xmpp-rs/xmpp-parsers: --- cargo run --example=generate-caps https://code.matthewwild.co.uk/scansion/ <<< "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' name='scansion' type='bot'/><feature var='http://jabber.org/protocol/disco#info'/><feature var='urn:xmpp:bookmarks:1+notify'/></query>" -Juliet-new sends: - <presence id='presence0'> - <c xmlns='http://jabber.org/protocol/caps' - hash='sha-1' - node='https://code.matthewwild.co.uk/scansion/' - ver='CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - <c xmlns='urn:xmpp:caps'> - <hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>OTy9GPCvBZRvqzOHmD/ThA1WbBH3tNoeKbdqKQCRPHc=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='sha3-256'>f/rxDeTf6HyjQ382V3GEG/UfAs5IeclC05jBSBnVQCI=</hash> - <hash xmlns='urn:xmpp:hashes:2' algo='blake2b-256'>ucfqg/NrLj0omE+26hYMrbpcmxHcU4Z3hfAQIF+6tt0=</hash> - </c> - </presence> - -Juliet-new receives: - <iq from="${Juliet-new's JID}" id='disco' type='get'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='/> - </iq> - -Juliet-new sends: - <iq to="${Juliet-new's JID}" id='disco' type='result'> - <query xmlns='http://jabber.org/protocol/disco#info' node='https://code.matthewwild.co.uk/scansion/#CPuQARM1gCTq2f6/ZjHUzWL2QHg='> - <identity category='client' name='scansion' type='bot'/> - <feature var='http://jabber.org/protocol/disco#info'/> - <feature var='urn:xmpp:bookmarks:1+notify'/> - </query> - </iq> - -Juliet-old connects - -Juliet-old sends: - <iq type='get' id='get0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='pub0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='theplay@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Play's the Thing' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='pub0'/> - -Juliet-old sends: - <iq type='get' id='get1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='pub1'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <item id='orchard@conference.shakespeare.lit'> - <conference xmlns='urn:xmpp:bookmarks:1' - name='The Orchard' - autojoin='true'> - <nick>JC</nick> - </conference> - </item> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='pub1'/> - -Juliet-old sends: - <iq type='get' id='get2'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get2'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Play's the Thing' - autojoin='true' - jid='theplay@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='retract0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <items node='urn:xmpp:bookmarks:1'> - <retract id='theplay@conference.shakespeare.lit'/> - </items> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='retract0'/> - -Juliet-old sends: - <iq type='get' id='get3'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get3'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'> - <conference name='The Orchard' - autojoin='true' - jid='orchard@conference.shakespeare.lit'> - <nick>JC</nick> - </conference> - </storage> - </query> - </iq> - -Juliet-old sends: - <iq type='set' id='purge0'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-new receives: - <message type='headline' from="${Juliet-new's JID}"> - <event xmlns='http://jabber.org/protocol/pubsub#event'> - <purge node='urn:xmpp:bookmarks:1'/> - </event> - </message> - -Juliet-old receives: - <iq type='result' id='purge0'/> - -Juliet-old sends: - <iq type='get' id='get4'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old receives: - <iq type='result' id='get4'> - <query xmlns='jabber:iq:private'> - <storage xmlns='storage:bookmarks'/> - </query> - </iq> - -Juliet-old disconnects - -Juliet-new disconnects - -// vim: syntax=xml:
--- a/mod_broadcast/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Broadcast a message to online users -... - -Introduction -============ - -This module largely duplicates the functionality of the standard -mod\_announce that is included with Prosody. It was developed for -compatibility with some clients (e.g. iChat) that do not support ad-hoc -commands or sending to JIDs with the format -'example.com/announce/online'. - -It may also be useful in other specific cases. - -Configuration -============= - - Component "broadcast@example.com" "broadcast" - -By default, only server admins are allowed to post to this address. You -can override this, by specifying the 'broadcast\_senders' option: - - Component "broadcast@example.com" "broadcast" - broadcast_senders = { "user1@example.com", "user2@example.com" } - -Compatibility -============= - - ------ ------- - 0.9 Works - 0.10 Works - trunk Doesn't work (uses is_admin) - ------ -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_broadcast/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +labels: +- 'Stage-Stable' +summary: Broadcast a message to online users +... + +Introduction +============ + +This module largely duplicates the functionality of the standard +mod\_announce that is included with Prosody. It was developed for +compatibility with some clients (e.g. iChat) that do not support ad-hoc +commands or sending to JIDs with the format +'example.com/announce/online'. + +It may also be useful in other specific cases. + +Configuration +============= + + Component "broadcast@example.com" "broadcast" + +By default, only server admins are allowed to post to this address. You +can override this, by specifying the 'broadcast\_senders' option: + + Component "broadcast@example.com" "broadcast" + broadcast_senders = { "user1@example.com", "user2@example.com" } + +Compatibility +============= + + ------ ------- + 0.9 Works + 0.10 Works + trunk Doesn't work (uses is_admin) + ------ -------
--- a/mod_c2s_conn_throttle/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: c2s connections throttling module -... - -Introduction -============ - -This module allows to throttle those client connections which exceed a -n\*seconds limit. - -Usage -===== - -Copy the module folder into your prosody modules directory. Place the -module between your enabled modules either into the global or a vhost -section. - -Optional configuration directives: - -``` {.lua} - -cthrottler_logins_count = 3 -- number of login attempts allowed, default is 3 -cthrottler_time = 60 -- .. in number of seconds, default is 60 -``` - -Info -==== - -- 0.8, works -- 0.9, works
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_c2s_conn_throttle/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +--- +labels: +- 'Stage-Stable' +summary: c2s connections throttling module +... + +Introduction +============ + +This module allows to throttle those client connections which exceed a +n\*seconds limit. + +Usage +===== + +Copy the module folder into your prosody modules directory. Place the +module between your enabled modules either into the global or a vhost +section. + +Optional configuration directives: + +``` {.lua} + +cthrottler_logins_count = 3 -- number of login attempts allowed, default is 3 +cthrottler_time = 60 -- .. in number of seconds, default is 60 +``` + +Info +==== + +- 0.8, works +- 0.9, works
--- a/mod_c2s_limit_sessions/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ---- -labels: -summary: Limit number of resources a user may connect -... - -Introduction -============ - -This module lets you limit number of resources a user may connect. - -Configuration -============= - -After installing and adding the module to `modules_enabled` you can -specify a limit with `max_resources` which defaults to 10.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_c2s_limit_sessions/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,15 @@ +--- +labels: +summary: Limit number of resources a user may connect +... + +Introduction +============ + +This module lets you limit number of resources a user may connect. + +Configuration +============= + +After installing and adding the module to `modules_enabled` you can +specify a limit with `max_resources` which defaults to 10.
--- a/mod_cache_c2s_caps/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -summary: Cache caps on user sessions ---- - -Description -=========== - -This module listens on presences containing caps (XEP-0115) and asks the client -for the corresponding disco#info if it changed. - -It fires the c2s-capabilities-changed event once the disco#info result is -received. - -Compatibility -============= - - ------- --------------- - trunk Works - 0.11 Does not work - 0.10 Does not work - ------- ---------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cache_c2s_caps/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,21 @@ +--- +summary: Cache caps on user sessions +--- + +Description +=========== + +This module listens on presences containing caps (XEP-0115) and asks the client +for the corresponding disco#info if it changed. + +It fires the c2s-capabilities-changed event once the disco#info result is +received. + +Compatibility +============= + + ------- --------------- + trunk Works + 0.11 Does not work + 0.10 Does not work + ------- ---------------
--- a/mod_captcha_registration/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: provides captcha protection for registration form -... - -Introduction -============ - -Prosody-captcha is a little modification of prosody's -"mod\_register.lua" module that provides captcha protection for -registration form. - -Installation -============ - -First of all you should build and install lua bindings for libgd — -[lua-gd](https://github.com/ittner/lua-gd/). - -Then clone repsository lua-captcha: - - $ git clone https://github.com/mrDoctorWho/lua-captcha - -install it: - - $ make install - -Configuration -============= - -After that you would configure prosody. This module requires from you 4 -fields, you should add this into your VirtualHost entry. - - captcha_config = { - dir = "/tmp"; -- Directory used to storage captcha images. Please make sure prosody user allowed to write there. - timeout = 60; -- Timeout when captcha will expire - web_path = "challenge"; -- Web path used to separate main prosody site from itself modules. - font = "/usr/lib/prosody/FiraSans-Regular.ttf" -- Font used for captcha text - } - -You can run script "install.lua" to install this or instead of that -while prosody developers didn't accepted "dataforms" changes you should -replace standard prosody "dataforms.lua" located in ubuntu in -/usr/lib/prosody/util by another one from this repository. You should do -the same thing with "mod\_register.lua" located in ubuntu in -/usr/lib/prosody/modules. - -After this all you can try to register on your server and see the -captcha. - -TODO -==== - -- Maybe use recaptcha instead of libgd.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_captcha_registration/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,54 @@ +--- +labels: +- 'Stage-Beta' +summary: provides captcha protection for registration form +... + +Introduction +============ + +Prosody-captcha is a little modification of prosody's +"mod\_register.lua" module that provides captcha protection for +registration form. + +Installation +============ + +First of all you should build and install lua bindings for libgd — +[lua-gd](https://github.com/ittner/lua-gd/). + +Then clone repsository lua-captcha: + + $ git clone https://github.com/mrDoctorWho/lua-captcha + +install it: + + $ make install + +Configuration +============= + +After that you would configure prosody. This module requires from you 4 +fields, you should add this into your VirtualHost entry. + + captcha_config = { + dir = "/tmp"; -- Directory used to storage captcha images. Please make sure prosody user allowed to write there. + timeout = 60; -- Timeout when captcha will expire + web_path = "challenge"; -- Web path used to separate main prosody site from itself modules. + font = "/usr/lib/prosody/FiraSans-Regular.ttf" -- Font used for captcha text + } + +You can run script "install.lua" to install this or instead of that +while prosody developers didn't accepted "dataforms" changes you should +replace standard prosody "dataforms.lua" located in ubuntu in +/usr/lib/prosody/util by another one from this repository. You should do +the same thing with "mod\_register.lua" located in ubuntu in +/usr/lib/prosody/modules. + +After this all you can try to register on your server and see the +captcha. + +TODO +==== + +- Maybe use recaptcha instead of libgd.
--- a/mod_carbons/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: Message Carbons -superseded_by: mod_carbons -... - -Since Prosody 0.10, this module is [included in Prosody](https://prosody.im/doc/modules/mod_carbons), you will be redirected there shortly.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_carbons/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,8 @@ +--- +labels: +- 'Stage-Obsolete' +summary: Message Carbons +superseded_by: mod_carbons +... + +Since Prosody 0.10, this module is [included in Prosody](https://prosody.im/doc/modules/mod_carbons), you will be redirected there shortly.
--- a/mod_checkcerts/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -labels: -summary: Certificate expiry reminder -... - -Introduction -============ - -This module periodically checks your certificate to see if it is about -to expire soon. The time before expiry is printed in the logs. About a -week before a certificate expires, reminder messages will be sent to -admins. - -Configuration -============= - -Simply add the module to the `modules_enabled` list. You can optionally -configure how long before expiry to start sending messages to admins. - - modules_enabled = { - ... - "checkcerts" - } - checkcerts_notify = 7 -- ( in days ) - -Compatibility -============= - -Needs LuaSec 0.5+ - -Originally written for Prosody 0.9.x, apparently incompatible with -0.10.x.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_checkcerts/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,37 @@ +--- +labels: +- Stage-Broken +summary: Certificate expiry reminder +... + +::: {.alert .alert-info} +This module is incompatible with prosody since version 0.10. +::: + +Introduction +============ + +This module periodically checks your certificate to see if it is about +to expire soon. The time before expiry is printed in the logs. About a +week before a certificate expires, reminder messages will be sent to +admins. + +Configuration +============= + +Simply add the module to the `modules_enabled` list. You can optionally +configure how long before expiry to start sending messages to admins. + + modules_enabled = { + ... + "checkcerts" + } + checkcerts_notify = 7 -- ( in days ) + +Compatibility +============= + +Needs LuaSec 0.5+ + +Originally written for Prosody 0.9.x, apparently incompatible with +0.10 or greater
--- a/mod_client_certs/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Client-side certificate management for Prosody' -... - -Introduction -============ - -[XEP-0257](http://xmpp.org/extensions/xep-0257.html) specifies a -protocol for clients to store and manage client side certificates. When -a client presents a stored client side certificate during the TLS -handshake, it can log in without supplying a password (using SASL -EXTERNAL). This makes it possible to have multiple devices accessing an -account, without any of them needing to know the password, and makes it -easier to revoke access for a single device. - -Details -======= - -Each user can add their own certificates. These do not need to be signed -by a trusted CA, yet they do need to be valid at the time of logging in -and they should include an subjectAltName with otherName -"id-on-xmppAddr" with the JID of the user. - -Generating your certificate ---------------------------- - -1. To generate your own certificate with a "id-on-xmppAddr" attribute - using the command line `openssl` tool, first create a file called - `client.cnf` with contents: - - [req] prompt = no - x509_extensions = v3_extensions - req_extensions = v3_extensions - distinguished_name = distinguished_name - - [v3_extensions] - extendedKeyUsage = clientAuth - keyUsage = digitalSignature,keyEncipherment - basicConstraints = CA:FALSE - subjectAltName = @subject_alternative_name - - [subject_alternative_name] - otherName.0 = - 1.3.6.1.5.5.7.8.5;FORMAT:UTF8,UTF8:hamlet@shakespeare.lit - - [distinguished_name] - commonName = Your Name - emailAddress = hamlet@shakespeare.lit - -2. Replace the values for `otherName.0` and `commonName` and - `emailAddress` with your own values. The JID in `otherName.0` can - either be a full JID or a bare JID, in the former case, the client - can only use the resource specified in the resource. There are many - other fields you can add, however, for SASL EXTERNAL, they will have - no meaning. You can add more JIDs as `otherName.1`, `otherName.2`, - etc. -3. Create a private key (as an example, a 4096 bits RSA key): - - openssl genrsa -out client.key 4096 - -4. Create the certificate request: - - openssl req -key client.key -new -out client.req -config client.cnf -extensions v3_extensions - -5. Sign it yourself: - - openssl x509 -req -days 365 -in client.req -signkey client.key -out client.crt -extfile client.cnf -extensions v3_extensions - -The 365 means the certificate will be valid for a year starting now. - -The `client.key` **must** be kept secret, and is only needed by clients -connecting using this certificate. The `client.crt` file contains the -certificate that should be sent to the server using XEP-0257, and is -also needed by clients connecting to the server. The `client.req` file -is not needed anymore. - -Configuration -============= - -(None yet) - -Compatibility -============= - - ----- ----------------------------- - 0.9 Works - 0.8 Untested. Probably doesn't. - ----- ----------------------------- - -Clients -======= - -(None?) - -TODO -==== - -Possible options to add to the configuration: - -- Require certificates to be signed by a trusted CA. -- Do not require a id-on-xmppAddr -- Remove expired certs after a certain time -- Limit the number of certificates per user
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_client_certs/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,105 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Client-side certificate management for Prosody' +... + +Introduction +============ + +[XEP-0257](http://xmpp.org/extensions/xep-0257.html) specifies a +protocol for clients to store and manage client side certificates. When +a client presents a stored client side certificate during the TLS +handshake, it can log in without supplying a password (using SASL +EXTERNAL). This makes it possible to have multiple devices accessing an +account, without any of them needing to know the password, and makes it +easier to revoke access for a single device. + +Details +======= + +Each user can add their own certificates. These do not need to be signed +by a trusted CA, yet they do need to be valid at the time of logging in +and they should include an subjectAltName with otherName +"id-on-xmppAddr" with the JID of the user. + +Generating your certificate +--------------------------- + +1. To generate your own certificate with a "id-on-xmppAddr" attribute + using the command line `openssl` tool, first create a file called + `client.cnf` with contents: + + [req] prompt = no + x509_extensions = v3_extensions + req_extensions = v3_extensions + distinguished_name = distinguished_name + + [v3_extensions] + extendedKeyUsage = clientAuth + keyUsage = digitalSignature,keyEncipherment + basicConstraints = CA:FALSE + subjectAltName = @subject_alternative_name + + [subject_alternative_name] + otherName.0 = + 1.3.6.1.5.5.7.8.5;FORMAT:UTF8,UTF8:hamlet@shakespeare.lit + + [distinguished_name] + commonName = Your Name + emailAddress = hamlet@shakespeare.lit + +2. Replace the values for `otherName.0` and `commonName` and + `emailAddress` with your own values. The JID in `otherName.0` can + either be a full JID or a bare JID, in the former case, the client + can only use the resource specified in the resource. There are many + other fields you can add, however, for SASL EXTERNAL, they will have + no meaning. You can add more JIDs as `otherName.1`, `otherName.2`, + etc. +3. Create a private key (as an example, a 4096 bits RSA key): + + openssl genrsa -out client.key 4096 + +4. Create the certificate request: + + openssl req -key client.key -new -out client.req -config client.cnf -extensions v3_extensions + +5. Sign it yourself: + + openssl x509 -req -days 365 -in client.req -signkey client.key -out client.crt -extfile client.cnf -extensions v3_extensions + +The 365 means the certificate will be valid for a year starting now. + +The `client.key` **must** be kept secret, and is only needed by clients +connecting using this certificate. The `client.crt` file contains the +certificate that should be sent to the server using XEP-0257, and is +also needed by clients connecting to the server. The `client.req` file +is not needed anymore. + +Configuration +============= + +(None yet) + +Compatibility +============= + + ----- ----------------------------- + 0.9 Works + 0.8 Untested. Probably doesn't. + ----- ----------------------------- + +Clients +======= + +(None?) + +TODO +==== + +Possible options to add to the configuration: + +- Require certificates to be signed by a trusted CA. +- Do not require a id-on-xmppAddr +- Remove expired certs after a certain time +- Limit the number of certificates per user
--- a/mod_client_proxy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Proxy multiple client resources behind a single component' -... - -What it does -============ - -This module must be used as a component. For example: - - Component "proxy.domain.example" "client_proxy" - target_address = "some-user@some-domain.example" - -All IQ requests against the proxy host (in the above example: -proxy.domain.example) are sent to a random resource of the target address (in -the above example: some-user@some-domain.example). The entity behind the -target address is called the "implementing client". - -The IQ requests are JAT-ed (JAT: Jabber Address Translation) so that when the -implementing client answers the IQ request, it is sent back to the component, -which reverts the translation and routes the reply back to the user. - -Let us assume that user@some-domain.example sends a request. The -proxy.domain.example component has the client_proxy module loaded and proxies to -some-user@some-domain.example. some-user@some-domain.example has two resources, -/a and /b. - - user -> component: - <iq type='get' id='1234' to='proxy.domain.example' from='user@some-domain.example/abc'> - component -> implementing client: - <iq type='get' id='1234' to='some-user@some-domain.example/a' from='proxy.domain.example/encoded-from'> - implementing client -> component: - <iq type='result' id='1234' to='proxy.domain.example/encoded-from' from='some-user@some-domain.example/a'> - component -> user: - <iq type='result' id='1234' to='user@some-domain.example/abc' from='proxy.domain.example'> - -The encoded-from resource used in the exchange between the proxy component -and the implementing client is an implementation-defined string which allows -the proxy component to revert the JAT. - - -Use cases -========= - -* Implementation of services within clients instead of components, thus making - use of the more advanced authentication features. -* Load-balancing requests to different client resources. -* General evilness - - -Configuration -============= - -To use this module, it needs to be loaded on a component: - - Component "proxy.yourdomain.example" "client_proxy" - target_address = "implementation@yourdomain.example" - -It will then send a subscription request to implementation@yourdomain.example -which MUST be accepted: this is required so that the component can detect the -resources to which IQ requests can be dispatched. - - -Limitations -=========== - -* It does not handle presence or message stanzas. -* It does not allow the implementing client to initiate IQ requests
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_client_proxy/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,69 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Proxy multiple client resources behind a single component' +... + +What it does +============ + +This module must be used as a component. For example: + + Component "proxy.domain.example" "client_proxy" + target_address = "some-user@some-domain.example" + +All IQ requests against the proxy host (in the above example: +proxy.domain.example) are sent to a random resource of the target address (in +the above example: some-user@some-domain.example). The entity behind the +target address is called the "implementing client". + +The IQ requests are JAT-ed (JAT: Jabber Address Translation) so that when the +implementing client answers the IQ request, it is sent back to the component, +which reverts the translation and routes the reply back to the user. + +Let us assume that user@some-domain.example sends a request. The +proxy.domain.example component has the client_proxy module loaded and proxies to +some-user@some-domain.example. some-user@some-domain.example has two resources, +/a and /b. + + user -> component: + <iq type='get' id='1234' to='proxy.domain.example' from='user@some-domain.example/abc'> + component -> implementing client: + <iq type='get' id='1234' to='some-user@some-domain.example/a' from='proxy.domain.example/encoded-from'> + implementing client -> component: + <iq type='result' id='1234' to='proxy.domain.example/encoded-from' from='some-user@some-domain.example/a'> + component -> user: + <iq type='result' id='1234' to='user@some-domain.example/abc' from='proxy.domain.example'> + +The encoded-from resource used in the exchange between the proxy component +and the implementing client is an implementation-defined string which allows +the proxy component to revert the JAT. + + +Use cases +========= + +* Implementation of services within clients instead of components, thus making + use of the more advanced authentication features. +* Load-balancing requests to different client resources. +* General evilness + + +Configuration +============= + +To use this module, it needs to be loaded on a component: + + Component "proxy.yourdomain.example" "client_proxy" + target_address = "implementation@yourdomain.example" + +It will then send a subscription request to implementation@yourdomain.example +which MUST be accepted: this is required so that the component can detect the +resources to which IQ requests can be dispatched. + + +Limitations +=========== + +* It does not handle presence or message stanzas. +* It does not allow the implementing client to initiate IQ requests
--- a/mod_cloud_notify/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'XEP-0357: Cloud push notifications' ---- - -Introduction -============ - -This module enables support for sending "push notifications" to clients that -need it, typically those running on certain mobile devices. - -As well as this module, your client must support push notifications (the apps -that need it generally do, of course) and the app developer's push gateway -must be reachable from your Prosody server (this happens over a normal XMPP -server-to-server 's2s' connection). - -Details -======= - -Some platforms, notably Apple's iOS and many versions of Android, impose -limits that prevent applications from running or accessing the network in the -background. This makes it difficult or impossible for an XMPP application to -remain reliably connected to a server to receive messages. - -In order for messaging and other apps to receive notifications, the OS vendors -run proprietary servers that their OS maintains a permanent connection to in -the background. Then they provide APIs to application developers that allow -sending notifications to specific devices via those servers. - -When you connect to your server with an app that requires push notifications, -it will use this module to set up a "push registration". When you receive -a message but your device is not connected to the server, this module will -generate a notification and send it to the push gateway operated by your -application's developers). Their gateway will then connect to your device's -OS vendor and ask them to forward the notification to your device. When your -device receives the notification, it will display it or wake up the app so it -can connect to XMPP and receive any pending messages. - -This protocol is described for developers in [XEP-0357: Push Notifications]. - -For this module to work reliably, you must have [mod_smacks], [mod_mam] and -[mod_carbons] also enabled on your server. - -Some clients, notably Siskin and Snikket iOS need some additional extensions -that are not currently defined in a standard XEP. To support these clients, -see [mod_cloud_notify_extensions]. - -Configuration -============= - - Option Default Description - ------------------------------------ ----------------- ------------------------------------------------------------------------------------------------------------------- - `push_notification_important_body` `New Message!` The body text to use when the stanza is important (see above), no message body is sent if this is empty - `push_max_errors` `16` How much persistent push errors are tolerated before notifications for the identifier in question are disabled - `push_max_devices` `5` The number of allowed devices per user (the oldest devices are automatically removed if this threshold is reached) - `push_max_hibernation_timeout` `259200` (72h) Number of seconds to extend the smacks timeout if no push was triggered yet (default: 72 hours) - `push_notification_with_body` (\*) `false` Whether or not to send the real message body to remote pubsub node. Without end-to-end encryption, enabling this may expose your message contents to your client developers and OS vendor. Not recommended. - `push_notification_with_sender` (\*) `false` Whether or not to send the real message sender to remote pubsub node. Enabling this may expose your contacts to your client developers and OS vendor. Not recommended. - -(\*) There are privacy implications for enabling these options. - -Internal design notes -===================== - -App servers are notified about offline messages, messages stored by [mod_mam] -or messages waiting in the smacks queue. -The business rules outlined [here](//mail.jabber.org/pipermail/standards/2016-February/030925.html) are all honored[^2]. - -To cooperate with [mod_smacks] this module consumes some events: -`smacks-ack-delayed`, `smacks-hibernation-start` and `smacks-hibernation-end`. -These events allow this module to send out notifications for messages received -while the session is hibernated by [mod_smacks] or even when smacks -acknowledgements for messages are delayed by a certain amount of seconds -configurable with the [mod_smacks] setting `smacks_max_ack_delay`. - -The `smacks_max_ack_delay` setting allows to send out notifications to clients -which aren't already in smacks hibernation state (because the read timeout or -connection close didn't already happen) but also aren't responding to acknowledgement -request in a timely manner. This setting thus allows conversations to be smoother -under such circumstances. - -The new event `cloud-notify-ping` can be used by any module to send out a cloud -notification to either all registered endpoints for the given user or only the endpoints -given in the event data. - -The config setting `push_notification_important_body` can be used to specify an alternative -body text to send to the remote pubsub node if the stanza is encrypted or has a body. -This way the real contents of the message aren't revealed to the push appserver but it -can still see that the push is important. -This is used by Chatsecure on iOS to send out high priority pushes in those cases for example. - -Compatibility -============= - -**Note:** This module should be used with Lua 5.2 and higher. Using it with -Lua 5.1 may cause push notifications to not be sent to some clients. - ------- ----------------------------------------------------------------------------- - trunk Works - 0.12 Works - 0.11 Works - 0.10 Works - 0.9 Support dropped, use last supported version [675726ab06d3](//hg.prosody.im/prosody-modules/raw-file/675726ab06d3/mod_cloud_notify/mod_cloud_notify.lua) ------- ----------------------------------------------------------------------------- - - -[^1]: The service which is expected to forward notifications to something like Google Cloud Messaging or Apple Notification Service -[^2]: [business_rules.markdown](//hg.prosody.im/prosody-modules/file/tip/mod_cloud_notify/business_rules.markdown)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cloud_notify/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,109 @@ +--- +labels: +- 'Stage-Beta' +summary: 'XEP-0357: Cloud push notifications' +--- + +Introduction +============ + +This module enables support for sending "push notifications" to clients that +need it, typically those running on certain mobile devices. + +As well as this module, your client must support push notifications (the apps +that need it generally do, of course) and the app developer's push gateway +must be reachable from your Prosody server (this happens over a normal XMPP +server-to-server 's2s' connection). + +Details +======= + +Some platforms, notably Apple's iOS and many versions of Android, impose +limits that prevent applications from running or accessing the network in the +background. This makes it difficult or impossible for an XMPP application to +remain reliably connected to a server to receive messages. + +In order for messaging and other apps to receive notifications, the OS vendors +run proprietary servers that their OS maintains a permanent connection to in +the background. Then they provide APIs to application developers that allow +sending notifications to specific devices via those servers. + +When you connect to your server with an app that requires push notifications, +it will use this module to set up a "push registration". When you receive +a message but your device is not connected to the server, this module will +generate a notification and send it to the push gateway operated by your +application's developers). Their gateway will then connect to your device's +OS vendor and ask them to forward the notification to your device. When your +device receives the notification, it will display it or wake up the app so it +can connect to XMPP and receive any pending messages. + +This protocol is described for developers in [XEP-0357: Push Notifications]. + +For this module to work reliably, you must have [mod_smacks], [mod_mam] and +[mod_carbons] also enabled on your server. + +Some clients, notably Siskin and Snikket iOS need some additional extensions +that are not currently defined in a standard XEP. To support these clients, +see [mod_cloud_notify_extensions]. + +Configuration +============= + + Option Default Description + ------------------------------------ ----------------- ------------------------------------------------------------------------------------------------------------------- + `push_notification_important_body` `New Message!` The body text to use when the stanza is important (see above), no message body is sent if this is empty + `push_max_errors` `16` How much persistent push errors are tolerated before notifications for the identifier in question are disabled + `push_max_devices` `5` The number of allowed devices per user (the oldest devices are automatically removed if this threshold is reached) + `push_max_hibernation_timeout` `259200` (72h) Number of seconds to extend the smacks timeout if no push was triggered yet (default: 72 hours) + `push_notification_with_body` (\*) `false` Whether or not to send the real message body to remote pubsub node. Without end-to-end encryption, enabling this may expose your message contents to your client developers and OS vendor. Not recommended. + `push_notification_with_sender` (\*) `false` Whether or not to send the real message sender to remote pubsub node. Enabling this may expose your contacts to your client developers and OS vendor. Not recommended. + +(\*) There are privacy implications for enabling these options. + +Internal design notes +===================== + +App servers are notified about offline messages, messages stored by [mod_mam] +or messages waiting in the smacks queue. +The business rules outlined [here](//mail.jabber.org/pipermail/standards/2016-February/030925.html) are all honored[^2]. + +To cooperate with [mod_smacks] this module consumes some events: +`smacks-ack-delayed`, `smacks-hibernation-start` and `smacks-hibernation-end`. +These events allow this module to send out notifications for messages received +while the session is hibernated by [mod_smacks] or even when smacks +acknowledgements for messages are delayed by a certain amount of seconds +configurable with the [mod_smacks] setting `smacks_max_ack_delay`. + +The `smacks_max_ack_delay` setting allows to send out notifications to clients +which aren't already in smacks hibernation state (because the read timeout or +connection close didn't already happen) but also aren't responding to acknowledgement +request in a timely manner. This setting thus allows conversations to be smoother +under such circumstances. + +The new event `cloud-notify-ping` can be used by any module to send out a cloud +notification to either all registered endpoints for the given user or only the endpoints +given in the event data. + +The config setting `push_notification_important_body` can be used to specify an alternative +body text to send to the remote pubsub node if the stanza is encrypted or has a body. +This way the real contents of the message aren't revealed to the push appserver but it +can still see that the push is important. +This is used by Chatsecure on iOS to send out high priority pushes in those cases for example. + +Compatibility +============= + +**Note:** This module should be used with Lua 5.2 and higher. Using it with +Lua 5.1 may cause push notifications to not be sent to some clients. + +------ ----------------------------------------------------------------------------- + trunk Works + 0.12 Works + 0.11 Works + 0.10 Works + 0.9 Support dropped, use last supported version [675726ab06d3](//hg.prosody.im/prosody-modules/raw-file/675726ab06d3/mod_cloud_notify/mod_cloud_notify.lua) +------ ----------------------------------------------------------------------------- + + +[^1]: The service which is expected to forward notifications to something like Google Cloud Messaging or Apple Notification Service +[^2]: [business_rules.markdown](//hg.prosody.im/prosody-modules/file/tip/mod_cloud_notify/business_rules.markdown)
--- a/mod_cloud_notify/business_rules.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -XEP-0357 Business rules implementation in prosody -================================================= - -Daniel proposed some business rules for push notifications [^1] -This document describes the various implementation details involved in -implementing these rules in prosody. - -Point 3 of Daniel's mail is implemented by setting two attributes -on the session table when a client enables push for a session: - -- push_identifier: this is push_jid .. "<" .. (push_node or "") - (this value is used as key of the user_push_services table) -- push_settings: this is a reference to the user_push_services[push_identifier] - - -Point 4 of Daniel's mail contains the actual business rules ------------------------------------------------------------ - -**a)** -CSI is honoured in this scenario because messages hold back by csi don't even -reach the smacks module. mod_smacks has 3 events: - -- smacks-ack-delayed: This event is triggered when the client doesn't respond to - a smacks <r> in a configurable amount of time (default: 60 seconds). - Mod_cloud_notify reacts on this event and sends out push notifications - to the push service registered for this session in point 3 (see above) for all - stanzas in the smacks queue (the queue is given in the event). - -- smacks-hibernation-start: This event is triggered when the smacks session - is put in hibernation state. The event contains the smacks queue, too. - Mod_cloud_notify uses this event to send push notifications for all - stanzas not already pushed and installs a "stanzas/out"-filter to - react on new stanzas coming in while the session is hibernated. - The push endpoint of the hibernated session is then also notified - for every new stanza. -- smacks-hibernation-end: This event is triggered, when the smacks hibernation - is stopped (the smacks session is resumed) and used by Mod_cloud_notify - to remove the "stanzas/out"-filter. - -**b)** -Mod_mam already provides an event named "archive-message-added" which is -triggered every time a stanza is saved into the mam store. -Mod_cloud_notify uses this event to send out push notifications to all -push services registered for the user the stanza is for, but *only* -to those push services not having an active (or smacks hibernated) session. -Only those stanzas are considered that contain the "for_user" event attribute -of mod_mam as the user part of the jid. -This is done to ensure that mam archiving rules are honoured. - -**c)** -The "message/offline/handle"-hook is used to send out push notifications to all -registered push services belonging to the user the offline stanza is for. -This was already implemented in the first version of mod_cloud_notify. - - -Some statements to related technologies/XEPs/modules ----------------------------------------------------- - -- carbons: These are handled as usual and don't interfere with these business rules - at all. Smacks events are generated for carbon copies if needed and mod_cloud_notify - uses them to wake up the device in question if needed, as normal stanzas would do, too. - -- csi: Csi is honoured also, because every stanza hold back by mod_pump or other csi - modules is never seen by the smacks module, thus not added to its queue and not - forwarded to mod_cloud_notify by the smacks events. - Mod_cloud_notify does only notify devices having no active or smacks hibernated session - of new mam stored stanzas, so stanzas filtered by csi don't get to mod_cloud_notify - this way neither. - -- other technologies: There shouldn't be any issues with other technologies imho. - -[^1]: https://mail.jabber.org/pipermail/standards/2016-February/030925.html
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cloud_notify/business_rules.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,72 @@ +XEP-0357 Business rules implementation in prosody +================================================= + +Daniel proposed some business rules for push notifications [^1] +This document describes the various implementation details involved in +implementing these rules in prosody. + +Point 3 of Daniel's mail is implemented by setting two attributes +on the session table when a client enables push for a session: + +- push_identifier: this is push_jid .. "<" .. (push_node or "") + (this value is used as key of the user_push_services table) +- push_settings: this is a reference to the user_push_services[push_identifier] + + +Point 4 of Daniel's mail contains the actual business rules +----------------------------------------------------------- + +**a)** +CSI is honoured in this scenario because messages hold back by csi don't even +reach the smacks module. mod_smacks has 3 events: + +- smacks-ack-delayed: This event is triggered when the client doesn't respond to + a smacks <r> in a configurable amount of time (default: 60 seconds). + Mod_cloud_notify reacts on this event and sends out push notifications + to the push service registered for this session in point 3 (see above) for all + stanzas in the smacks queue (the queue is given in the event). + +- smacks-hibernation-start: This event is triggered when the smacks session + is put in hibernation state. The event contains the smacks queue, too. + Mod_cloud_notify uses this event to send push notifications for all + stanzas not already pushed and installs a "stanzas/out"-filter to + react on new stanzas coming in while the session is hibernated. + The push endpoint of the hibernated session is then also notified + for every new stanza. +- smacks-hibernation-end: This event is triggered, when the smacks hibernation + is stopped (the smacks session is resumed) and used by Mod_cloud_notify + to remove the "stanzas/out"-filter. + +**b)** +Mod_mam already provides an event named "archive-message-added" which is +triggered every time a stanza is saved into the mam store. +Mod_cloud_notify uses this event to send out push notifications to all +push services registered for the user the stanza is for, but *only* +to those push services not having an active (or smacks hibernated) session. +Only those stanzas are considered that contain the "for_user" event attribute +of mod_mam as the user part of the jid. +This is done to ensure that mam archiving rules are honoured. + +**c)** +The "message/offline/handle"-hook is used to send out push notifications to all +registered push services belonging to the user the offline stanza is for. +This was already implemented in the first version of mod_cloud_notify. + + +Some statements to related technologies/XEPs/modules +---------------------------------------------------- + +- carbons: These are handled as usual and don't interfere with these business rules + at all. Smacks events are generated for carbon copies if needed and mod_cloud_notify + uses them to wake up the device in question if needed, as normal stanzas would do, too. + +- csi: Csi is honoured also, because every stanza hold back by mod_pump or other csi + modules is never seen by the smacks module, thus not added to its queue and not + forwarded to mod_cloud_notify by the smacks events. + Mod_cloud_notify does only notify devices having no active or smacks hibernated session + of new mam stored stanzas, so stanzas filtered by csi don't get to mod_cloud_notify + this way neither. + +- other technologies: There shouldn't be any issues with other technologies imho. + +[^1]: https://mail.jabber.org/pipermail/standards/2016-February/030925.html
--- a/mod_cloud_notify_extensions/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ ---- -summary: "Tigase custom push extensions for iOS" -labels: -- 'Stage-Beta' -rockspec: - dependencies: - - mod_cloud_notify_encrypted - - mod_cloud_notify_priority_tag - - mod_cloud_notify_filters -... - -Introduction -============ - -This is a meta-module that simply enables all the modules required to support -Siskin or Snikket iOS on a Prosody server. - -These are currently: - -- mod_cloud_notify_encrypted -- mod_cloud_notify_priority_tag -- mod_cloud_notify_filters - -See the individual module pages for more details. In particular, -mod_cloud_notify_encrypted depends on -[luaossl](http://25thandclement.com/~william/projects/luaossl.html), which -must be installed. It is available in Debian via apt as -[`lua-luaossl`](https://tracker.debian.org/pkg/lua-luaossl) or via -`luarocks install luaossl`. - -Note: On MUC services you should also load mod_muc_offline_delivery directly -under the MUC component in your config file, that is not handled by this -module. - -Configuration -============= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -# Compatibility - - ------- ------------- - 0.12 Works - 0.11 Should work - trunk Works - ------- -------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_cloud_notify_extensions/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,47 @@ +--- +summary: "Tigase custom push extensions for iOS" +labels: +- 'Stage-Beta' +rockspec: + dependencies: + - mod_cloud_notify_encrypted + - mod_cloud_notify_priority_tag + - mod_cloud_notify_filters +... + +Introduction +============ + +This is a meta-module that simply enables all the modules required to support +Siskin or Snikket iOS on a Prosody server. + +These are currently: + +- [mod_cloud_notify_encrypted] +- [mod_cloud_notify_priority_tag] +- [mod_cloud_notify_filters] + +See the individual module pages for more details. In particular, +mod_cloud_notify_encrypted depends on +[luaossl](http://25thandclement.com/~william/projects/luaossl.html), which +must be installed. It is available in Debian via apt as +[`lua-luaossl`](https://tracker.debian.org/pkg/lua-luaossl) or via +`luarocks install luaossl`. + +Note: On MUC services you should also load mod_muc_offline_delivery directly +under the MUC component in your config file, that is not handled by this +module. + +Configuration +============= + +There is no configuration for this module, just add it to +modules\_enabled as normal. + +# Compatibility + + ------- ------------- + 0.12 Works + 0.11 Should work + trunk Works + ------- -------------
--- a/mod_compat_dialback/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -summary: Workaround for Dialback with some servers that violate RFC 6120 -... - -This module provides a workaround for servers that do not set the `to` -attribute on stream headers, which is required per [RFC6120]: - -> ## 4.7.2. to -> -> For initial stream headers in both client-to-server and -> server-to-server communication, the initiating entity MUST include the -> 'to' attribute and MUST set its value to a domainpart that the -> initiating entity knows or expects the receiving entity to service. - -As a side effect of [this issue](https://prosody.im/issues/issue/285), -Prosody 0.10 will be unable to do [Dialback][xep220] with servers that -don't follow this. - -# Known servers affected - -* Openfire 3.10.2 (and probably earlier versions)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compat_dialback/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,21 @@ +--- +summary: Workaround for Dialback with some servers that violate RFC 6120 +... + +This module provides a workaround for servers that do not set the `to` +attribute on stream headers, which is required per [RFC6120]: + +> ## 4.7.2. to +> +> For initial stream headers in both client-to-server and +> server-to-server communication, the initiating entity MUST include the +> 'to' attribute and MUST set its value to a domainpart that the +> initiating entity knows or expects the receiving entity to service. + +As a side effect of [this issue](https://prosody.im/issues/issue/285), +Prosody 0.10 will be unable to do [Dialback][xep220] with servers that +don't follow this. + +# Known servers affected + +* Openfire 3.10.2 (and probably earlier versions)
--- a/mod_compat_muc_admin/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: | - COMPAT Module for old clients using wrong namespaces in MUC's - affiliation manipulations. -... - -Details -======= - -Adds compatibility for old clients/libraries attempting to change -affiliations and retrieve 'em sending the \< -http://jabber.org/protocol/muc\#owner \> xmlns instead of \< -http://jabber.org/protocol/muc\#admin \>. - -Usage -===== - -Copy the plugin into your prosody's modules directory. - -And add it between the enabled muc component's modules - -Compatibility -============= - -- 0.7-0.8.x
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compat_muc_admin/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,27 @@ +--- +labels: +- 'Stage-Beta' +summary: | + COMPAT Module for old clients using wrong namespaces in MUC's + affiliation manipulations. +... + +Details +======= + +Adds compatibility for old clients/libraries attempting to change +affiliations and retrieve 'em sending the \< +http://jabber.org/protocol/muc\#owner \> xmlns instead of \< +http://jabber.org/protocol/muc\#admin \>. + +Usage +===== + +Copy the plugin into your prosody's modules directory. + +And add it between the enabled muc component's modules + +Compatibility +============= + +- 0.7-0.8.x
--- a/mod_compat_roles/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: Compatibility layer for Prosody's future roles API ---- - -Introduction -============ - -This module provides compatibility with Prosody's new role and permissions -system. It aims to run on Prosody 0.11 and 0.12, providing a limited version -of the new API backed by is_admin() (which is not going to be present in trunk -and future Prosody versions). - -It is designed for use by modules which want to be compatible with Prosody -versions with and without the new permissions API. - -Configuration -============= - -There is no configuration. - -Usage (for developers) -====================== - -If you are a module developer, and want your module to work with Prosody trunk -and future releases, you should avoid the `usermanager.is_admin()` function. - -Instead, depend on this module: - -``` -module:depends("compat_roles") -``` - -Then use `module:may()` instead: - -``` -if module:may(":do-something") then - -- Blah -end -``` - -For more information on the new role/permissions API, check Prosody's -developer documentation at https://prosody.im/doc/developers/permissions - -Compatibility -============= - -Requires Prosody 0.11 or 0.12.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compat_roles/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,49 @@ +--- +labels: +- Stage-Alpha +summary: Compatibility layer for Prosody's future roles API +--- + +Introduction +============ + +This module provides compatibility with Prosody's new role and permissions +system. It aims to run on Prosody 0.11 and 0.12, providing a limited version +of the new API backed by is_admin() (which is not going to be present in trunk +and future Prosody versions). + +It is designed for use by modules which want to be compatible with Prosody +versions with and without the new permissions API. + +Configuration +============= + +There is no configuration. + +Usage (for developers) +====================== + +If you are a module developer, and want your module to work with Prosody trunk +and future releases, you should avoid the `usermanager.is_admin()` function. + +Instead, depend on this module: + +``` +module:depends("compat_roles") +``` + +Then use `module:may()` instead: + +``` +if module:may(":do-something") then + -- Blah +end +``` + +For more information on the new role/permissions API, check Prosody's +developer documentation at https://prosody.im/doc/developers/permissions + +Compatibility +============= + +Requires Prosody 0.11 or 0.12.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compliance_2023/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,22 @@ +--- +summary: XMPP Compliance Suites 2023 self-test +labels: +- Stage-Beta +rockspec: + dependencies: + - mod_cloud_notify + +... + +Compare the list of enabled modules with +[XEP-0479: XMPP Compliance Suites 2023] and produce basic report to the +Prosody log file. + +If installed with the Prosody plugin installer then all modules needed for a green checkmark should be included. (With prosody 0.12 only [mod_cloud_notify] is not included with prosody and we need the community module) + +# Compatibility + + Prosody-Version Status + --------------- ---------------------- + trunk Works as of 2024-12-21 + 0.12 Works
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compliance_2023/mod_compliance_2023.lua Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,79 @@ +-- Copyright (c) 2021 Kim Alvefur +-- +-- This module is MIT licensed. + +local hostmanager = require "core.hostmanager"; + +local array = require "util.array"; +local set = require "util.set"; + +local modules_enabled = module:get_option_inherited_set("modules_enabled"); + +for host in pairs(hostmanager.get_children(module.host)) do + local component = module:context(host):get_option_string("component_module"); + if component then + modules_enabled:add(component); + modules_enabled:include(module:context(host):get_option_set("modules_enabled", {})); + end +end + +local function check(suggested, alternate, ...) + if set.intersection(modules_enabled, set.new({suggested; alternate; ...})):empty() then return suggested; end + return false; +end + +local compliance = { + array {"Server"; check("tls"); check("disco")}; + + array {"Advanced Server"; check("pep", "pep_simple")}; + + array {"Web"; check("bosh"); check("websocket")}; + + -- No Server requirements for Advanced Web + + array {"IM"; check("vcard_legacy", "vcard"); check("carbons"); check("http_file_share", "http_upload")}; + + array { + "Advanced IM"; + check("vcard_legacy", "vcard"); + check("blocklist"); + check("muc"); + check("private"); + check("smacks"); + check("mam"); + check("bookmarks"); + }; + + array {"Mobile"; check("smacks"); check("csi_simple", "csi_battery_saver")}; + + array {"Advanced Mobile"; check("cloud_notify")}; + + array {"A/V Calling"; check("turn_external", "external_services", "turncredentials", "extdisco")}; + +}; + +function check_compliance() + local compliant = true; + for _, suite in ipairs(compliance) do + local section = suite:pop(1); + if module:get_option_boolean("compliance_" .. section:lower():gsub("%A", "_"), true) then + local missing = set.new(suite:filter(function(m) return type(m) == "string" end):map(function(m) return "mod_" .. m end)); + if suite[1] then + if compliant then + compliant = false; + module:log("warn", "Missing some modules for XMPP Compliance 2023"); + end + module:log("info", "%s Compliance: %s", section, missing); + end + end + end + + if compliant then module:log("info", "XMPP Compliance 2023: Compliant ✔️"); end +end + +if prosody.start_time then + check_compliance() +else + module:hook_global("server-started", check_compliance); +end +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compliance_latest/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,27 @@ +--- +summary: XMPP Compliance Suites self-test +labels: +- Stage-Beta +rockspec: + dependencies: + - mod_compliance_2023 +... + +# Introduction + +This meta-module will always `require` (and therefore auto-load) the lastest compliance tester we have in the community modules. +Currently this is [mod_compliance_2023]. See the linked module for further details. + +If you do not use the *Prosody plugin installer* this module will likely have limited value to you. +You can also just install the current compliance tester manually. + +# Configuration + +Just load this module as any other module and it will automatically install and load [mod_compliance_2023] if you use the *Prosody plugin installer*. + +# Compatibility + + Prosody-Version Status + --------------- ---------------------- + trunk Works as of 2024-12-22 + 0.12 Works
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compliance_latest/mod_compliance_latest.lua Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,6 @@ +local success, err = pcall(function() module:depends("compliance_2023") end) + +if not success then +module:log_status( "error", "Error, can't load module: mod_compliance_2023. Is this module downloaded into a folder readable by prosody?" ) +return false +end
--- a/mod_component_client/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -Introduction -============ - -This module turns Prosody hosts into components of other XMPP servers. - -Configuration -============= - -Example configuration: - -``` {.lua} -VirtualHost "component.example.com" -modules_enabled = { "component_client" } -component_client = { - host = "localhost"; - port = 5347; - secret = "hunter2"; -} -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_component_client/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,19 @@ +Introduction +============ + +This module turns Prosody hosts into components of other XMPP servers. + +Configuration +============= + +Example configuration: + +``` {.lua} +VirtualHost "component.example.com" +modules_enabled = { "component_client" } +component_client = { + host = "localhost"; + port = 5347; + secret = "hunter2"; +} +```
--- a/mod_component_http/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ ---- -summary: 'Allows implementing a component or bot over HTTP' -... - -Introduction -============ - -This module allows you to implement a component that speaks HTTP. Stanzas (such as messages) coming from XMPP are sent to -a configurable URL as a HTTP POST. If the POST returns a response, that response is returned to the sender over XMPP. - -See also mod_post_msg. - -Example usage -------------- - -Example echo bot in PHP: - -``` php -<?php - -// Receive and decode message JSON -$post_data = file_get_contents('php://input'); -$received = json_decode($post_data)->body; - -// Send response -header('Content-Type: application/json'); -echo json_encode(array( - 'body' => "Did you say $received?" -)); - -?> -``` - -Configuration -============= - -The module is quite flexible, but should generally be loaded as a component like this: - -``` -Component "yourservice.example.com" "component_http" - component_post_url = "https://example.com/your-api" -``` - -Such a component would handle traffic for all JIDs with 'yourservice.example.com' as the hostname, such -as 'foobar@yourservice.example.com'. Although this example uses a subdomain, there is no requirement for -the component to use a subdomain. - -Available configuration options are: - - - Option Description - ------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------- - component\_post\_url The URL that will handle incoming stanzas - component\_post\_stanzas A list of stanza types to forward over HTTP. Defaults to `{ "message" }`. - -Details -======= - -Requests --------- - -Each received stanza is converted into a JSON object, and submitted to `component_post_url` using a HTTP POST request. - -The JSON object always has the following properties: - - Property Description - -------------------------- ------------ - to The JID that the stanza was sent to (e.g. foobar@your.component.domain) - from The sender's JID. - kind The kind of stanza (will always be "message", "presence" or "iq". - stanza The full XML of the stanza. - -Additionally, the JSON object may contain the following properties: - - Property Description - -------------------------- ------------ - body If the stanza is a message, and it contains a body, this is the string content of the body. - - -Responses ---------- - -If you wish to respond to a stanza, you may include a reply when you respond to the HTTP request. - -Responses must have a HTTP status 200 (OK), and must set the Conent-Type header to `application/json`. - -A response may contain any of the properties of a request. If not supplied, then defaults are chosen. - -If 'to' and 'from' are not specified in the response, they are automatically swapped so that the reply is sent to the original sender of the stanza. - -If 'kind' is not set, it defaults to 'message', and if 'body' is set, this is automatically added as a message body. - -If 'stanza' is set, it overrides all of the above, and the supplied stanza is sent as-is using Prosody's normal routing rules. Note that stanzas -sent by components must have a 'to' and 'from'. - -Presence --------- - -By default the module automatically handles presence to provide an always-on component, that automatically accepts subscription requests. - -This means that by default presence stanzas are not forwarded to the configured URL. To provide your own presence handling, you can override -this by adding "presence" to the component\_post\_stanzas option in your config. - - -Compatibility -============= - -Should work with all versions of Prosody from 0.9 upwards.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_component_http/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,108 @@ +--- +summary: 'Allows implementing a component or bot over HTTP' +... + +Introduction +============ + +This module allows you to implement a component that speaks HTTP. Stanzas (such as messages) coming from XMPP are sent to +a configurable URL as a HTTP POST. If the POST returns a response, that response is returned to the sender over XMPP. + +See also mod_post_msg. + +Example usage +------------- + +Example echo bot in PHP: + +``` php +<?php + +// Receive and decode message JSON +$post_data = file_get_contents('php://input'); +$received = json_decode($post_data)->body; + +// Send response +header('Content-Type: application/json'); +echo json_encode(array( + 'body' => "Did you say $received?" +)); + +?> +``` + +Configuration +============= + +The module is quite flexible, but should generally be loaded as a component like this: + +``` +Component "yourservice.example.com" "component_http" + component_post_url = "https://example.com/your-api" +``` + +Such a component would handle traffic for all JIDs with 'yourservice.example.com' as the hostname, such +as 'foobar@yourservice.example.com'. Although this example uses a subdomain, there is no requirement for +the component to use a subdomain. + +Available configuration options are: + + + Option Description + ------------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------- + component\_post\_url The URL that will handle incoming stanzas + component\_post\_stanzas A list of stanza types to forward over HTTP. Defaults to `{ "message" }`. + +Details +======= + +Requests +-------- + +Each received stanza is converted into a JSON object, and submitted to `component_post_url` using a HTTP POST request. + +The JSON object always has the following properties: + + Property Description + -------------------------- ------------ + to The JID that the stanza was sent to (e.g. foobar@your.component.domain) + from The sender's JID. + kind The kind of stanza (will always be "message", "presence" or "iq". + stanza The full XML of the stanza. + +Additionally, the JSON object may contain the following properties: + + Property Description + -------------------------- ------------ + body If the stanza is a message, and it contains a body, this is the string content of the body. + + +Responses +--------- + +If you wish to respond to a stanza, you may include a reply when you respond to the HTTP request. + +Responses must have a HTTP status 200 (OK), and must set the Conent-Type header to `application/json`. + +A response may contain any of the properties of a request. If not supplied, then defaults are chosen. + +If 'to' and 'from' are not specified in the response, they are automatically swapped so that the reply is sent to the original sender of the stanza. + +If 'kind' is not set, it defaults to 'message', and if 'body' is set, this is automatically added as a message body. + +If 'stanza' is set, it overrides all of the above, and the supplied stanza is sent as-is using Prosody's normal routing rules. Note that stanzas +sent by components must have a 'to' and 'from'. + +Presence +-------- + +By default the module automatically handles presence to provide an always-on component, that automatically accepts subscription requests. + +This means that by default presence stanzas are not forwarded to the configured URL. To provide your own presence handling, you can override +this by adding "presence" to the component\_post\_stanzas option in your config. + + +Compatibility +============= + +Should work with all versions of Prosody from 0.9 upwards.
--- a/mod_component_roundrobin/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ ---- -labels: -summary: 'Component round-robin load balancing module' -... - -Introduction -============ - -This module enables multiple instances of external components to connect -at the same time, and does round-robin load-balancing of incoming -stanzas. - -Example -======= - - Component "test.example.com" - modules_enabled = { "component_roundrobin" } - -- Other component options such as secrets here
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_component_roundrobin/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,18 @@ +--- +labels: +summary: 'Component round-robin load balancing module' +... + +Introduction +============ + +This module enables multiple instances of external components to connect +at the same time, and does round-robin load-balancing of incoming +stanzas. + +Example +======= + + Component "test.example.com" + modules_enabled = { "component_roundrobin" } + -- Other component options such as secrets here
--- a/mod_compression_unsafe/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -**NOTE:** XMPP compression has unresolved [security concerns](https://mail.jabber.org/pipermail/standards/2014-October/029215.html), -and this module has been removed from Prosody and renamed. - -While the bandwidth usage of XMPP isn't that much, compressing the data -sent to/from your server can give significant benefits to those on slow -connections, such as dial-up or mobile networks. Prosody supports -compression for client-to-server (if your client supports it) and -server-to-server streams using the mod\_compression plugin. - -# Details - -mod\_compression implements [XEP-0138], and supports the zlib compression -algorithm. - -## Dependencies - -The XMPP protocol specifies that all clients and servers supporting -compression must support the "zlib" compression method, and this is what -Prosody uses. However you will need to install zlib support for Lua on -your system. There are different ways of doing this depending on your -system. If in doubt whether it is installed correctly, the command -`lua -lzlib` in a console should open a Lua prompt with no errors. - -Debian/Ubuntu -: `apt-get install lua-zlib` - -LuaRocks -: `luarocks install lua-zlib` - -Source -: <https://github.com/brimworks/lua-zlib> - -# Usage - -``` lua -modules_enabled = { - -- Other modules - "compression_unsafe"; -- Enable mod_compression_unsafe -} -``` - -## Configuration - -The compression level can be set using the `compression_level` option -which can be a number from 1 to 9. Higher compression levels will use -more resources but less bandwidth. - -## Example - -``` lua -modules_enabled = { - -- Other modules - "compression_unsafe"; -- Enable mod_compression_unsafe -} - -compression_level = 5 -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compression_unsafe/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,65 @@ +--- +labels: +- 'Stage-Deprecated' +summary: 'Implementation of XEP-0138' +... + +::: {.alert .alert-warning} +**NOTE:** XMPP compression has unresolved [security concerns](https://mail.jabber.org/pipermail/standards/2014-October/029215.html), +and this module has been removed from Prosody and renamed. +::: + +While the bandwidth usage of XMPP isn't that much, compressing the data +sent to/from your server can give significant benefits to those on slow +connections, such as dial-up or mobile networks. Prosody supports +compression for client-to-server (if your client supports it) and +server-to-server streams using the mod\_compression plugin. + +# Details + +mod\_compression implements [XEP-0138], and supports the zlib compression +algorithm. + +## Dependencies + +The XMPP protocol specifies that all clients and servers supporting +compression must support the "zlib" compression method, and this is what +Prosody uses. However you will need to install zlib support for Lua on +your system. There are different ways of doing this depending on your +system. If in doubt whether it is installed correctly, the command +`lua -lzlib` in a console should open a Lua prompt with no errors. + +Debian/Ubuntu +: `apt-get install lua-zlib` + +LuaRocks +: `luarocks install lua-zlib` + +Source +: <https://github.com/brimworks/lua-zlib> + +# Usage + +``` lua +modules_enabled = { + -- Other modules + "compression_unsafe"; -- Enable mod_compression_unsafe +} +``` + +## Configuration + +The compression level can be set using the `compression_level` option +which can be a number from 1 to 9. Higher compression levels will use +more resources but less bandwidth. + +## Example + +``` lua +modules_enabled = { + -- Other modules + "compression_unsafe"; -- Enable mod_compression_unsafe +} + +compression_level = 5 +```
--- a/mod_conformance_restricted/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ ---- -labels: -summary: Send restricted XML for conformance testing -... - -Introduction -============ - -This module sends processing instructions, comments, DTDs and a non -predefined entity (defined by the DTD) to the requesting entity. - -Usage -===== - -Send "PI", "comment", "DTD" or "entity" to -<xmpp:example.com/conformance>, while directly connected to the Prosody -instance.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_conformance_restricted/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,17 @@ +--- +labels: +summary: Send restricted XML for conformance testing +... + +Introduction +============ + +This module sends processing instructions, comments, DTDs and a non +predefined entity (defined by the DTD) to the requesting entity. + +Usage +===== + +Send "PI", "comment", "DTD" or "entity" to +<xmpp:example.com/conformance>, while directly connected to the Prosody +instance.
--- a/mod_conversejs/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,197 +0,0 @@ ---- -summary: Simplify setup of Converse.js -depends: -- 'mod\_bosh' -- 'mod\_websocket' -provides: -- http -title: 'mod\_conversejs' -rockspec: - build: - copy_directories: - - templates ---- - -Introduction -============ - -This module simplifies setup of [Converse.js](https://conversejs.org/) -by serving it from Prosodys internal [http server][doc:http] along with -generated configuration to match the local VirtualHost. It becomes -available on an URL like `https://example.com:5281/conversejs` - -Configuration -============= - -The module uses general Prosody options for basic configuration. It -should just work after loading it. - -``` {.lua} -modules_enabled = { - -- other modules... - "conversejs"; -} -``` - -Authentication --------------- - -[Authentication settings][doc:authentication] are used determine -whether to configure Converse.js to use `login` or `anonymous` mode. - -Connection methods ------------------- - -mod_conversejs also determines the [BOSH][doc:setting_up_bosh] and -[WebSocket][doc:websocket] URL automatically, see their respective -documentation for how to configure them. Both connection methods are -loaded automatically. - -Auto-loading of `mod_bosh` or `mod_websocket` can be prevented by adding -it to `modules_disabled` but note that at least one of them must be -allowed for Converse.js to work. - -HTTP ----- - -The module is served on Prosody's default HTTP ports at the path -`/conversejs`. More details on configuring HTTP modules in Prosody can -be found in our [HTTP documentation](http://prosody.im/doc/http). - -## Templates - -The HTML and JS can be customized either by editing the included -`template.html` and `template.js` files or configuring your own like: - -```lua -conversejs_html_template = "/path/to/my-template.html" -conversejs_js_template = "/path/to/my-template.js" -``` - -The HTML template uses Prosodys -[`util.interpolation`][doc:developers:util:interpolation] template -library while the JS template has `%s` where generated settings are -injected. - -Other ------ - -To pass [other Converse.js -options](https://conversejs.org/docs/html/configuration.html), or -override the derived settings, one can set `conversejs_options` like -this: - -``` {.lua} -conversejs_options = { - debug = true; - view_mode = "fullscreen"; -} -``` - -Note that the following options are automatically provided, and -**overriding them may cause problems**: - -- `authentication` *based on Prosody's authentication settings* -- `bosh_service_url` -- `websocket_url` -- `discover_connection_methods` *Disabled since we provide this* -- `assets_path` -- `allow_registration` *based on whether registration is enabled* -- These settings are set to the current `VirtualHost`: - - `jid` - - `default_domain` - - `domain_placeholder` - - `registration_domain` - -`mod_bosh` and/or `mod_websocket` are automatically enabled if available -and the respective endpoint is included in the generated options. - -## Loading resources - -By default the module will load the main script and CSS from -cdn.conversejs.org. For privacy or performance reasons you may want to -load the scripts from somewhere else. - -To use a local distribution or build of Converse.js set -conversejs_resources to the local path of "dist" directory: - -``` {.lua} -conversejs_resources = "/usr/src/conversejs/dist"; -``` - -To use a different web server or CDN simply use the conversejs_cdn -option: - -``` {.lua} -conversejs_cdn = "https://cdn.example.com" -``` - -To select a specific version of Converse.js, you may override the version: - -``` {.lua} -conversejs_version = "5.0.0" -``` - -Note that versions other than the default may not have been tested with this module, and may include incompatible changes. - -Finally, if you can override all of the above and just specify links directly to the CSS and JS files: - -``` {.lua} -conversejs_script = "https://example.com/my-converse.js" -conversejs_css = "https://example.com/my-converse.css" -``` - -Additional tags ---------------- - -To add additional tags to the module, such as custom CSS or scripts, you may use the conversejs_tags option: - -``` {.lua} -conversejs_tags = { - -- Load custom CSS - [[<link rel="stylesheet" href="https://example.org/css/custom.css">]]; - - -- Load libsignal-protocol.js for OMEMO support (GPLv3; be aware of licence implications) - [[<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>]]; -} -``` - -The example above uses the `[[` and `]]` syntax simply because it will not conflict with any embedded quotes. - -Custimizing the generated PWA options -------------------------------------- - -``` {.lua} -conversejs_name = "Service name" -- Also used as the web page title -conversejs_short_name = "Shorter name" -conversejs_description = "Description of the service" -conversejs_manifest_icons = { - { - src = "https://example.com/logo/512.png", - sizes = "512x512", - }, - { - src = "https://example.com/logo/192.png", - sizes = "192x192", - }, - { - src = "https://example.com/logo/192.svg", - sizes = "192x192", - }, - { - src = "https://example.com/logo/512.svg", - sizes = "512x512", - }, -} -conversejs_pwa_color = "#397491" -``` - -Compatibility -============= - - Prosody version state - ----------------- --------------- - 0.9 Does not work - 0.10 Should work - 0.11 Works - trunk Works
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_conversejs/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,197 @@ +--- +summary: Simplify setup of Converse.js +depends: +- 'mod\_bosh' +- 'mod\_websocket' +provides: +- http +title: 'mod\_conversejs' +rockspec: + build: + copy_directories: + - templates +--- + +Introduction +============ + +This module simplifies setup of [Converse.js](https://conversejs.org/) +by serving it from Prosodys internal [http server][doc:http] along with +generated configuration to match the local VirtualHost. It becomes +available on an URL like `https://example.com:5281/conversejs` + +Configuration +============= + +The module uses general Prosody options for basic configuration. It +should just work after loading it. + +``` {.lua} +modules_enabled = { + -- other modules... + "conversejs"; +} +``` + +Authentication +-------------- + +[Authentication settings][doc:authentication] are used determine +whether to configure Converse.js to use `login` or `anonymous` mode. + +Connection methods +------------------ + +mod_conversejs also determines the [BOSH][doc:setting_up_bosh] and +[WebSocket][doc:websocket] URL automatically, see their respective +documentation for how to configure them. Both connection methods are +loaded automatically. + +Auto-loading of `mod_bosh` or `mod_websocket` can be prevented by adding +it to `modules_disabled` but note that at least one of them must be +allowed for Converse.js to work. + +HTTP +---- + +The module is served on Prosody's default HTTP ports at the path +`/conversejs`. More details on configuring HTTP modules in Prosody can +be found in our [HTTP documentation](http://prosody.im/doc/http). + +## Templates + +The HTML and JS can be customized either by editing the included +`template.html` and `template.js` files or configuring your own like: + +```lua +conversejs_html_template = "/path/to/my-template.html" +conversejs_js_template = "/path/to/my-template.js" +``` + +The HTML template uses Prosodys +[`util.interpolation`][doc:developers:util:interpolation] template +library while the JS template has `%s` where generated settings are +injected. + +Other +----- + +To pass [other Converse.js +options](https://conversejs.org/docs/html/configuration.html), or +override the derived settings, one can set `conversejs_options` like +this: + +``` {.lua} +conversejs_options = { + debug = true; + view_mode = "fullscreen"; +} +``` + +Note that the following options are automatically provided, and +**overriding them may cause problems**: + +- `authentication` *based on Prosody's authentication settings* +- `bosh_service_url` +- `websocket_url` +- `discover_connection_methods` *Disabled since we provide this* +- `assets_path` +- `allow_registration` *based on whether registration is enabled* +- These settings are set to the current `VirtualHost`: + - `jid` + - `default_domain` + - `domain_placeholder` + - `registration_domain` + +`mod_bosh` and/or `mod_websocket` are automatically enabled if available +and the respective endpoint is included in the generated options. + +## Loading resources + +By default the module will load the main script and CSS from +cdn.conversejs.org. For privacy or performance reasons you may want to +load the scripts from somewhere else. + +To use a local distribution or build of Converse.js set +conversejs_resources to the local path of "dist" directory: + +``` {.lua} +conversejs_resources = "/usr/src/conversejs/dist"; +``` + +To use a different web server or CDN simply use the conversejs_cdn +option: + +``` {.lua} +conversejs_cdn = "https://cdn.example.com" +``` + +To select a specific version of Converse.js, you may override the version: + +``` {.lua} +conversejs_version = "5.0.0" +``` + +Note that versions other than the default may not have been tested with this module, and may include incompatible changes. + +Finally, if you can override all of the above and just specify links directly to the CSS and JS files: + +``` {.lua} +conversejs_script = "https://example.com/my-converse.js" +conversejs_css = "https://example.com/my-converse.css" +``` + +Additional tags +--------------- + +To add additional tags to the module, such as custom CSS or scripts, you may use the conversejs_tags option: + +``` {.lua} +conversejs_tags = { + -- Load custom CSS + [[<link rel="stylesheet" href="https://example.org/css/custom.css">]]; + + -- Load libsignal-protocol.js for OMEMO support (GPLv3; be aware of licence implications) + [[<script src="https://cdn.conversejs.org/3rdparty/libsignal-protocol.min.js"></script>]]; +} +``` + +The example above uses the `[[` and `]]` syntax simply because it will not conflict with any embedded quotes. + +Custimizing the generated PWA options +------------------------------------- + +``` {.lua} +conversejs_name = "Service name" -- Also used as the web page title +conversejs_short_name = "Shorter name" +conversejs_description = "Description of the service" +conversejs_manifest_icons = { + { + src = "https://example.com/logo/512.png", + sizes = "512x512", + }, + { + src = "https://example.com/logo/192.png", + sizes = "192x192", + }, + { + src = "https://example.com/logo/192.svg", + sizes = "192x192", + }, + { + src = "https://example.com/logo/512.svg", + sizes = "512x512", + }, +} +conversejs_pwa_color = "#397491" +``` + +Compatibility +============= + + Prosody version state + ----------------- --------------- + 0.9 Does not work + 0.10 Should work + 0.11 Works + trunk Works
--- a/mod_couchdb/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Storage' -summary: A CouchDB backend for Prosody -... - -***Note:** This module needs updating to the 0.8 storage module API.* - -Introduction -============ - -This is an experimental Prosody backend for CouchDB. - -Configuration -============= - -In your config file, under the relevant host, add: - - datastore = "couchdb"; - couchdb_url = "http://127.0.0.1:5984/database-name"; - -Compatibility -============= - -This module was developed as a prototype during development of the -storage provider API in Prosody 0.8. The final storage provider API is -different, so this module needs updates to work. - -Quirks -====== - -This implementation is a work in progress. - -- The data stored in couchdb is limited to: account data, rosters, - private XML and vCards
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_couchdb/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +labels: +- 'Stage-Alpha' +- 'Type-Storage' +summary: A CouchDB backend for Prosody +... + +***Note:** This module needs updating to the 0.8 storage module API.* + +Introduction +============ + +This is an experimental Prosody backend for CouchDB. + +Configuration +============= + +In your config file, under the relevant host, add: + + datastore = "couchdb"; + couchdb_url = "http://127.0.0.1:5984/database-name"; + +Compatibility +============= + +This module was developed as a prototype during development of the +storage provider API in Prosody 0.8. The final storage provider API is +different, so this module needs updates to work. + +Quirks +====== + +This implementation is a work in progress. + +- The data stored in couchdb is limited to: account data, rosters, + private XML and vCards
--- a/mod_csi/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ ---- -summary: Client State Indication support -superseded_by: mod_csi -labels: -- 'Stage-Obsolete' -... - -Since Prosody 0.11, this module is [included in Prosody](https://prosody.im/doc/modules/mod_csi), you will be redirected there shortly.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,8 @@ +--- +summary: Client State Indication support +superseded_by: mod_csi +labels: +- 'Stage-Obsolete' +... + +Since Prosody 0.11, this module is [included in Prosody](https://prosody.im/doc/modules/mod_csi), you will be redirected there shortly.
--- a/mod_csi_battery_saver/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ ---- -description: CSI module to save battery on mobile devices ---- - -Please use this module instead of [mod_csi_pump] if you want timestamping, -properly handled carbon copies, support for handling encrypted messages and -correctly handled smacks events. - -If smacks is used on the same server this needs at least version [f70c02c14161] -of the smacks module! There could be message reordering on resume otherwise. - -Stanzas are queued in a buffer until either an "important" stanza is -encountered or the buffer becomes full. Then all queued stanzas are sent -at the same time. This way, nothing is lost or reordered while still -allowing for power usage savings by not requiring mobile clients to -bring up their radio for unimportant stanzas. - -`IQ` stanzas, and `message` stanzas containing a body or being encrypted, -chat markers (see [XEP-0333]) and all *nonzas* are considered important. -If the config option `csi_battery_saver_filter_muc` is set to true, -groupchat messages must set a subject or have the user's username or nickname -mentioned in the messages (or be encrypted) to count as "important". -**Warning:** you should only set this to true if your users can live with -groupchat messages being delayed several minutes! -On the other hand if this option is set to false (*default*), -all groupchat messages having a body or being encrypted are considered "important". -In this case [mod_csi_muc_priorities] can be used to let user configure per groupchat -which of them are important for them (e.g. all messages having a body are important) -and which are not (e.g. only mentions and own messages are important). -If users don't change their settings, [mod_csi_muc_priorities] handles all groupchats -as important (see its docs for more information). -`Presence` stanzas are always considered not "important". - -All buffered stanzas that allow timestamping are properly stamped to -reflect their original send time, see [XEP-0203]. - -Use with other CSI plugins such as [mod_throttle_presence], -[mod_filter_chatstates], [mod_csi_simple] or [mod_csi_pump] is **not** supported. -Usage of [mod_csi_muc_priorities] is allowed (see configuration). - -*Hint:* [mod_csi_muc_priorities] needs [mod_track_muc_joins] to function properly. - -Configuration -============= - - Option Default Description - ---------------------------------- ---------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - `csi_battery_saver_filter_muc` false Controls whether all MUC messages having a body should be considered as important as long as [mod_csi_muc_priorities] doesn't configure them to be **not** important (false) or only such containing the user's room nic (true). **WARNING:** you should only set this to true if your users can live with muc messages being delayed several minutes. - `csi_battery_saver_queue_size` 256 Size of the stanza buffer used for the queue (if the queue is full a flush will be forced) - - -[f70c02c14161]: //hg.prosody.im/prosody-modules/raw-file/f70c02c14161/mod_smacks/mod_smacks.lua \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi_battery_saver/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,52 @@ +--- +description: CSI module to save battery on mobile devices +--- + +Please use this module instead of [mod_csi_pump] if you want timestamping, +properly handled carbon copies, support for handling encrypted messages and +correctly handled smacks events. + +If smacks is used on the same server this needs at least version [f70c02c14161] +of the smacks module! There could be message reordering on resume otherwise. + +Stanzas are queued in a buffer until either an "important" stanza is +encountered or the buffer becomes full. Then all queued stanzas are sent +at the same time. This way, nothing is lost or reordered while still +allowing for power usage savings by not requiring mobile clients to +bring up their radio for unimportant stanzas. + +`IQ` stanzas, and `message` stanzas containing a body or being encrypted, +chat markers (see [XEP-0333]) and all *nonzas* are considered important. +If the config option `csi_battery_saver_filter_muc` is set to true, +groupchat messages must set a subject or have the user's username or nickname +mentioned in the messages (or be encrypted) to count as "important". +**Warning:** you should only set this to true if your users can live with +groupchat messages being delayed several minutes! +On the other hand if this option is set to false (*default*), +all groupchat messages having a body or being encrypted are considered "important". +In this case [mod_csi_muc_priorities] can be used to let user configure per groupchat +which of them are important for them (e.g. all messages having a body are important) +and which are not (e.g. only mentions and own messages are important). +If users don't change their settings, [mod_csi_muc_priorities] handles all groupchats +as important (see its docs for more information). +`Presence` stanzas are always considered not "important". + +All buffered stanzas that allow timestamping are properly stamped to +reflect their original send time, see [XEP-0203]. + +Use with other CSI plugins such as [mod_throttle_presence], +[mod_filter_chatstates], [mod_csi_simple] or [mod_csi_pump] is **not** supported. +Usage of [mod_csi_muc_priorities] is allowed (see configuration). + +*Hint:* [mod_csi_muc_priorities] needs [mod_track_muc_joins] to function properly. + +Configuration +============= + + Option Default Description + ---------------------------------- ---------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + `csi_battery_saver_filter_muc` false Controls whether all MUC messages having a body should be considered as important as long as [mod_csi_muc_priorities] doesn't configure them to be **not** important (false) or only such containing the user's room nic (true). **WARNING:** you should only set this to true if your users can live with muc messages being delayed several minutes. + `csi_battery_saver_queue_size` 256 Size of the stanza buffer used for the queue (if the queue is full a flush will be forced) + + +[f70c02c14161]: //hg.prosody.im/prosody-modules/raw-file/f70c02c14161/mod_smacks/mod_smacks.lua \ No newline at end of file
--- a/mod_csi_compat/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ ---- -labels: -summary: 'Implement the google:queue protocol and map to mod\_csi events' -... - -Introduction -============ - -This module implements the google:queue protocol and maps it to -[mod\_csi](mod_csi.html) events. - -Configuration -============= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi_compat/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,23 @@ +--- +labels: +summary: 'Implement the google:queue protocol and map to mod\_csi events' +... + +Introduction +============ + +This module implements the google:queue protocol and maps it to +[mod\_csi](mod_csi.html) events. + +Configuration +============= + +There is no configuration for this module, just add it to +modules\_enabled as normal. + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_csi_grace_period/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -This module helps reduces power usage of inactive mobile clients while -another client is actively used. E.g. in the case of chatting on a -desktop computer, instantly pushing messages to a phone in someones -pocket is not the best use of radio time. - -Works with [mod_csi_simple][doc:modules:mod_csi_simple] which is -included with Prosody since 0.11
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi_grace_period/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,24 @@ +--- +summary: Don't wake inactive clients, if annother is in use. +labels: +- 'Stage-Beta' +... + +# Introduction + +This module helps reduces power usage of inactive mobile clients while +another client is actively used. E.g. in the case of chatting on a +desktop computer, instantly pushing messages to a phone in someones +pocket is not the best use of radio time. + +# Compatibility + +Works with [mod_csi_simple][doc:modules:mod_csi_simple] which is +included with Prosody. + + ------- -------------- + trunk* Works + 0.12 Works + ------- -------------- + +*as of 2024-10-22
--- a/mod_csi_muc_priorities/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -# Introduction - -This module lets users specify which of the group chats they are in are -less important. This influences when -[mod_csi_simple][doc:modules:mod_csi_simple] decides to send -stanzas vs waiting until there is more to send. Users in many large -public channels might benefit from this. - -# Configuration - -The module is configured via ad-hoc an command called *Configure group -chat priorities* that should appear in the menus of compatible clients. - -The command presents a form that accepts a list of XMPP addresses. -Currently there is a single priority, *Lower priority*, which is -suitable for e.g. noisy public channels. mod_csi_simple considers -groupchat messages important by default on the assumptions that smaller -and more important private chats are more common among most users. - -A message of type groupchat from an address in this list will not be -considered important enough to send it to an inactive client, unless it -is from the current user or mentions of their nickname. **Note** that -mention support require the separate module [mod_track_muc_joins] -to also be loaded. - -``` {.lua} -modules_enabled = { - -- other modules etc - - "csi_simple", - "csi_muc_priorities", - "track_muc_joins", -- optional -} -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi_muc_priorities/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,35 @@ +# Introduction + +This module lets users specify which of the group chats they are in are +more or less important. This influences when +[mod_csi_simple][doc:modules:mod_csi_simple] decides to send +stanzas vs waiting until there is more to send. Users in many large +public channels might benefit from this. + +# Configuration + +The module is configured via ad-hoc an command called *Configure group +chat priorities* that should appear in the menus of compatible clients. + +The command presents a form that accepts a list of XMPP addresses. +Currently you can specify channels as lower priority (which is suitable +for e.g. noisy public channels) or higher priority (which is suitable +for e.g. small private channels where immediate message delivery is +desired). You can also specify whether mucs default to lower priority +or not. + +A message of type groupchat from an address in the low priority list will +not be considered important enough to send it to an inactive client, +unless it is from the current user or mentions of their nickname. +**Note** that mention support require the separate module +[mod_track_muc_joins] to also be loaded. + +``` {.lua} +modules_enabled = { + -- other modules etc + + "csi_simple", + "csi_muc_priorities", + "track_muc_joins", -- optional +} +```
--- a/mod_csi_simple_compat/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -# About - -This module allows using the [mod_csi_simple][doc:modules:mod_csi_simple] -setting `csi_important_payloads` (added in trunk/0.12) in Prosody 0.11.x. - -# Config - -``` -modules_enabled = { - -- other modules etc - "csi_simple", - "csi_simple_compat", -} - -csi_important_payloads = { - -- Anything in this namespace: - "{urn:example:important-namespace}", - -- Specific element name and namespace: - "{urn:example:xmpp:priority}super-important", -} -``` - -# Example - -``` lua -csi_important_payloads = { - -- XEP-0353: Jingle Message Initiation - "{urn:xmpp:jingle-message:0}", -} -``` -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_csi_simple_compat/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,31 @@ +# About + +This module allows using the [mod_csi_simple][doc:modules:mod_csi_simple] +setting `csi_important_payloads` (added in trunk/0.12) in Prosody 0.11.x. + +# Config + +``` +modules_enabled = { + -- other modules etc + "csi_simple", + "csi_simple_compat", +} + +csi_important_payloads = { + -- Anything in this namespace: + "{urn:example:important-namespace}", + -- Specific element name and namespace: + "{urn:example:xmpp:priority}super-important", +} +``` + +# Example + +``` lua +csi_important_payloads = { + -- XEP-0353: Jingle Message Initiation + "{urn:xmpp:jingle-message:0}", +} +``` +
--- a/mod_data_access/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ ---- -labels: -summary: 'HTTP access to prosody’s storage mechanism' -... - -Introduction ------------- - -This module gives HTTP access to prosody’s storage mechanism. It uses -normal HTTP verbs and [Basic HTTP -authentication](http://tools.ietf.org/html/rfc2617), so you could call -it RESTful if you like buzzwords. - -Syntax ------- - -To Fetch data, issue a normal GET request - - GET /data[/<host>/<user>]/<store>[/<format>] HTTP/1.1 - Authorization: <base64(authzid:password)> - -OR - - PUT|POST /data[/<host>/<user>]/<store> HTTP/1.1 - Content-Type: text/x-lua | application/json - - <data> - -These map to `datamanager.method(user, host, store, data)`, where choice -of `method` and its parameters are explained below. - -### Verbs - - Verb Meaning datamanager method - -------- ------------------------------- --------------------------- - `GET` Just fetch data `load()` or `list_load()` - `PUT` Replace all data in the store `store()` - `POST` Append item to the store `list_append()` - -Note: In a `GET` request, if `load()` returns `nil`, `list_load()` will -be tried instead. - -### Fields - - Field Description Default - ---------- ----------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- - `host` Which virtual host to access Required. If not set in the path, the domain-part of the authzid is used. - `user` Which users storage to access Required. If not set in the path, uses the node part of the authzid. - `store` Which storage to access. Required. - `format` Which format to serialize to. `json` and `lua` are supported. When uploading data, the `Content-Type` header is used. `json` - `data` The actual data to upload in a `PUT` or `POST` request. `nil` - -Note: Only admins can change data for users other than themselves. - -### Example usage - -Here follows some example usage using `curl`. - -Get your account details: - - curl http://prosody.local:5280/data/accounts -u user@example.com:secr1t - {"password":"secr1t"} - -Set someones account details: - - curl -X PUT http://prosody.local:5280/data/example.com/user/accounts -u admin@host:r00tp4ssw0rd --header 'Content-Type: application/json' --data-binary '{"password":"changeme"}' - -### Client library - -- https://metacpan.org/module/Prosody::Mod::Data::Access - -### TODO - -- Use `Accept` header. - -Compatibility -============= - - ------- -------------- - trunk Doesn't work (uses is_admin) - 0.12 Works? - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_data_access/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,82 @@ +--- +labels: +summary: 'HTTP access to prosody’s storage mechanism' +... + +Introduction +------------ + +This module gives HTTP access to prosody’s storage mechanism. It uses +normal HTTP verbs and [Basic HTTP +authentication](http://tools.ietf.org/html/rfc2617), so you could call +it RESTful if you like buzzwords. + +Syntax +------ + +To Fetch data, issue a normal GET request + + GET /data[/<host>/<user>]/<store>[/<format>] HTTP/1.1 + Authorization: <base64(authzid:password)> + +OR + + PUT|POST /data[/<host>/<user>]/<store> HTTP/1.1 + Content-Type: text/x-lua | application/json + + <data> + +These map to `datamanager.method(user, host, store, data)`, where choice +of `method` and its parameters are explained below. + +### Verbs + + Verb Meaning datamanager method + -------- ------------------------------- --------------------------- + `GET` Just fetch data `load()` or `list_load()` + `PUT` Replace all data in the store `store()` + `POST` Append item to the store `list_append()` + +Note: In a `GET` request, if `load()` returns `nil`, `list_load()` will +be tried instead. + +### Fields + + Field Description Default + ---------- ----------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------- + `host` Which virtual host to access Required. If not set in the path, the domain-part of the authzid is used. + `user` Which users storage to access Required. If not set in the path, uses the node part of the authzid. + `store` Which storage to access. Required. + `format` Which format to serialize to. `json` and `lua` are supported. When uploading data, the `Content-Type` header is used. `json` + `data` The actual data to upload in a `PUT` or `POST` request. `nil` + +Note: Only admins can change data for users other than themselves. + +### Example usage + +Here follows some example usage using `curl`. + +Get your account details: + + curl http://prosody.local:5280/data/accounts -u user@example.com:secr1t + {"password":"secr1t"} + +Set someones account details: + + curl -X PUT http://prosody.local:5280/data/example.com/user/accounts -u admin@host:r00tp4ssw0rd --header 'Content-Type: application/json' --data-binary '{"password":"changeme"}' + +### Client library + +- https://metacpan.org/module/Prosody::Mod::Data::Access + +### TODO + +- Use `Accept` header. + +Compatibility +============= + + ------- -------------- + trunk Doesn't work (uses is_admin) + 0.12 Works? + ------- --------------
--- a/mod_debug_omemo/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -summary: "Generate OMEMO debugging links" -labels: -- 'Stage-Alpha' -... - -Introduction -============ - -This module allows you to view advanced information about OMEMO-encrypted messages, -and can be helpful to diagnose decryption problems. - -It generates a link to itself and adds this link to the plaintext contents of -encrypted messages. This will be shown by clients that do not support OMEMO, -or are unable to decrypt the message. - -This module depends on a working HTTP setup in Prosody, and honours the [usual -HTTP configuration options](https://prosody.im/doc/http). - -Configuration -============= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -Compatibility -============= - - ----- ------- - 0.11 Hopefully works - trunk Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_debug_omemo/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +--- +summary: "Generate OMEMO debugging links" +labels: +- 'Stage-Alpha' +... + +Introduction +============ + +This module allows you to view advanced information about OMEMO-encrypted messages, +and can be helpful to diagnose decryption problems. + +It generates a link to itself and adds this link to the plaintext contents of +encrypted messages. This will be shown by clients that do not support OMEMO, +or are unable to decrypt the message. + +This module depends on a working HTTP setup in Prosody, and honours the [usual +HTTP configuration options](https://prosody.im/doc/http). + +Configuration +============= + +There is no configuration for this module, just add it to +modules\_enabled as normal. + +Compatibility +============= + + ----- ------- + 0.11 Hopefully works + trunk Works + ----- -------
--- a/mod_debug_traceback/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Generate tracebacks on-demand ---- - -# Introduction - -This module writes out a traceback to a file when a chosen signal (by default -`SIGUSR1`) is received. It can be useful to diagnose cases where Prosody is -unresponsive. - -# Configuration - -`debug_traceback_filename` -: The name of the file to write the traceback to. Some variables - are supported, see [mod_log_ringbuffer] docs for more info. Defaults - to `{paths.data}/traceback-{pid}-{count}.log`. - -`debug_traceback_signal` -: The name of the signal to listen for. Defaults to `SIGUSR1`. - -# Compatibility - -Prosody 0.12 or later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_debug_traceback/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,25 @@ +--- +labels: +- 'Stage-Alpha' +summary: Generate tracebacks on-demand +--- + +# Introduction + +This module writes out a traceback to a file when a chosen signal (by default +`SIGUSR1`) is received. It can be useful to diagnose cases where Prosody is +unresponsive. + +# Configuration + +`debug_traceback_filename` +: The name of the file to write the traceback to. Some variables + are supported, see [mod_log_ringbuffer] docs for more info. Defaults + to `{paths.data}/traceback-{pid}-{count}.log`. + +`debug_traceback_signal` +: The name of the signal to listen for. Defaults to `SIGUSR1`. + +# Compatibility + +Prosody 0.12 or later.
--- a/mod_default_bookmarks/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ ---- -labels: -summary: Default bookmarked chatrooms -... - -Introduction -============ - -This module allows you to add default bookmarks for users. It only kicks -in when the user has no existing bookmarks, so users are free to add, -change or remove them. - -Details -======= - -Most clients support storing a private list of room "bookmarks" on the -server. When they log in, they fetch this list and join any that are -marked as "autojoin". If this list is empty, as it would be for new -users, this module would return the list supplied in the config. - -Configuration -============= - -Add "default\_bookmarks" to your modules\_enabled list: - - modules_enabled = { - -- ...other modules here... -- - "default_bookmarks"; - -- ...maybe some more here... -- - } - -Then add a list of the default rooms you want: - -``` lua -default_bookmarks = { - { jid = "room@conference.example.com"; name = "The Room"; autojoin = true }; - -- Specifying a password is supported: - { jid = "secret-room@conference.example.com"; name = "A Secret Room"; password = "secret"; autojoin = true }; - -- You can also use this compact syntax: - "yetanother@conference.example.com"; -- this will get "yetanother" as name -}; -``` - -Compatibility -------------- - - ------- --------------- - trunk Works - 0.10 Should work - 0.9 Should work - ------- ---------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_default_bookmarks/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,51 @@ +--- +labels: +summary: Default bookmarked chatrooms +... + +Introduction +============ + +This module allows you to add default bookmarks for users. It only kicks +in when the user has no existing bookmarks, so users are free to add, +change or remove them. + +Details +======= + +Most clients support storing a private list of room "bookmarks" on the +server. When they log in, they fetch this list and join any that are +marked as "autojoin". If this list is empty, as it would be for new +users, this module would return the list supplied in the config. + +Configuration +============= + +Add "default\_bookmarks" to your modules\_enabled list: + + modules_enabled = { + -- ...other modules here... -- + "default_bookmarks"; + -- ...maybe some more here... -- + } + +Then add a list of the default rooms you want: + +``` lua +default_bookmarks = { + { jid = "room@conference.example.com"; name = "The Room"; autojoin = true }; + -- Specifying a password is supported: + { jid = "secret-room@conference.example.com"; name = "A Secret Room"; password = "secret"; autojoin = true }; + -- You can also use this compact syntax: + "yetanother@conference.example.com"; -- this will get "yetanother" as name +}; +``` + +Compatibility +------------- + + ------- --------------- + trunk Works + 0.10 Should work + 0.9 Should work + ------- ---------------
--- a/mod_default_vcard/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Automatically populate vcard based on account details -... - -Introduction -============ - -It is possible for the user to supply more than just a username and -password when creating an account using -[mod\_register](https://prosody.im/doc/modules/mod_register). This -module automatically copies over that data to the user's vcard. - -Details -======= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -Note that running this on a public-facing server that prompts for email -during registration will make the user's email address publicly visible -in their vcard. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_default_vcard/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,30 @@ +--- +labels: +- 'Stage-Beta' +summary: Automatically populate vcard based on account details +... + +Introduction +============ + +It is possible for the user to supply more than just a username and +password when creating an account using +[mod\_register](https://prosody.im/doc/modules/mod_register). This +module automatically copies over that data to the user's vcard. + +Details +======= + +There is no configuration for this module, just add it to +modules\_enabled as normal. + +Note that running this on a public-facing server that prompts for email +during registration will make the user's email address publicly visible +in their vcard. + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_delegation/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'XEP-0355 (Namespace Delegation) implementation' -... - -Introduction -============ - -Namespace Delegation is an extension which allows server to delegate some -features handling to an entity/component. Typical use case is an external PEP -service, but it can be used more generally when your preferred server lack one -internal feature, and you found an external component which can do it. - -Details -======= - -You can have all the details by reading the -[XEP-0355](http://xmpp.org/extensions/xep-0355.html). Only the admin mode is -implemented so far. - -Usage -===== - -To use the module, like usual add **"delegation"** to your *modules\_enabled*. -Note that if you use it with a local component, you also need to activate the -module in your component section: - - modules_enabled = { - [...] - - "delegation"; - } - - [...] - - Component "youcomponent.yourdomain.tld" - component_secret = "yourpassword" - modules_enabled = {"delegation"} - -then specify delegated namespaces **in your host section** like that: - - VirtualHost "yourdomain.tld" - - delegations = { - ["urn:xmpp:mam:2"] = { - filtering = {"node"}; - jid = "pubsub.yourdomain.tld"; - }, - ["http://jabber.org/protocol/pubsub"] = { - jid = "pubsub.yourdomain.tld"; - }, - ["http://jabber.org/protocol/pubsub#owner"] = { - jid = "pubsub.yourdomain.tld"; - }, - ["urn:xmpp:delegation:2:bare:disco#info:*"] = { - jid = "pubsub.yourdomain.tld"; - }, - ["urn:xmpp:delegation:2:bare:disco#items:*"] = { - jid = "pubsub.yourdomain.tld"; - }, - - } - -Here all MAM requests with a "node" attribute (i.e. all MAM pubsub request) will -be delegated to pubsub.yourdomain.tld. Similarly, all pubsub request to the host -(i.e. the PEP requests) will be delegated to pubsub.yourdomain.tld. Check the -XEP for the meaning of "urn:xmpp:delegation:2:bare:disco#info:*" and -"urn:xmpp:delegation:2:bare:disco#items:*". - -**/!\ Be extra careful when you give a delegation to an entity/component, it's a -powerful access, only do it if you absolutely trust the component/entity, and -you know where the software is coming from** - -Configuration -============= - -The configuration is done with a table which map delegated namespace to -namespace data. Namespace data MUST have a **jid** (in the form **jid = -"delegated@domain.tld"**) and MAY have an additional **filtering** array. If -filtering is present, request with attributes in the array will be delegated, -others will be treated normally (i.e. by Prosody). - -If you are not a developer, the delegated namespace(s)/attribute(s) are most -probably specified with the external component/entity you want to use. - -The pseudo-namespace `http://jabber.org/protocol/disco#items:*` is used to -delegate remaining disco#items (i.e. items nodes not already handled by Prosody -itself). - -Compatibility -============= - -If you use it with Prosody 0.9 and a component, you need to patch -core/mod\_component.lua to fire a new signal. To do it, copy the following patch -in a, for example, /tmp/component.patch file: - - diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua - --- a/plugins/mod_component.lua - +++ b/plugins/mod_component.lua - @@ -85,6 +85,7 @@ - session.type = "component"; - module:log("info", "External component successfully authenticated"); - session.send(st.stanza("handshake")); - + module:fire_event("component-authenticated", { session = session }); - - return true; - end - -Then, at the root of prosody, enter: - -`patch -p1 < /tmp/component.patch` - - ----- ---------------------------------------------------- - 0.11 Works - 0.10 Works - 0.9 Need a patched core/mod\_component.lua (see above) - ----- ---------------------------------------------------- - -Note -==== - -This module is often used with mod\_privilege (c.f. XEP for more details)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_delegation/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,123 @@ +--- +labels: +- 'Stage-Beta' +summary: 'XEP-0355 (Namespace Delegation) implementation' +... + +Introduction +============ + +Namespace Delegation is an extension which allows server to delegate some +features handling to an entity/component. Typical use case is an external PEP +service, but it can be used more generally when your preferred server lack one +internal feature, and you found an external component which can do it. + +Details +======= + +You can have all the details by reading the +[XEP-0355](http://xmpp.org/extensions/xep-0355.html). Only the admin mode is +implemented so far. + +Usage +===== + +To use the module, like usual add **"delegation"** to your *modules\_enabled*. +Note that if you use it with a local component, you also need to activate the +module in your component section: + + modules_enabled = { + [...] + + "delegation"; + } + + [...] + + Component "youcomponent.yourdomain.tld" + component_secret = "yourpassword" + modules_enabled = {"delegation"} + +then specify delegated namespaces **in your host section** like that: + + VirtualHost "yourdomain.tld" + + delegations = { + ["urn:xmpp:mam:2"] = { + filtering = {"node"}; + jid = "pubsub.yourdomain.tld"; + }, + ["http://jabber.org/protocol/pubsub"] = { + jid = "pubsub.yourdomain.tld"; + }, + ["http://jabber.org/protocol/pubsub#owner"] = { + jid = "pubsub.yourdomain.tld"; + }, + ["urn:xmpp:delegation:2:bare:disco#info:*"] = { + jid = "pubsub.yourdomain.tld"; + }, + ["urn:xmpp:delegation:2:bare:disco#items:*"] = { + jid = "pubsub.yourdomain.tld"; + }, + + } + +Here all MAM requests with a "node" attribute (i.e. all MAM pubsub request) will +be delegated to pubsub.yourdomain.tld. Similarly, all pubsub request to the host +(i.e. the PEP requests) will be delegated to pubsub.yourdomain.tld. Check the +XEP for the meaning of "urn:xmpp:delegation:2:bare:disco#info:*" and +"urn:xmpp:delegation:2:bare:disco#items:*". + +**/!\ Be extra careful when you give a delegation to an entity/component, it's a +powerful access, only do it if you absolutely trust the component/entity, and +you know where the software is coming from** + +Configuration +============= + +The configuration is done with a table which map delegated namespace to +namespace data. Namespace data MUST have a **jid** (in the form **jid = +"delegated@domain.tld"**) and MAY have an additional **filtering** array. If +filtering is present, request with attributes in the array will be delegated, +others will be treated normally (i.e. by Prosody). + +If you are not a developer, the delegated namespace(s)/attribute(s) are most +probably specified with the external component/entity you want to use. + +The pseudo-namespace `http://jabber.org/protocol/disco#items:*` is used to +delegate remaining disco#items (i.e. items nodes not already handled by Prosody +itself). + +Compatibility +============= + +If you use it with Prosody 0.9 and a component, you need to patch +core/mod\_component.lua to fire a new signal. To do it, copy the following patch +in a, for example, /tmp/component.patch file: + + diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua + --- a/plugins/mod_component.lua + +++ b/plugins/mod_component.lua + @@ -85,6 +85,7 @@ + session.type = "component"; + module:log("info", "External component successfully authenticated"); + session.send(st.stanza("handshake")); + + module:fire_event("component-authenticated", { session = session }); + + return true; + end + +Then, at the root of prosody, enter: + +`patch -p1 < /tmp/component.patch` + + ----- ---------------------------------------------------- + 0.11 Works + 0.10 Works + 0.9 Need a patched core/mod\_component.lua (see above) + ----- ---------------------------------------------------- + +Note +==== + +This module is often used with mod\_privilege (c.f. XEP for more details)
--- a/mod_devices/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Device identification' -... - -Description -============ - -This is an experimental module that aims to identify the different -devices (technically clients) that a user uses with their account. - -It is expected that at some point this will be backed by a nicer protocol, -but it currently uses a variety of hacky methods to track devices between -sessions. - -Usage -===== - -``` {.lua} -modules_enabled = { - -- ... - "devices", - -- ... -} -``` - -Configuration -============= - -Option summary --------------- - - option type default - ------------------------------ ----------------------- ----------- - max\_user\_devices number `5` - - -Compatibility -============= - - ------- ----------------------- - trunk Works - 0.11 Works - 0.10 Does not work - ------- -----------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_devices/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,46 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Device identification' +... + +Description +============ + +This is an experimental module that aims to identify the different +devices (technically clients) that a user uses with their account. + +It is expected that at some point this will be backed by a nicer protocol, +but it currently uses a variety of hacky methods to track devices between +sessions. + +Usage +===== + +``` {.lua} +modules_enabled = { + -- ... + "devices", + -- ... +} +``` + +Configuration +============= + +Option summary +-------------- + + option type default + ------------------------------ ----------------------- ----------- + max\_user\_devices number `5` + + +Compatibility +============= + + ------- ----------------------- + trunk Works + 0.11 Works + 0.10 Does not work + ------- -----------------------
--- a/mod_disable_tls/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Disable TLS on certain client ports -... - -Introduction -============ - -This module can be used to prevent Prosody from offering TLS on client -ports that you specify. This can be useful to work around buggy clients -when transport security is not required. - -Configuration -============= - -Load the module, and set `disable_tls_ports` to a list of ports: - - disable_tls_ports = { 5322 } - -Don't forget to add any extra ports to c2s\_ports, so that Prosody is -actually listening for connections! - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_disable_tls/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +labels: +- 'Stage-Beta' +summary: Disable TLS on certain client ports +... + +Introduction +============ + +This module can be used to prevent Prosody from offering TLS on client +ports that you specify. This can be useful to work around buggy clients +when transport security is not required. + +Configuration +============= + +Load the module, and set `disable_tls_ports` to a list of ports: + + disable_tls_ports = { 5322 } + +Don't forget to add any extra ports to c2s\_ports, so that Prosody is +actually listening for connections! + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_discodot/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -# Flowcharts! - -Put this module somewhere Prosody will find it and then run -`prosodyctl mod_discodot | dot -Tsvg -o disco-graph.svg` to receive a -graph like this[^1]: - - +------------------------+ +------------------------------------------+ - | proxy.external.example | <-- | VirtualHost "example.com" | -+ - +------------------------+ +------------------------------------------+ | - | | - | | - v | - +------------------------------------------+ | - | Component "conference.example.com" "muc" | <+ - +------------------------------------------+ - -Example config for the above: - -``` {.lua} -VirtualHost "xmpp.example.com" -disco_items = { - { "conference.example.com"; }; - { "proxy.external.example"; }; -} - -Component "conference.example.com" "muc" -``` - -Note the `disco_items` entry causing duplication since subdomains are -implicitly added. - -[^1]: this was actuall made with `graph-easy`
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_discodot/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +# Flowcharts! + +Put this module somewhere Prosody will find it and then run +`prosodyctl mod_discodot | dot -Tsvg -o disco-graph.svg` to receive a +graph like this[^1]: + + +------------------------+ +------------------------------------------+ + | proxy.external.example | <-- | VirtualHost "example.com" | -+ + +------------------------+ +------------------------------------------+ | + | | + | | + v | + +------------------------------------------+ | + | Component "conference.example.com" "muc" | <+ + +------------------------------------------+ + +Example config for the above: + +``` {.lua} +VirtualHost "xmpp.example.com" +disco_items = { + { "conference.example.com"; }; + { "proxy.external.example"; }; +} + +Component "conference.example.com" "muc" +``` + +Note the `disco_items` entry causing duplication since subdomains are +implicitly added. + +[^1]: this was actuall made with `graph-easy`
--- a/mod_discoitems/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Manually override the list of service discovery items -... - -Introduction -============ - -This Prosody plugin lets you manually override the service discovery -items for a host. - -Usage -===== - -Simply add `"discoitems"` to your modules\_enabled list. Then add the -`disco_items` option to hosts for which you wish to override the default -response. - -Note: mod\_disco in Prosody 0.8+ supports the `disco_items` option; this -plugin changes the behavior from appending items to replacing items - -Configuration -============= - -The `disco_items` option can be added to relevant hosts: - - disco_items = { - {"proxy.eu.jabber.org", "Jabber.org SOCKS5 service"}; - {"conference.jabber.org", "The Jabber.org MUC"}; - } - -The format for individual items is `{JID, display-name}`. The -display-name can be omitted: `{JID}`. - -Compatibility -============= - - ----- ------------- - 0.8 Works - 0.7 Works - 0.6 Works - 0.5 Should work - ----- -------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_discoitems/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,44 @@ +--- +labels: +- 'Stage-Beta' +summary: Manually override the list of service discovery items +... + +Introduction +============ + +This Prosody plugin lets you manually override the service discovery +items for a host. + +Usage +===== + +Simply add `"discoitems"` to your modules\_enabled list. Then add the +`disco_items` option to hosts for which you wish to override the default +response. + +Note: mod\_disco in Prosody 0.8+ supports the `disco_items` option; this +plugin changes the behavior from appending items to replacing items + +Configuration +============= + +The `disco_items` option can be added to relevant hosts: + + disco_items = { + {"proxy.eu.jabber.org", "Jabber.org SOCKS5 service"}; + {"conference.jabber.org", "The Jabber.org MUC"}; + } + +The format for individual items is `{JID, display-name}`. The +display-name can be omitted: `{JID}`. + +Compatibility +============= + + ----- ------------- + 0.8 Works + 0.7 Works + 0.6 Works + 0.5 Should work + ----- -------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_dnsbl/README.markdown Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,112 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Flag accounts registered by IPs matching blocklists' +depends: + - mod_anti_spam +--- + +This module is designed for servers with public registration enabled, and +makes it easier to identify accounts that have been registered by potentially +"bad" IP addresses, e.g. those that are likely to be used by spam bots. + +**Note:** Running a Prosody instance with public registration enabled opens up +your server as a potential relay for spam and abuse, which can have a negative +impact on your server and the network as a whole. We do not recommended it +unless you have prior experience operating public internet services and are +prepared for the time and effort necessary to tackle any issues. For other +advice, see the Prosody documentation on [public servers](https://prosody.im/doc/public_servers). + +## How does it work? + +When a user account is registered on your server, this module checks the user's +IP address against a list of configured blocklists. If a match is found, it +flags the account using [mod_flags]. + +Flags can be reviewed and managed by using the mod_flags commands and flagged +accounts can be automatically restricted, e.g. by mod_firewall or similar. + +This module supports two kinds of block lists: + +- DNS blocklists (DNSBLs) +- Text files, with one IP/subnet per line + +## Configuration + +**Note:** mod_dnsbl requires mod_anti_spam to be installed, but it does not +need to be enabled or loaded (only some code is shared). mod_flags is also +required, and this will be automatically loaded if not specified in the +config file. + +The main configuration option is `dnsbls`, a list of DNSBL addresses: + +```lua +dnsbls = { + "dnsbl.dronebl.org"; + "cbl.abuseat.org"; +} +``` + +You can set a message to be sent to users who register from a matched IP +address: + +```lua +dnsbl_message = "Your IP address has been detected on a block list. Some functionality may be restricted." +``` + +You can change the default flag that is applied to accounts: + +```lua +dnsbl_flag = "dnsbl_hit" +``` + +### File-based blocklists + +As well as real DNSBLs, you can also put file-based blocklists here, by +prefixing `@` to a filesystem path (Prosody must have read permission to +access the file): + +```lua +dnsbls = { + "dnsbl.dronebl.org"; + "@/etc/prosody/ip_blocklist.txt"; +} +``` + +The file must contain a single IP address or subnet on each line, though blank +lines and comments are ignored. For example: + +``` +# This is a comment +203.0.113.0/24 +2001:db8:7894::/64 +``` + +File-based lists are automatically reloaded when you reload Prosody's +configuration. + +### Advanced configuration + +You can override the flag and message on a per-blocklist basis with a slightly +more detailed configuration syntax: + +```lua +dnsbls = { + ["dnsbl.dronebl.org"] = { + flag = "dnsbl_hit"; + message = "Your account is restricted because your IP address has been detected as running an open proxy. For more information see https://dronebl.org/lookup?ip={registration.ip}"; + }; + ["@/etc/prosody/ip_blocklist.txt"] = { + flag = "local_blocklist"; + message = "Your account is restricted"; + }; +} +``` + +## Compatibility + +Compatible with Prosody 0.12 and later. + +If you are using Prosody 0.12, make sure you install mod_flags from the +community module repository. If you are using a later version, mod_flags is +already included with Prosody.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_dnsbl/mod_dnsbl.lua Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,248 @@ +local lfs = require "lfs"; + +local adns = require "net.adns"; +local it = require "util.iterators"; +local parse_cidr = require "util.ip".parse_cidr; +local parse_ip = require "util.ip".new_ip; +local promise = require "util.promise"; +local set = require "util.set"; +local st = require "util.stanza"; + +local render_message = require "util.interpolation".new("%b{}", function (s) + return s; +end); + +local trie = module:require("mod_anti_spam/trie"); + +local dnsbls_config_raw = module:get_option("dnsbls"); +local default_dnsbl_flag = module:get_option_string("dnsbl_flag", "dnsbl_hit"); +local default_dnsbl_message = module:get_option("dnsbl_message"); + +if not dnsbls_config_raw then + module:log_status("error", "No 'dnsbls' in config file"); + return; +end + +local dnsbls = set.new(); +local dnsbls_config = {}; + +for k, v in ipairs(dnsbls_config_raw) do + local dnsbl_name, dnsbl_config; + if type(k) == "string" then + dnsbl_name = k; + dnsbl_config = v; + else + dnsbl_name = v; + dnsbl_config = {}; + end + dnsbls:add(dnsbl_name); + dnsbls_config[dnsbl_name] = dnsbl_config; +end + +local function read_dnsbl_file(filename) + local t = trie.new(); + local f, err = io.open(filename); + if not f then + module:log("error", "Failed to read file: %s", err); + return t; + end + + local n_line, n_added = 0, 0; + for line in f:lines() do + n_line = n_line + 1; + line = line:gsub("#.+$", ""):match("^%s*(.-)%s*$"); + if line == "" then -- luacheck: ignore 542 + -- Skip + else + local parsed_ip, parsed_bits = parse_cidr(line); + if not parsed_ip then + -- Skip + module:log("warn", "Failed to parse IP/CIDR on %s:%d", filename, n_line); + else + if not parsed_bits then + -- Default to full length of IP address + parsed_bits = #parsed_ip.packed * 8; + end + t:add_subnet(parsed_ip, parsed_bits); + n_added = n_added + 1; + end + end + end + + module:log("info", "Loaded %d entries from %s", n_added, filename); + + return t; +end + +local ipsets = {}; +local ipsets_last_updated = {}; + +function reload_file_dnsbls() + for dnsbl in dnsbls do + if dnsbl:byte(1) == 64 then -- '@' + local filename = dnsbl:sub(2); + local file_last_updated = lfs.attributes(filename, "change"); + if (ipsets_last_updated[dnsbl] or 0) < file_last_updated then + ipsets[dnsbl] = read_dnsbl_file(filename); + ipsets_last_updated[dnsbl] = file_last_updated; + end + end + end +end + +module:hook_global("config-reloaded", reload_file_dnsbls); +reload_file_dnsbls(); + +local mod_flags = module:depends("flags"); + +local function reverse(ip, suffix) + local a,b,c,d = ip:match("^(%d+).(%d+).(%d+).(%d+)$"); + if not a then return end + return ("%d.%d.%d.%d.%s"):format(d,c,b,a, suffix); +end + +function check_dnsbl(ip_address, dnsbl, callback, ud) + if dnsbl:byte(1) == 64 then -- '@' + local parsed_ip = parse_ip(ip_address); + if not parsed_ip then + module:log("warn", "Failed to parse IP address: %s", ip_address); + callback(ud, false, dnsbl); + return; + end + callback(ud, not not ipsets[dnsbl]:contains_ip(parsed_ip), dnsbl); + return; + else + if ip_address:sub(1,7):lower() == "::ffff:" then + ip_address = ip_address:sub(8); + end + + local rbl_ip = reverse(ip_address, dnsbl); + if not rbl_ip then return; end + + module:log("debug", "Sending DNSBL lookup for %s", ip_address); + adns.lookup(function (reply) + local hit = not not (reply and reply[1]); + module:log("debug", "Received DNSBL result for %s: %s", ip_address, hit and "present" or "absent"); + callback(ud, hit, dnsbl); + end, rbl_ip); + end +end + +local function handle_dnsbl_register_result(registration_event, hit, dnsbl) + if not hit then return; end + + if registration_event.dnsbl_match then return; end + registration_event.dnsbl_match = true; + + local username = registration_event.username; + local flag = dnsbls_config[dnsbl].flag or default_dnsbl_flag; + + module:log("info", "Flagging %s for user %s registered from %s matching %s", flag, username, registration_event.ip, dnsbl); + + mod_flags:add_flag(username, flag, "Matched "..dnsbl); + + local msg = dnsbls_config[dnsbl].message or default_dnsbl_message; + + if msg then + module:log("debug", "Sending warning message to %s", username); + local msg_stanza = st.message( + { + to = username.."@"..module.host; + from = module.host; + }, + render_message(msg, { registration = registration_event }) + ); + module:send(msg_stanza); + end +end + +module:hook("user-registered", function (event) + local session = event.session; + local ip = event.ip or (session and session.ip); + if not ip then return; end + + if not event.ip then + event.ip = ip; + end + + for dnsbl in dnsbls do + check_dnsbl(ip, dnsbl, handle_dnsbl_register_result, event); + end +end); + +module:add_item("account-trait", { + name = "register-dnsbl-hit"; + prob_bad_true = 0.6; + prob_bad_false = 0.4; +}); + +module:hook("get-account-traits", function (event) + event.traits["register-dnsbl-hit"] = mod_flags.has_flag(event.username, default_dnsbl_flag); +end); + +module:add_item("shell-command", { + section = "dnsbl"; + section_desc = "Manage DNS blocklists"; + name = "lists"; + desc = "Show all lists currently in use on the specified host"; + args = { + { name = "host", type = "string" }; + }; + host_selector = "host"; + handler = function(self, host) --luacheck: ignore 212/self 212/host + local count = 0; + for list in dnsbls do + count = count + 1; + self.session.print(list); + end + return true, ("%d lists"):format(count); + end; +}); + +module:add_item("shell-command", { + section = "dnsbl"; + section_desc = "Manage DNS blocklists"; + name = "check"; + desc = "Check an IP against the configured block lists"; + args = { + { name = "host", type = "string" }; + { name = "ip_address", type = "string" }; + }; + host_selector = "host"; + handler = function(self, host, ip_address) --luacheck: ignore 212/self 212/host + local parsed_ip = parse_ip(ip_address); + if not parsed_ip then + return false, "Failed to parse IP address"; + end + + local matches, total = 0, 0; + + local promises = {}; + + for dnsbl in dnsbls do + total = total + 1; + promises[dnsbl] = promise.new(function (resolve) + check_dnsbl(parsed_ip, dnsbl, resolve, true); + end); + end + + return promise.all_settled(promises):next(function (results) + for dnsbl, result in it.sorted_pairs(results) do + local msg; + if result.status == "fulfilled" then + if result.value then + msg = "[X]"; + matches = matches + 1; + else + msg = "[ ]"; + end + else + msg = "[?]"; + end + + print(msg, dnsbl); + end + return ("Found in %d of %d lists"):format(matches, total); + end); + end; +});
--- a/mod_dnsupdate/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Generate a DNS UPDATE order in format suitable for `nsupdate` based on -current port configuration. - -Example output: - -``` -$ prosodyctl mod_dnsupdate -d example.com -t xmpp.example.com example.com -zone example.com -server ns1.example.com -ttl 3600 -add _xmpp-client._tcp.example.com IN SRV 1 1 5222 -add _xmpp-server._tcp.example.com IN SRV 1 1 5269 -``` -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_dnsupdate/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,14 @@ +Generate a DNS UPDATE order in format suitable for `nsupdate` based on +current port configuration. + +Example output: + +``` +$ prosodyctl mod_dnsupdate -d example.com -t xmpp.example.com example.com +zone example.com +server ns1.example.com +ttl 3600 +add _xmpp-client._tcp.example.com IN SRV 1 1 5222 +add _xmpp-server._tcp.example.com IN SRV 1 1 5269 +``` +
--- a/mod_dwd/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ ---- -summary: 'Dialback-without-Dialback' -... - -Introduction -============ - -This module implements an optimization of the Dialback protocol, by -skipping the dialback step for servers presenting a valid certificate. - -Configuration -============= - -Simply add the module to the `modules_enabled` list. - - modules_enabled = { - ... - "dwd"; - } - -Compatibility -============= - - ------------------ -------------------------- - 0.10 Built into mod\_dialback - 0.9 + LuaSec 0.5 Works - 0.8 Doesn't work - ------------------ --------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_dwd/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,28 @@ +--- +summary: 'Dialback-without-Dialback' +... + +Introduction +============ + +This module implements an optimization of the Dialback protocol, by +skipping the dialback step for servers presenting a valid certificate. + +Configuration +============= + +Simply add the module to the `modules_enabled` list. + + modules_enabled = { + ... + "dwd"; + } + +Compatibility +============= + + ------------------ -------------------------- + 0.10 Built into mod\_dialback + 0.9 + LuaSec 0.5 Works + 0.8 Doesn't work + ------------------ --------------------------
--- a/mod_e2e_policy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -Introduction -============ - -This module was written to encourage usage of End-to-end encryption for chat and MUC messages. It can be configured to warn the sender after every plaintext/unencrypted message or to block all plaintext/unencrypted messages. It also supports MUC and JID whitelisting, so administrators can for example whitelist public support MUCs ;-) - -Configuration -============= - -Enable the module as any other: - - modules_enabled = { - "e2e_policy"; - } - -You can then set some options to configure your desired policy: - - Option Default Description - ------------------------------------ ------------ ------------------------------------------------------------------------------------------------------------------------------------------------- - e2e\_policy\_chat `"optional"` Policy for chat messages. Possible values: `"none"`, `"optional"` and `"required"`. - e2e\_policy\_muc `"optional"` Policy for MUC messages. Possible values: `"none"`, `"optional"` and `"required"`. - e2e\_policy\_whitelist `{ }` Make this module ignore messages sent to and from this JIDs or MUCs. - e2e\_policy\_message\_optional\_chat `""` Set a custom warning message for chat messages. - e2e\_policy\_message\_required\_chat `""` Set a custom error message for chat messages. - e2e\_policy\_message\_optional\_muc `""` Set a custom warning message for MUC messages. - e2e\_policy\_message\_required\_muc `""` Set a custom error message for MUC messages. - -Some examples: - - e2e_policy_chat = "optional" - e2e_policy_muc = "optional" - e2e_policy_whitelist = { "admin@example.com", "prosody@conference.prosody.im" } - e2e_policy_message_optional_chat = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for conversations on this server." - e2e_policy_message_required_chat = "For security reasons, OMEMO, OTR or PGP encryption is required for conversations on this server." - e2e_policy_message_optional_muc = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for MUC on this server." - e2e_policy_message_required_muc = "For security reasons, OMEMO, OTR or PGP encryption is required for MUC on this server." - -Compatibility -============= - - ----- ------------- - trunk Works - 0.10 Should work - 0.9 Should work - ----- ------------- - -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_e2e_policy/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,46 @@ +Introduction +============ + +This module was written to encourage usage of End-to-end encryption for chat and MUC messages. It can be configured to warn the sender after every plaintext/unencrypted message or to block all plaintext/unencrypted messages. It also supports MUC and JID whitelisting, so administrators can for example whitelist public support MUCs ;-) + +Configuration +============= + +Enable the module as any other: + + modules_enabled = { + "e2e_policy"; + } + +You can then set some options to configure your desired policy: + + Option Default Description + ------------------------------------ ------------ ------------------------------------------------------------------------------------------------------------------------------------------------- + e2e\_policy\_chat `"optional"` Policy for chat messages. Possible values: `"none"`, `"optional"` and `"required"`. + e2e\_policy\_muc `"optional"` Policy for MUC messages. Possible values: `"none"`, `"optional"` and `"required"`. + e2e\_policy\_whitelist `{ }` Make this module ignore messages sent to and from this JIDs or MUCs. + e2e\_policy\_message\_optional\_chat `""` Set a custom warning message for chat messages. + e2e\_policy\_message\_required\_chat `""` Set a custom error message for chat messages. + e2e\_policy\_message\_optional\_muc `""` Set a custom warning message for MUC messages. + e2e\_policy\_message\_required\_muc `""` Set a custom error message for MUC messages. + +Some examples: + + e2e_policy_chat = "optional" + e2e_policy_muc = "optional" + e2e_policy_whitelist = { "admin@example.com", "prosody@conference.prosody.im" } + e2e_policy_message_optional_chat = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for conversations on this server." + e2e_policy_message_required_chat = "For security reasons, OMEMO, OTR or PGP encryption is required for conversations on this server." + e2e_policy_message_optional_muc = "For security reasons, OMEMO, OTR or PGP encryption is STRONGLY recommended for MUC on this server." + e2e_policy_message_required_muc = "For security reasons, OMEMO, OTR or PGP encryption is required for MUC on this server." + +Compatibility +============= + + ----- ------------- + trunk Works + 0.10 Should work + 0.9 Should work + ----- ------------- + +
--- a/mod_easy_invite/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ ---- -labels: -- 'Stage-Deprecated' -summary: 'Invite management module for Prosody (deprecated)' -rockspec: - dependencies: - - mod_invites ---- - -::: {.alert .alert-warning} -**NOTE:** This module has been deprecated. Its functionality has been -moved to other modules, see the mod_invites documentation for details. -::: - - -This module allows admins and users to create invitations suitable for sharing -to potential new users/contacts. - -User invitations can be created through the "New Invite" ad-hoc command. An overview -of the semantics and protocol can be found at [modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites/). - -This module depends on mod_invites to actually create and store the invitation tokens. - -# Configuration - -To allow users to join your server through invitations, you must -enable mod_register_ibr and set allow_registration = true, and then -also set `registration_invite_only = true` to restrict registration. - -| Name | Description | Default | -|--------------------------|-----------------------------------------------------------------------------------|---------| -| registration_invite_only | Whether registration attempts without an invite token should be blocked | true | -| allow_user_invites | Whether existing users should be allowed to invite new users to register accounts | true | - -## Example: Invite-only registration -``` {.lua} --- To allow invitation through a token, mod_register -allow_registration = true -registration_invite_only = true -``` - -## Example: Open registration - -This setup allows completely open registration, even without -an invite token. - -``` {.lua} -allow_registration = true -registration_invite_only = false -``` - -## Invite creation permissions - -To allow existing users of your server to send invitation links that -allow new people to join your server, you can set `allow_user_invites = true`. - -If you do not wish users to invite other users to create accounts on your -server, set `allow_user_invites = false`. They will still be able to send -contact invites, but new contacts will be required to register an account -on a different server. - -# Usage - -Users can use the "New Invite" ad-hoc command through their client. - -Admins can create registration links using prosodyctl, e.g. - -``` -prosodyctl mod_easy_invite example.com generate -``` - -# Compatibility - -0.11 and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_easy_invite/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,74 @@ +--- +labels: +- 'Stage-Deprecated' +summary: 'Invite management module for Prosody (deprecated)' +rockspec: + dependencies: + - mod_invites +--- + +::: {.alert .alert-warning} +**NOTE:** This module has been deprecated. Its functionality has been +moved to other modules, see the mod_invites documentation for details. +::: + + +This module allows admins and users to create invitations suitable for sharing +to potential new users/contacts. + +User invitations can be created through the "New Invite" ad-hoc command. An overview +of the semantics and protocol can be found at [modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites/). + +This module depends on mod_invites to actually create and store the invitation tokens. + +# Configuration + +To allow users to join your server through invitations, you must +enable mod_register_ibr and set allow_registration = true, and then +also set `registration_invite_only = true` to restrict registration. + +| Name | Description | Default | +|--------------------------|-----------------------------------------------------------------------------------|---------| +| registration_invite_only | Whether registration attempts without an invite token should be blocked | true | +| allow_user_invites | Whether existing users should be allowed to invite new users to register accounts | true | + +## Example: Invite-only registration +``` {.lua} +-- To allow invitation through a token, mod_register +allow_registration = true +registration_invite_only = true +``` + +## Example: Open registration + +This setup allows completely open registration, even without +an invite token. + +``` {.lua} +allow_registration = true +registration_invite_only = false +``` + +## Invite creation permissions + +To allow existing users of your server to send invitation links that +allow new people to join your server, you can set `allow_user_invites = true`. + +If you do not wish users to invite other users to create accounts on your +server, set `allow_user_invites = false`. They will still be able to send +contact invites, but new contacts will be required to register an account +on a different server. + +# Usage + +Users can use the "New Invite" ad-hoc command through their client. + +Admins can create registration links using prosodyctl, e.g. + +``` +prosodyctl mod_easy_invite example.com generate +``` + +# Compatibility + +0.11 and later.
--- a/mod_email_pass/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ ---- -labels: -- 'Stage-Beta' -... - -Introduction -============ - -This module aims to help in the procedure of user password restoration. -To start the restoration, the user must go to an URL provided by this -module, fill the JID and email and submit the request. - -The module will generate a token valid for 24h and send an email with a -specially crafted url to the vCard email address. If the user goes to -this url, will be able to change his password. - -Usage -===== - -Simply add "email\_pass" to your modules\_enabled list and copy files -"**mod\_email\_pass.lua**" and "**vcard.lib.lua**" to prosody modules -folder. This module need to that **https\_host** or **http\_host** must -be configured. This parameter is necessary to construct the URL that has -been sended to the user. - -This module only send emails to the vCard user email address, then the -user must set this address in order to be capable of do the restoration. - -Configuration -============= - - --------------- ------------------------------------------------------------ - smtp\_server The Host/ip of your SMTP server - smtp\_port Port used by your SMTP server. Default 25 - smtp\_ssl Use of SMTP SSL legacy (No STARTTLS) - smtp\_user Username used to do SMTP auth - smtp\_pass Password used to do SMTP auth - smtp\_address EMail address that will be apears as From in mails - msg\_subject Subject used for messages/mails - msg\_body Message send when password has been changed - url\_path Path where the module will be visible. Default /resetpass/ - --------------- ------------------------------------------------------------ - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_email_pass/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,49 @@ +--- +labels: +- 'Stage-Beta' +... + +Introduction +============ + +This module aims to help in the procedure of user password restoration. +To start the restoration, the user must go to an URL provided by this +module, fill the JID and email and submit the request. + +The module will generate a token valid for 24h and send an email with a +specially crafted url to the vCard email address. If the user goes to +this url, will be able to change his password. + +Usage +===== + +Simply add "email\_pass" to your modules\_enabled list and copy files +"**mod\_email\_pass.lua**" and "**vcard.lib.lua**" to prosody modules +folder. This module need to that **https\_host** or **http\_host** must +be configured. This parameter is necessary to construct the URL that has +been sended to the user. + +This module only send emails to the vCard user email address, then the +user must set this address in order to be capable of do the restoration. + +Configuration +============= + + --------------- ------------------------------------------------------------ + smtp\_server The Host/ip of your SMTP server + smtp\_port Port used by your SMTP server. Default 25 + smtp\_ssl Use of SMTP SSL legacy (No STARTTLS) + smtp\_user Username used to do SMTP auth + smtp\_pass Password used to do SMTP auth + smtp\_address EMail address that will be apears as From in mails + msg\_subject Subject used for messages/mails + msg\_body Message send when password has been changed + url\_path Path where the module will be visible. Default /resetpass/ + --------------- ------------------------------------------------------------ + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_extdisco/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -summary: External Service Discovery -... - -Introduction -============ - -This module adds support for [XEP-0215: External Service Discovery], -which lets Prosody advertise non-XMPP services. - -Configuration -============= - -Example services from the XEP: - -``` {.lua} -modules_enabled = { - -- other modules ... - "extdisco"; -} - -external_services = { - ["stun.shakespeare.lit"] = { - port="9998"; - transport="udp"; - type="stun"; - }; - ["relay.shakespeare.lit"] = { - password="jj929jkj5sadjfj93v3n"; - port="9999"; - transport="udp"; - type="turn"; - username="nb78932lkjlskjfdb7g8"; - }; - ["192.0.2.1"] = { - port="8888"; - transport="udp"; - type="stun"; - }; - ["192.0.2.1"] = { - port="8889"; - password="93jn3bakj9s832lrjbbz"; - transport="udp"; - type="turn"; - username="auu98sjl2wk3e9fjdsl7"; - }; - ["ftp.shakespeare.lit"] = { - name="Shakespearean File Server"; - password="guest"; - port="20"; - transport="tcp"; - type="ftp"; - username="guest"; - }; -} -``` - -Compatibility -============= - -Incompatible with -[mod_turncredentials](https://modules.prosody.im/mod_turncredentials).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_extdisco/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,62 @@ +--- +summary: External Service Discovery +... + +Introduction +============ + +This module adds support for [XEP-0215: External Service Discovery], +which lets Prosody advertise non-XMPP services. + +Configuration +============= + +Example services from the XEP: + +``` {.lua} +modules_enabled = { + -- other modules ... + "extdisco"; +} + +external_services = { + ["stun.shakespeare.lit"] = { + port="9998"; + transport="udp"; + type="stun"; + }; + ["relay.shakespeare.lit"] = { + password="jj929jkj5sadjfj93v3n"; + port="9999"; + transport="udp"; + type="turn"; + username="nb78932lkjlskjfdb7g8"; + }; + ["192.0.2.1"] = { + port="8888"; + transport="udp"; + type="stun"; + }; + ["192.0.2.1"] = { + port="8889"; + password="93jn3bakj9s832lrjbbz"; + transport="udp"; + type="turn"; + username="auu98sjl2wk3e9fjdsl7"; + }; + ["ftp.shakespeare.lit"] = { + name="Shakespearean File Server"; + password="guest"; + port="20"; + transport="tcp"; + type="ftp"; + username="guest"; + }; +} +``` + +Compatibility +============= + +Incompatible with +[mod_turncredentials](https://modules.prosody.im/mod_turncredentials).
--- a/mod_external_services/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ ---- -summary: External Service Discovery -... - -This module implementing [XEP-0215] is included with Prosody 0.12 but made available here for older versions. - -Documentation is available on the [Prosody site][doc:modules:mod_external_services].
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_external_services/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,7 @@ +--- +summary: External Service Discovery +... + +This module implementing [XEP-0215] is included with Prosody 0.12 but made available here for older versions. + +Documentation is available on the [Prosody site][doc:modules:mod_external_services].
--- a/mod_file_management/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ ---- -description: File management for uploaded files -labels: -- Stage-Alpha ---- - -Introduction -============ - -This module exposes ad-hoc commands [XEP-0050] for listing uploaded files, and -later for managing them. - -Configuration -============= - -This module depends on mod\_http\_upload, and exposes ad-hoc commands for each -operation a user might do on their uploaded files. - -The module can be added to the `modules_enabled` field on a host on which -mod\_http\_upload is loaded. - -Compatibility -============= - -Works with Prosody trunk at least.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_file_management/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,25 @@ +--- +description: File management for uploaded files +labels: +- Stage-Alpha +--- + +Introduction +============ + +This module exposes ad-hoc commands [XEP-0050] for listing uploaded files, and +later for managing them. + +Configuration +============= + +This module depends on mod\_http\_upload, and exposes ad-hoc commands for each +operation a user might do on their uploaded files. + +The module can be added to the `modules_enabled` field on a host on which +mod\_http\_upload is loaded. + +Compatibility +============= + +Works with Prosody trunk at least.
--- a/mod_filter_chatstates/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -summary: Drop chat states from messages to inactive sessions -... - -Introduction -============ - -Some mobile XMPP client developers consider [Chat State -Notifications](http://xmpp.org/extensions/xep-0085.html) to be a waste -of power and bandwidth, especially when the user is not actively looking -at their device. This module will filter them out while the session is -considered inactive. It depends on [mod\_csi](/mod_csi.html) for -deciding when to begin and end filtering. - -Configuration -============= - -There is no configuration for this module, just add it to -modules\_enabled as normal. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_filter_chatstates/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,33 @@ +--- +summary: Drop chat states from messages to inactive sessions +... + +::: {.alert .alert-info} +This module discards certain kinds of stanzas that are unnecessary to deliver to inactive clients. This is technically against the XMPP specification, and has the potential to cause bugs. However it is being used by some people successfully, and reduces the overall bandwidth usage for mobile devices. +On the other hand it does not save battery usage in a relevant way compared to other `csi` modules. +Consider using [mod_csi_simple][doc:modules:mod_csi_simple] that is incuded in prosody since Version 0.11. +::: + +Introduction +============ + +Some mobile XMPP client developers consider [Chat State +Notifications](http://xmpp.org/extensions/xep-0085.html) to be a waste +of power and bandwidth, especially when the user is not actively looking +at their device. This module will filter them out while the session is +considered inactive. It depends on [mod\_csi](/mod_csi.html) for +deciding when to begin and end filtering. + +Configuration +============= + +There is no configuration for this module, just add it to +modules\_enabled as normal. + +Compatibility +============= + + ----- ------- + 0.11 Works + 0.10 Works + ----- -------
--- a/mod_firewall/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,820 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'A rule-based stanza filtering module' -rockspec: - build: - modules: - mod_firewall.actions: actions.lib.lua - mod_firewall.conditions: conditions.lib.lua - mod_firewall.definitions: definitions.lib.lua - mod_firewall.marks: marks.lib.lua - mod_firewall.test: test.lib.lua - copy_directories: - - scripts ---- - ------------------------------------------------------------------------- - -**Note:** mod\_firewall is in its very early stages. This documentation -is liable to change, and some described functionality may be missing, -incomplete or contain bugs. - ------------------------------------------------------------------------- - -Introduction -============ - -A firewall is an invaluable tool in the sysadmin's toolbox. However -while low-level firewalls such as iptables and pf are incredibly good at -what they do, they are generally not able to handle application-layer -rules. - -The goal of mod\_firewall is to provide similar services at the XMPP -layer. Based on rule scripts it can efficiently block, bounce, drop, -forward, copy, redirect stanzas and more! Furthermore all rules can be -applied and updated dynamically at runtime without restarting the -server. - -Details -======= - -mod\_firewall loads one or more scripts, and compiles these to Lua code -that reacts to stanzas flowing through Prosody. The firewall script -syntax is unusual, but straightforward. - -A firewall script is dominated by rules. Each rule has two parts: -conditions, and actions. When a stanza matches all of the conditions, -all of the actions are executed in order. - -Here is a simple example to block stanzas from spammer@example.com: - - FROM: spammer@example.com - DROP. - -FROM is a condition, and DROP is an action. This is about as simple as -it gets. How about heading to the other extreme? Let's demonstrate -something more complex that mod\_firewall can do for you: - - %ZONE myorganisation: staff.myorg.example, support.myorg.example - - ENTERING: myorganisation - KIND: message - TIME: 12am-9am, 5pm-12am, Saturday, Sunday - REPLY=Sorry, I am afraid our office is closed at the moment. If you need assistance, please call our 24-hour support line on 123-456-789. - -This rule will reply with a short message whenever someone tries to send -a message to someone at any of the hosts defined in the 'myorganisation' -outside of office hours. - -Specifying rule sets --------------------- - -Firewall rules should be written into text files, e.g. `ruleset.pfw` file. -One or more rule files can be specified in the configuration using: - - firewall_scripts = { "path/to/ruleset.pfw", "path/to/ruleset2.pfw" } - -If multiple files are specified and they both add rules to the same [chains](#chains), -each file's rules will be processed in order, but the order of files is undefined. - -Reloading Prosody's configuration also reloads firewall rules. - -Make sure that `firewall_scripts` is in the global section of the configuration file -and not below a virtual host or a component - unless you want per-vhost -firewall rules. - -Conditions ----------- - -All conditions must come before any action in a rule block. The -condition name is followed by a colon (':'), and the value to test for. - -A condition can be preceded or followed by `NOT` to negate its match. -For example: - - NOT FROM: user@example.com - KIND NOT: message - -Some conditions do not take parameters, and these should end with just a -question mark, like: - - IN ROSTER? - -### Zones - -A 'zone' is one or more hosts or JIDs. It is possible to match when a -stanza is entering or leaving a zone, while at the same time not -matching traffic passing between JIDs in the same zone. - -Zones are defined at the top of a script with the following syntax (they -are not part of a rule block): - - %ZONE myzone: host1, host2, user@host3, foo.bar.example - -There is an automatic zone named `$local`, which automatically includes -all of the current server's active hosts (including components). It can -be used to match stanzas entering or leaving the current server. - -A host listed in a zone also matches all users on that host (but not -subdomains). - -The following zone-matching conditions are supported: - - Condition Matches - ------------ ------------------------------------------ - `ENTERING` When a stanza is entering the named zone - `LEAVING` When a stanza is leaving the named zone - -### Lists - -It is possible to create or load lists of strings for use in scripts. For -example, you might load a list of blocked JIDs, malware URLs or simple words -that you want to filter messages on. - - List type Example - ----------- ----------------------- - memory %LIST spammers: memory - file %LIST spammers: file:/etc/spammers.txt - http %LIST spammers: http://example.com/spammers.txt - -#### List types -##### memory - -``` -%LIST name: memory (limit: number) -``` - -A memory-only list, with an optional limit. Supports addition and removal of items by scripts. - -If a limit is provided, the oldest item will be discarded to make room for a new item if the -list is full. The limit is useful to prevent infinite memory growth on busy servers. - -##### file - -``` -%LIST name: file:/path/to/file (missing: string) -``` - -Reads a list from a file. The list can be added to and removed from by scripts, but -these changes do not persist between restarts. - -If the file is missing, an error will be raised. The optional 'missing' parameter can be set -to 'ignore' (e.g. `(missing: ignore)`) to ignore a missing file. - -##### http - -``` -%LIST name: http://example.com/ (ttl: number, pattern: pat, hash: sha1, checkcerts: when-sni) -``` - -Fetches a list from a HTTP or HTTPS URL. The following options are accepted: - - Option Description - ------- ----------- - ttl Seconds to cache the list for. After expiry, it will be refetched. Default 3600 (1 hour). - pattern Optional pattern used to extract list entries from the response. Default is to treat each line as a single item. - hash Optional hash to be applied to items before looking them up in the list, e.g. sha1 or sha256. - checkcert Whether to verify HTTPS certificates. May be "always", "never" or "when-sni". Default "when-sni". - -The "when-sni" default disables certificate verification when Prosody's HTTP client API doesn't support SNI, -as in Prosody 0.11.6 and earlier. - -#### CHECK LIST - -Checks whether a simple [expression](#expressions) is found in a given list. - -Example: - - %LIST blocked_jids: file:/etc/prosody/blocked_jids.txt - - # Rule to block presence subscription requests from blocked JIDs - KIND: presence - TYPE: subscribe - CHECK LIST: blocked_jids contains $<@from> - BOUNCE=policy-violation (Your JID is blocked) - -#### SCAN - -SCAN allows you to search inside a stanza for a given pattern, and check each result against a list. For example, -you could scan a message body for words and check if any of the words are found in a given list. - -Before using SCAN, you need to define a search location and a pattern. The search location uses the same 'path' -format as documented under the 'INSPECT' condition. Patterns can be any valid Lua pattern. - -To use the above example: - - # Define a search location called 'body' which fetches the text of the 'body' element - %SEARCH body: body# - # Define a pattern called 'word' which matches any sequence of letters - %PATTERN word: [A-Za-z]+ - # Finally, we also need our list of "bad" words: - %LIST badwords: file:/etc/prosody/bad_words.txt - - # Now we can use these to SCAN incoming stanzas - # If it finds a match, bounce the stanza - SCAN: body for word in badwords - BOUNCE=policy-violation (This word is not allowed!) - -#### COUNT - -COUNT is similar to SCAN, in that it uses a defined SEARCH and breaks it up according to a PATTERN. Then it -counts the number of results. - -For example, to block every message with more than one URL: - - # Define a search location called 'body' which fetches the text of the 'body' element - %SEARCH body: body# - # Define a pattern called 'url' which matches HTTP links - %PATTERN url: https?://%S+ - - COUNT: url in body > 1 - BOUNCE=policy-violation (Up to one HTTP URL is allowed in messages) - -### Stanza matching - - Condition Matches - ----------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ - `KIND` The kind of stanza. May be 'message', 'presence' or 'iq' - `TYPE` The type of stanza. This varies depending on the kind of stanza. See 'Stanza types' below for more information. - `PAYLOAD` The stanza contains a child with the given namespace. Useful for determining the type of an iq request, or whether a message contains a certain extension. - `INSPECT` The node at the specified path exists or matches a given string. This allows you to look anywhere inside a stanza. See below for examples and more. - -#### Stanza types - - Stanza Valid types - ---------- ------------------------------------------------------------------------------------------ - iq get, set, result, error - presence *available*, unavailable, probe, subscribe, subscribed, unsubscribe, unsubscribed, error - message normal, chat, groupchat, headline, error - -**Note:** The type 'available' for presence does not actually appear in -the protocol. Available presence is signalled by the omission of a type. -Similarly, a message stanza with no type is equivalent to one of type -'normal'. mod\_firewall handles these cases for you automatically. - -#### Sender/recipient matching - - Condition Matches - --------------- ------------------------------------------------------- - `FROM` The JID in the 'from' attribute matches the given JID. - `TO` The JID in the 'to' attribute matches the given JID. - `TO SELF` The stanza is sent by any of a user's resources to their own bare JID. - `TO FULL JID` The stanza is addressed to a **valid** full JID on the local server (full JIDs include a resource at the end, and only exist for the lifetime of a single session, therefore the recipient **must be online**, or this check will not match). - `FROM FULL JID` The stanza is from a full JID (unlike `TO FULL JID` this check is on the format of the JID only). - -The TO and FROM conditions both accept wildcards in the JID when it is -enclosed in angle brackets ('\<...\>'). For example: - - # All users at example.com - FROM: <*>@example.com - - # The user 'admin' on any subdomain of example.com - FROM: admin@<*.example.com> - -You can also use [Lua's pattern -matching](http://www.lua.org/manual/5.1/manual.html#5.4.1) for more -powerful matching abilities. Patterns are a lightweight -regular-expression alternative. Simply contain the pattern in double -angle brackets. The pattern is automatically anchored at the start and -end (so it must match the entire portion of the JID). - - # Match admin@example.com, and admin1@example.com, etc. - FROM: <<admin%d*>>@example.com - -**Note:** It is important to know that 'example.com' is a valid JID on -its own, and does **not** match 'user@example.com'. To efficiently match -domains we recommend defining them as [Zones](#zones). - - Condition Matches - ---------------- --------------------------------------------------------------- - `FROM_EXACTLY` The JID in the 'from' attribute exactly matches the given JID - `TO_EXACTLY` The JID in the 'to' attribute exactly matches the given JID - -These additional conditions do not support pattern matching, but are -useful to match the exact to/from address on a stanza. For example, if -no resource is specified then only bare JIDs will be matched. TO and FROM -match all resources if no resource is specified to match. - -**Note:** Some chains execute before Prosody has performed any -normalisation or validity checks on the to/from JIDs on an incoming -stanza. It is not advisable to perform access control or similar rules -on JIDs in these chains (see the [chain documentation](#chains) for more info). - -#### GeoIP matching - - Condition Matches - ---------------- -------------------------------------------------------------- - `FROM COUNTRY` Two or three letter country code looked up in GeoIP database - -This condition uses a GeoIP database to look up the origin country of -the IP attached to the current session. - -For example: - - # 3 letter country code - FROM COUNTRY: SWE - - # or 2 letter - FROM COUNTRY: SE - - # Explicit - FROM COUNTRY: code=SE - FROM COUNTRY: code3=SWE - -**Note:** This requires that the `lua-geoip` and `geoip-database` -packages are installed (on Debian, package names may differ on other -operating systems). - -#### INSPECT - -INSPECT takes a 'path' through the stanza to get a string (an attribute -value or text content). An example is the best way to explain. Let's -check that a user is not trying to register an account with the username -'admin'. This stanza comes from [XEP-0077: In-band -Registration](http://xmpp.org/extensions/xep-0077.html#example-4): - -``` xml -<iq type='set' id='reg2'> - <query xmlns='jabber:iq:register'> - <username>bill</username> - <password>Calliope</password> - <email>bard@shakespeare.lit</email> - </query> -</iq> -``` - - KIND: iq - TYPE: set - PAYLOAD: jabber:iq:register - INSPECT: {jabber:iq:register}query/username#=admin - BOUNCE=not-allowed (The username 'admin' is reserved.) - -That weird string deserves some explanation. It is a path, divided into -segments by '/'. Each segment describes an element by its name, -optionally prefixed by its namespace in curly braces ('{...}'). If the -path ends with a '\#' then the text content of the last element will be -returned. If the path ends with '@name' then the value of the attribute -'name' will be returned. - -You can use INSPECT to test for the existence of an element or attribute, -or you can check if it matches a specific value, e.g. by appending `=VALUE` -(like in the example above, that checks if the content of username is 'admin'). - -#### INSPECT comparison operators - -As well as checking for an exact string match, there are some other modifiers -you can apply to the comparison: - - Comparison Matches when - ------------- ------------------------------------------------------- - `=` The value is exactly the given string. - `/=` The value is or *contains* the given string (e.g. `/=admin` would match `administrator` or `myadmin`). - `~=` The value matches the given [Lua pattern](https://www.lua.org/manual/5.2/manual.html#6.4.1). - -Finally, if the comparison operator is preceded by a `$` character, [expressions](#expressions) -will be interpreted in the string following the comparison operator. - -e.g. `INSPECT: {jabber:iq:register}query/username}$/=$(session.host)` would match -if the username of an account registration contained the session's current hostname -somewhere in it. - -#### INSPECT performance - -INSPECT can be somewhat slower than the other stanza matching conditions. To -minimise performance impact, always place it below other faster -condition checks where possible (e.g. in the example above we first checked KIND, -TYPE and PAYLOAD matched what we wanted before reaching the INSPECT rule). - -### Roster - -These conditions access the roster of the recipient (only). Therefore they cannot (currently) -be used in some [chains](#chains), such as for outgoing messages (the recipient may be on another server). - -Performance note: these checks can potentially cause storage access (especially if the recipient -is currently offline), so you may want to limit their use in high-traffic situations, and place rules -containing a roster check below other rules (such as a rate limiter). The storage access is -performed unconditionally just before evaluation of the first rule that contains a roster-based -condition, even if earlier conditions in that rule do not match. - -#### IN ROSTER - -Tests whether the sender is in the recipient's roster. - - IN ROSTER? - -#### IN ROSTER GROUP - -Tests whether the sender is in the recipient's roster, and in the named group. - - IN ROSTER GROUP: Friends - -#### SUBSCRIBED - -Tests whether the recipient is subscribed to the sender, ie will receive -presence updates from them. - -Note that this *does* work, regardless of direction and which [chain](#chain) is -used, since both the sender and the recipient will have mirrored roster -entries. - -### Groups - -Using Prosody's mod\_groups it is possible to define groups of users on the server. You can -match based on these groups in firewall rules. - - Condition Matches - ----------------- ---------------------------- - `FROM GROUP` When the stanza is being sent from a member of the named group - `TO GROUP` When the stanza is being sent to a member of the named group - `CROSSING GROUPS` When the stanza is being sent between users of different named groups - -#### CROSSING GROUPS - -The `CROSSING GROUPS` condition takes a comma-separated list of groups to check. If the -sender and recipient are not in the same group (only the listed groups are checked), then the -this condition matches and the stanza is deemed to be crossing between groups. - -For example, if you had three groups: Engineering, Marketing and Employees. All users are -members of the 'Employees' group, and the others are for employees of the named department only. - -To prevent employees in the marketing department from communicating with engineers, you could use -the following rule: - -``` -CROSSING GROUPS: Marketing, Engineering -BOUNCE=policy-violation (no communication between these groups is allowed!) -``` - -This works, even though both the users are in the 'Employees' group, because that group is not listed -in the condition. - -In the above example, a user who is member of both groups is not restricted. - -#### SENT DIRECTED PRESENCE TO SENDER - -This condition matches if the recipient of a stanza has previously sent directed presence to the sender of the stanza. This -is often done in XMPP to exchange presence information with JIDs that are not on your roster, such as MUC rooms. - -This condition does not take a parameter - end the condition name with a question mark: - - # Rule to bounce messages from senders not in the roster who haven't been sent directed presence - NOT IN ROSTER? - NOT SENT DIRECTED PRESENCE TO SENDER? - BOUNCE=service-unavailable - -### Permissions - -Rules can consult Prosody's internal role and permissions system to check whether a certain action may -be performed. The acting entity, their role, and appropriate context is automatically inferred. All you -need to do is provide the identifier of the permission that should be checked. - - Condition Description - ----------------------- -------------------------------------------------------------------- - `MAY=permission` Checks whether 'permission' is allowed in the current context. - -As with all other conditions, `MAY` can be combined with `NOT` to negate the result of the check. - -Example, blocking outgoing stanzas from users with roles that do not allow the 'xmpp:federate' permission: - -``` -::deliver_remote -MAY NOT: xmpp:federate -BOUNCE=policy-violation (You are not allowed access to the federation) -``` - -### Roles - - Condition Matches - ---------------- ------------------------------------------------------------------------------------- - `TO ROLE` When the recipient JID of the stanza has the named role - `FROM ROLE` When the sender JID of the stanza has the named role - -**Note:** In most cases, you should avoid checking for specific roles, and instead check for -permissions granted by those roles (using the 'MAY' condition). - -### Admins - -**Deprecated:** These conditions should no longer be used. Prefer 'MAY', 'TO ROLE' or 'FROM ROLE'. - -Prosody allows certain JIDs to be declared as administrators of a host, component or the whole server. - - Condition Matches - ---------------- ------------------------------------------------------------------------------------- - `TO ADMIN` When the recipient of the stanza is admin of the current host - `FROM ADMIN` When the sender of the stanza is admin of the current host - `FROM ADMIN OF` When the sender of the stanza is an admin of the named host on the current server - `TO ADMIN OF` When the recipient of the stanza is an admin of the named host on the current server - -### Time and date - -#### TIME - -Matches stanzas sent during certain time periods. - - Condition Matches - ----------- ------------------------------------------------------------------------------------------- - TIME When the current server local time is within one of the comma-separated time ranges given - - TIME: 10pm-6am, 14:00-15:00 - REPLY=Zzzz. - -#### DAY - -It is also possible to match only on certain days of the week. - - Condition Matches - ----------- ----------------------------------------------------------------------------------------------------- - DAY When the current day matches one, or falls within a rage, in the given comma-separated list of days - -Example: - - DAY: Sat-Sun, Wednesday - REPLY=Sorry, I'm out enjoying life! - -All times and dates are handled in the server's local time. - -### Rate-limiting - -It is possible to selectively rate-limit stanzas, and use rules to -decide what to do with stanzas when over the limit. - -First, you must define any rate limits that you are going to use in your -script. Here we create a limiter called 'normal' that will allow 2 -stanzas per second, and then we define a rule to bounce messages when -over this limit. Note that the `RATE` definition is not part of a rule -(multiple rules can share the same limiter). - - %RATE normal: 2 (burst 3) - - KIND: message - LIMIT: normal - BOUNCE=policy-violation (Sending too fast!) - -The 'burst' parameter on the rate limit allows you to spread the limit -check over a given time period. For example the definition shown above -will allow the limit to be temporarily surpassed, as long as it is -within the limit after 3 seconds. You will almost always want to specify -a burst factor. - -Both the rate and the burst can be fractional values. For example a rate -of 0.1 means only one event is allowed every 10 seconds. - -The LIMIT condition actually does two things; first it counts against -the given limiter, and then it checks to see if the limiter over its -limit yet. If it is, the condition matches, otherwise it will not. - - Condition Matches - ----------- -------------------------------------------------------------------------------------------------- - `LIMIT` When the named limit is 'used up'. Using this condition automatically counts against that limit. - -**Note:** Reloading mod\_firewall resets the current state of any -limiters. - -#### Dynamic limits - -Sometimes you may want to have multiple throttles in a single condition, using some property of the session or stanza -to determine which throttle to use. For example, you might have a limit for incoming stanzas, but you want to limit by -sending JID, instead of all incoming stanzas sharing the same limit. - -You can use the 'on' keyword for this, like so: - - LIMIT: normal on EXPRESSION - -For more information on [expressions](#expressions), see the section later in this document. - -Each value of 'EXPRESSION' has to be tracked individually in a table, which uses a small amount of memory. To prevent -memory exhaustion, the number of tracked values is limited to 1000 by default. You can override this by setting the -maximum number of table entries when you define the rate: - - %RATE normal: 2 (burst 3) (entries 4096) - -Old values are automatically removed from the tracking table. However if the tracking table becomes full, new entries -will be rejected - it will behave as if the rate limit was reached, even for values that have not been seen before. Since -this opens up a potential denial of service (innocent users may be affected if malicious users can fill up the tracking -table within the limit period). You can choose to instead "fail open", and allow the rate limit to be temporarily bypassed -when the table is full. To choose this behaviour, add `(allow overflow)` to the RATE definition. - -### Session marking - -It is possible to 'mark' sessions (see the MARK_ORIGIN action below). To match stanzas from marked sessions, use the -`ORIGIN_MARKED` condition. - - Condition Description - ------------------------------- --------------------------------------------------------------- - ORIGIN MARKED: markname Matches if the origin has been marked with 'markname'. - ORIGIN MARKED: markname (Xs) Matches if the origin has been marked with 'markname' within the past X seconds. - -Example usage: - - # This rule drops messages from sessions that have been marked as spammers in the past hour - ORIGIN MARKED: spammer (3600s) - DROP. - - # This rule marks the origin session as a spammer if they send a message to a honeypot JID - KIND: message - TO: honeypot@example.com - MARK ORIGIN=spammer - -Actions -------- - -Actions come after all conditions in a rule block. There must be at -least one action, though conditions are optional. - -An action without parameters ends with a full-stop/period ('.'), and one -with parameters uses an equals sign ('='): - - # An action with no parameters: - DROP. - - # An action with a parameter: - REPLY=Hello, this is a reply. - -### Route modification - -The following common actions modify the stanza's route in some way. These -rules will halt further processing of the stanza - no further actions will be -executed, and no further rules will be checked. - - Action Description - ----------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- - `PASS.` Stop executing actions and rules on this stanza, and let it through this chain and any calling chains. - `DROP.` Stop executing actions and rules on this stanza, and discard it. - `DEFAULT.` Stop executing actions and rules on this stanza, prevent any other scripts/modules from handling it, to trigger the appropriate default "unhandled stanza" behaviour. Do not use in custom chains (it is treated as PASS). - `REDIRECT=jid` Redirect the stanza to the given JID. - `BOUNCE.` Bounce the stanza with the default error (usually service-unavailable) - `BOUNCE=error` Bounce the stanza with the given error (MUST be a defined XMPP stanza error, see [RFC6120](http://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions). - `BOUNCE=error (text)` As above, but include the supplied human-readable text with a description of the error - -**Note:** It is incorrect behaviour to reply to an 'error' stanza with another error, so BOUNCE will simply act the same as 'DROP' for stanzas that should not be bounced (error stanzas and iq results). - -### Replying and forwarding - -These actions cause a new stanza to be generated and sent somewhere. -Processing of the original stanza will continue beyond these actions. - - Action Description - ------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------- - `REPLY=text` Reply to the stanza (assumed to be a message) with the given text. - `COPY=jid` Make a copy of the stanza and send the copy to the specified JID. The copied stanza flows through Prosody's routing code, and as such is affected by firewall rules. Be careful to avoid loops. - `FORWARD=jid` Forward a copy of the stanza to the given JID (using XEP-0297). The stanza will be sent from the current host's JID. - -### Reporting - - Action Description - ------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- - `REPORT TO=jid [reason] [text]` Forwards the full stanza to `jid` with a XEP-0377 abuse report attached. - -Only the `jid` is mandatory. The `reason` parameter should be either `abuse`, `spam` or a custom URI. If not specified, it defaults to `abuse`. -After the reason, some human-readable text may be included to explain the report. - -Example: - -``` -KIND: message -TO: honeypot@example.com -REPORT TO=antispam.example.com spam Caught by the honeypot! -DROP. -``` - -### Stanza modification - -These actions make it possible to modify the content and structure of a -stanza. - - Action Description - ------------------------ ------------------------------------------------------------------------ - `STRIP=name` Remove any child elements with the given name in the default namespace - `STRIP=name namespace` Remove any child elements with the given name and the given namespace - `INJECT=xml` Inject the given XML into the stanza as a child element - -### Sessions - -It is possible to mark sessions, and then use these marks to match rules later on. - - Action Description - ------------------------ -------------------------------------------------------------------------- - `MARK ORIGIN=mark` Marks the originating session with the given flag. - `UNMARK ORIGIN=mark` Removes the given mark from the origin session (if it is set). - -**Note:** Marks apply to sessions, not JIDs. E.g. if marking in a rule that matches a stanza received -over s2s, it is the s2s session that is marked. - -It is possible to have multiple marks on an origin at any given time. - -### Informational - - Action Description - --------------- ------------------------------------------------------------------------------------------------------------------------ - `LOG=message` Logs the given message to Prosody's log file. Optionally prefix it with a log level in square brackets, e.g. `[debug]` - -You can include [expressions](#expressions) in log messages, using `$(...)` syntax. For example, to log the stanza that matched the rule, -you can use `$(stanza)`, or to log just the top tag of the stanza, use `$(stanza:top_tag())`. To fetch the sender JID, use `$(stanza.attr.from)`. - -Example: - - # Log all stanzas to user@example.com: - TO: user@example.com - LOG=[debug] User received: $(stanza) - -More info about expressions can be found below. - -Chains ------- - -Rules are grouped into "chains", which are injected at particular points in Prosody's routing code. - -Available built-in chains are: - - Chain Description - -------------- ------------------------------------------------------------------------------------------- - deliver Applies to stanzas delivered to local recipients (regardless of the stanza's origin) - deliver_remote Applies to stanzas delivered to remote recipients (just before they leave the local server) - preroute Applies to incoming stanzas from local users, before any routing rules are applied - -A chain is begun by a line `::name` where 'name' is the name of the chain you want the following rules to be -inserted into. If no chain is specified, rules are put into the 'deliver' chain. - -It is possible to create custom chains (useful with the `JUMP CHAIN` action described below). User-created -chains must begin with "user/", e.g. "user/spam_filtering". - -Example of chain use: - - # example.com's firewall script - - # This line is optional, because 'deliver' is the default chain anyway: - ::deliver - - # This rule matches any stanzas delivered to our local user bob: - TO: bob@example.com - DROP. - - # Oops! This rule will never match, because alice is not a local user, - # and only stanzas to local users go through the 'deliver' chain: - TO: alice@remote.example.com - DROP. - - # Create a 'preroute' chain of rules (matched for incoming stanzas from local clients): - ::preroute - # These rules are matched for outgoing stanzas from local clients - - # This will match any stanzas sent to alice from a local user: - TO: alice@remote.example.com - DROP. - - Action Description - ------------------------ ------------------------------------------------------------------------ - `JUMP CHAIN=name` Switches chains, and passes the stanza through the rules in chain 'name'. If the new chain causes the stanza to be dropped/redirected, the current chain halts further processing. - `RETURN.` Stops executing the current chain and returns to the parent chain. For built-in chains, equivalent to PASS. RETURN is implicit at the end of every chain. - -It is possible to jump to chains defined by other scripts and modules. - -Expressions ------------ - -Some conditions and actions in rules support "expressions" in their parameters (their documentation will indicate if this is the case). Most parameters -are static once the firewall script is loaded and compiled internally, however parameters that allow expressions can be dynamically calculated when a -rule is being run. - -There are two kinds of expression that you can use: stanza expressions, and code expressions. - -### Stanza expressions - -Stanza expressions are of the form `$<...>`, where `...` is a stanza path. For syntax of stanza paths, see the documentation for the 'INSPECT' condition -above. - -Example: - - LOG=Matched a stanza from $<@from> to $<@to> - -There are built in functions which can be applied to the output of a stanza expression, by appending the pipe ('|') operator, followed by the function -name. These functions are: - - Function Description - ------------ --------------------------------------- - bare Given a JID, strip any resource - node Return the node ('user part') of a JID - host Return the host ('domain') part of a JID - resource Return the resource part of a JID - -For example, to apply a rate limit to stanzas per sender domain: - - LIMIT normal on $<@from|host> - -If the path does not match (e.g. the element isn't found, or the attribute doesn't exist) or any of the functions fail to produce an output (e.g. an invalid -JID was passed to a function that only handles valid JIDs) the expression will return the text `<undefined>`. You can override this by ending the expression -with a double pipe ('||') followed by a quoted string to use as a default instead. E.g. to default to the string "normal" when there is no 'type' attribute: - - LOG=Stanza type is $<@type||"normal"> - -### Code expressions - -Code expressions use `$(...)` syntax. Code expressions are powerful, and allow unconstrained access to Prosody's internal environment. Therefore -code expressions are typically for advanced use-cases only. You may want to refer to Prosody's [developer documentation](https://prosody.im/doc/developers) -for more information. In particular, within code expressions you may access the 'session' object, which is the session object of the origin of the stanza, -and the 'stanza' object, which is the stanza being considered within the current rule. Whatever value the expression returns will be converted to a string. - -Example to limit stanzas per session type: - - LIMIT: normal on $(session.type)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_firewall/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,820 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'A rule-based stanza filtering module' +rockspec: + build: + modules: + mod_firewall.actions: actions.lib.lua + mod_firewall.conditions: conditions.lib.lua + mod_firewall.definitions: definitions.lib.lua + mod_firewall.marks: marks.lib.lua + mod_firewall.test: test.lib.lua + copy_directories: + - scripts +--- + +------------------------------------------------------------------------ + +**Note:** mod\_firewall is in its very early stages. This documentation +is liable to change, and some described functionality may be missing, +incomplete or contain bugs. + +------------------------------------------------------------------------ + +Introduction +============ + +A firewall is an invaluable tool in the sysadmin's toolbox. However +while low-level firewalls such as iptables and pf are incredibly good at +what they do, they are generally not able to handle application-layer +rules. + +The goal of mod\_firewall is to provide similar services at the XMPP +layer. Based on rule scripts it can efficiently block, bounce, drop, +forward, copy, redirect stanzas and more! Furthermore all rules can be +applied and updated dynamically at runtime without restarting the +server. + +Details +======= + +mod\_firewall loads one or more scripts, and compiles these to Lua code +that reacts to stanzas flowing through Prosody. The firewall script +syntax is unusual, but straightforward. + +A firewall script is dominated by rules. Each rule has two parts: +conditions, and actions. When a stanza matches all of the conditions, +all of the actions are executed in order. + +Here is a simple example to block stanzas from spammer@example.com: + + FROM: spammer@example.com + DROP. + +FROM is a condition, and DROP is an action. This is about as simple as +it gets. How about heading to the other extreme? Let's demonstrate +something more complex that mod\_firewall can do for you: + + %ZONE myorganisation: staff.myorg.example, support.myorg.example + + ENTERING: myorganisation + KIND: message + TIME: 12am-9am, 5pm-12am, Saturday, Sunday + REPLY=Sorry, I am afraid our office is closed at the moment. If you need assistance, please call our 24-hour support line on 123-456-789. + +This rule will reply with a short message whenever someone tries to send +a message to someone at any of the hosts defined in the 'myorganisation' +outside of office hours. + +Specifying rule sets +-------------------- + +Firewall rules should be written into text files, e.g. `ruleset.pfw` file. +One or more rule files can be specified in the configuration using: + + firewall_scripts = { "path/to/ruleset.pfw", "path/to/ruleset2.pfw" } + +If multiple files are specified and they both add rules to the same [chains](#chains), +each file's rules will be processed in order, but the order of files is undefined. + +Reloading Prosody's configuration also reloads firewall rules. + +Make sure that `firewall_scripts` is in the global section of the configuration file +and not below a virtual host or a component - unless you want per-vhost +firewall rules. + +Conditions +---------- + +All conditions must come before any action in a rule block. The +condition name is followed by a colon (':'), and the value to test for. + +A condition can be preceded or followed by `NOT` to negate its match. +For example: + + NOT FROM: user@example.com + KIND NOT: message + +Some conditions do not take parameters, and these should end with just a +question mark, like: + + IN ROSTER? + +### Zones + +A 'zone' is one or more hosts or JIDs. It is possible to match when a +stanza is entering or leaving a zone, while at the same time not +matching traffic passing between JIDs in the same zone. + +Zones are defined at the top of a script with the following syntax (they +are not part of a rule block): + + %ZONE myzone: host1, host2, user@host3, foo.bar.example + +There is an automatic zone named `$local`, which automatically includes +all of the current server's active hosts (including components). It can +be used to match stanzas entering or leaving the current server. + +A host listed in a zone also matches all users on that host (but not +subdomains). + +The following zone-matching conditions are supported: + + Condition Matches + ------------ ------------------------------------------ + `ENTERING` When a stanza is entering the named zone + `LEAVING` When a stanza is leaving the named zone + +### Lists + +It is possible to create or load lists of strings for use in scripts. For +example, you might load a list of blocked JIDs, malware URLs or simple words +that you want to filter messages on. + + List type Example + ----------- ----------------------- + memory %LIST spammers: memory + file %LIST spammers: file:/etc/spammers.txt + http %LIST spammers: http://example.com/spammers.txt + +#### List types +##### memory + +``` +%LIST name: memory (limit: number) +``` + +A memory-only list, with an optional limit. Supports addition and removal of items by scripts. + +If a limit is provided, the oldest item will be discarded to make room for a new item if the +list is full. The limit is useful to prevent infinite memory growth on busy servers. + +##### file + +``` +%LIST name: file:/path/to/file (missing: string) +``` + +Reads a list from a file. The list can be added to and removed from by scripts, but +these changes do not persist between restarts. + +If the file is missing, an error will be raised. The optional 'missing' parameter can be set +to 'ignore' (e.g. `(missing: ignore)`) to ignore a missing file. + +##### http + +``` +%LIST name: http://example.com/ (ttl: number, pattern: pat, hash: sha1, checkcerts: when-sni) +``` + +Fetches a list from a HTTP or HTTPS URL. The following options are accepted: + + Option Description + ------- ----------- + ttl Seconds to cache the list for. After expiry, it will be refetched. Default 3600 (1 hour). + pattern Optional pattern used to extract list entries from the response. Default is to treat each line as a single item. + hash Optional hash to be applied to items before looking them up in the list, e.g. sha1 or sha256. + checkcert Whether to verify HTTPS certificates. May be "always", "never" or "when-sni". Default "when-sni". + +The "when-sni" default disables certificate verification when Prosody's HTTP client API doesn't support SNI, +as in Prosody 0.11.6 and earlier. + +#### CHECK LIST + +Checks whether a simple [expression](#expressions) is found in a given list. + +Example: + + %LIST blocked_jids: file:/etc/prosody/blocked_jids.txt + + # Rule to block presence subscription requests from blocked JIDs + KIND: presence + TYPE: subscribe + CHECK LIST: blocked_jids contains $<@from> + BOUNCE=policy-violation (Your JID is blocked) + +#### SCAN + +SCAN allows you to search inside a stanza for a given pattern, and check each result against a list. For example, +you could scan a message body for words and check if any of the words are found in a given list. + +Before using SCAN, you need to define a search location and a pattern. The search location uses the same 'path' +format as documented under the 'INSPECT' condition. Patterns can be any valid Lua pattern. + +To use the above example: + + # Define a search location called 'body' which fetches the text of the 'body' element + %SEARCH body: body# + # Define a pattern called 'word' which matches any sequence of letters + %PATTERN word: [A-Za-z]+ + # Finally, we also need our list of "bad" words: + %LIST badwords: file:/etc/prosody/bad_words.txt + + # Now we can use these to SCAN incoming stanzas + # If it finds a match, bounce the stanza + SCAN: body for word in badwords + BOUNCE=policy-violation (This word is not allowed!) + +#### COUNT + +COUNT is similar to SCAN, in that it uses a defined SEARCH and breaks it up according to a PATTERN. Then it +counts the number of results. + +For example, to block every message with more than one URL: + + # Define a search location called 'body' which fetches the text of the 'body' element + %SEARCH body: body# + # Define a pattern called 'url' which matches HTTP links + %PATTERN url: https?://%S+ + + COUNT: url in body > 1 + BOUNCE=policy-violation (Up to one HTTP URL is allowed in messages) + +### Stanza matching + + Condition Matches + ----------- ------------------------------------------------------------------------------------------------------------------------------------------------------------ + `KIND` The kind of stanza. May be 'message', 'presence' or 'iq' + `TYPE` The type of stanza. This varies depending on the kind of stanza. See 'Stanza types' below for more information. + `PAYLOAD` The stanza contains a child with the given namespace. Useful for determining the type of an iq request, or whether a message contains a certain extension. + `INSPECT` The node at the specified path exists or matches a given string. This allows you to look anywhere inside a stanza. See below for examples and more. + +#### Stanza types + + Stanza Valid types + ---------- ------------------------------------------------------------------------------------------ + iq get, set, result, error + presence *available*, unavailable, probe, subscribe, subscribed, unsubscribe, unsubscribed, error + message normal, chat, groupchat, headline, error + +**Note:** The type 'available' for presence does not actually appear in +the protocol. Available presence is signalled by the omission of a type. +Similarly, a message stanza with no type is equivalent to one of type +'normal'. mod\_firewall handles these cases for you automatically. + +#### Sender/recipient matching + + Condition Matches + --------------- ------------------------------------------------------- + `FROM` The JID in the 'from' attribute matches the given JID. + `TO` The JID in the 'to' attribute matches the given JID. + `TO SELF` The stanza is sent by any of a user's resources to their own bare JID. + `TO FULL JID` The stanza is addressed to a **valid** full JID on the local server (full JIDs include a resource at the end, and only exist for the lifetime of a single session, therefore the recipient **must be online**, or this check will not match). + `FROM FULL JID` The stanza is from a full JID (unlike `TO FULL JID` this check is on the format of the JID only). + +The TO and FROM conditions both accept wildcards in the JID when it is +enclosed in angle brackets ('\<...\>'). For example: + + # All users at example.com + FROM: <*>@example.com + + # The user 'admin' on any subdomain of example.com + FROM: admin@<*.example.com> + +You can also use [Lua's pattern +matching](http://www.lua.org/manual/5.1/manual.html#5.4.1) for more +powerful matching abilities. Patterns are a lightweight +regular-expression alternative. Simply contain the pattern in double +angle brackets. The pattern is automatically anchored at the start and +end (so it must match the entire portion of the JID). + + # Match admin@example.com, and admin1@example.com, etc. + FROM: <<admin%d*>>@example.com + +**Note:** It is important to know that 'example.com' is a valid JID on +its own, and does **not** match 'user@example.com'. To efficiently match +domains we recommend defining them as [Zones](#zones). + + Condition Matches + ---------------- --------------------------------------------------------------- + `FROM_EXACTLY` The JID in the 'from' attribute exactly matches the given JID + `TO_EXACTLY` The JID in the 'to' attribute exactly matches the given JID + +These additional conditions do not support pattern matching, but are +useful to match the exact to/from address on a stanza. For example, if +no resource is specified then only bare JIDs will be matched. TO and FROM +match all resources if no resource is specified to match. + +**Note:** Some chains execute before Prosody has performed any +normalisation or validity checks on the to/from JIDs on an incoming +stanza. It is not advisable to perform access control or similar rules +on JIDs in these chains (see the [chain documentation](#chains) for more info). + +#### GeoIP matching + + Condition Matches + ---------------- -------------------------------------------------------------- + `FROM COUNTRY` Two or three letter country code looked up in GeoIP database + +This condition uses a GeoIP database to look up the origin country of +the IP attached to the current session. + +For example: + + # 3 letter country code + FROM COUNTRY: SWE + + # or 2 letter + FROM COUNTRY: SE + + # Explicit + FROM COUNTRY: code=SE + FROM COUNTRY: code3=SWE + +**Note:** This requires that the `lua-geoip` and `geoip-database` +packages are installed (on Debian, package names may differ on other +operating systems). + +#### INSPECT + +INSPECT takes a 'path' through the stanza to get a string (an attribute +value or text content). An example is the best way to explain. Let's +check that a user is not trying to register an account with the username +'admin'. This stanza comes from [XEP-0077: In-band +Registration](http://xmpp.org/extensions/xep-0077.html#example-4): + +``` xml +<iq type='set' id='reg2'> + <query xmlns='jabber:iq:register'> + <username>bill</username> + <password>Calliope</password> + <email>bard@shakespeare.lit</email> + </query> +</iq> +``` + + KIND: iq + TYPE: set + PAYLOAD: jabber:iq:register + INSPECT: {jabber:iq:register}query/username#=admin + BOUNCE=not-allowed (The username 'admin' is reserved.) + +That weird string deserves some explanation. It is a path, divided into +segments by '/'. Each segment describes an element by its name, +optionally prefixed by its namespace in curly braces ('{...}'). If the +path ends with a '\#' then the text content of the last element will be +returned. If the path ends with '@name' then the value of the attribute +'name' will be returned. + +You can use INSPECT to test for the existence of an element or attribute, +or you can check if it matches a specific value, e.g. by appending `=VALUE` +(like in the example above, that checks if the content of username is 'admin'). + +#### INSPECT comparison operators + +As well as checking for an exact string match, there are some other modifiers +you can apply to the comparison: + + Comparison Matches when + ------------- ------------------------------------------------------- + `=` The value is exactly the given string. + `/=` The value is or *contains* the given string (e.g. `/=admin` would match `administrator` or `myadmin`). + `~=` The value matches the given [Lua pattern](https://www.lua.org/manual/5.2/manual.html#6.4.1). + +Finally, if the comparison operator is preceded by a `$` character, [expressions](#expressions) +will be interpreted in the string following the comparison operator. + +e.g. `INSPECT: {jabber:iq:register}query/username}$/=$(session.host)` would match +if the username of an account registration contained the session's current hostname +somewhere in it. + +#### INSPECT performance + +INSPECT can be somewhat slower than the other stanza matching conditions. To +minimise performance impact, always place it below other faster +condition checks where possible (e.g. in the example above we first checked KIND, +TYPE and PAYLOAD matched what we wanted before reaching the INSPECT rule). + +### Roster + +These conditions access the roster of the recipient (only). Therefore they cannot (currently) +be used in some [chains](#chains), such as for outgoing messages (the recipient may be on another server). + +Performance note: these checks can potentially cause storage access (especially if the recipient +is currently offline), so you may want to limit their use in high-traffic situations, and place rules +containing a roster check below other rules (such as a rate limiter). The storage access is +performed unconditionally just before evaluation of the first rule that contains a roster-based +condition, even if earlier conditions in that rule do not match. + +#### IN ROSTER + +Tests whether the sender is in the recipient's roster. + + IN ROSTER? + +#### IN ROSTER GROUP + +Tests whether the sender is in the recipient's roster, and in the named group. + + IN ROSTER GROUP: Friends + +#### SUBSCRIBED + +Tests whether the recipient is subscribed to the sender, ie will receive +presence updates from them. + +Note that this *does* work, regardless of direction and which [chain](#chain) is +used, since both the sender and the recipient will have mirrored roster +entries. + +### Groups + +Using Prosody's mod\_groups it is possible to define groups of users on the server. You can +match based on these groups in firewall rules. + + Condition Matches + ----------------- ---------------------------- + `FROM GROUP` When the stanza is being sent from a member of the named group + `TO GROUP` When the stanza is being sent to a member of the named group + `CROSSING GROUPS` When the stanza is being sent between users of different named groups + +#### CROSSING GROUPS + +The `CROSSING GROUPS` condition takes a comma-separated list of groups to check. If the +sender and recipient are not in the same group (only the listed groups are checked), then the +this condition matches and the stanza is deemed to be crossing between groups. + +For example, if you had three groups: Engineering, Marketing and Employees. All users are +members of the 'Employees' group, and the others are for employees of the named department only. + +To prevent employees in the marketing department from communicating with engineers, you could use +the following rule: + +``` +CROSSING GROUPS: Marketing, Engineering +BOUNCE=policy-violation (no communication between these groups is allowed!) +``` + +This works, even though both the users are in the 'Employees' group, because that group is not listed +in the condition. + +In the above example, a user who is member of both groups is not restricted. + +#### SENT DIRECTED PRESENCE TO SENDER + +This condition matches if the recipient of a stanza has previously sent directed presence to the sender of the stanza. This +is often done in XMPP to exchange presence information with JIDs that are not on your roster, such as MUC rooms. + +This condition does not take a parameter - end the condition name with a question mark: + + # Rule to bounce messages from senders not in the roster who haven't been sent directed presence + NOT IN ROSTER? + NOT SENT DIRECTED PRESENCE TO SENDER? + BOUNCE=service-unavailable + +### Permissions + +Rules can consult Prosody's internal role and permissions system to check whether a certain action may +be performed. The acting entity, their role, and appropriate context is automatically inferred. All you +need to do is provide the identifier of the permission that should be checked. + + Condition Description + ----------------------- -------------------------------------------------------------------- + `MAY=permission` Checks whether 'permission' is allowed in the current context. + +As with all other conditions, `MAY` can be combined with `NOT` to negate the result of the check. + +Example, blocking outgoing stanzas from users with roles that do not allow the 'xmpp:federate' permission: + +``` +::deliver_remote +MAY NOT: xmpp:federate +BOUNCE=policy-violation (You are not allowed access to the federation) +``` + +### Roles + + Condition Matches + ---------------- ------------------------------------------------------------------------------------- + `TO ROLE` When the recipient JID of the stanza has the named role + `FROM ROLE` When the sender JID of the stanza has the named role + +**Note:** In most cases, you should avoid checking for specific roles, and instead check for +permissions granted by those roles (using the 'MAY' condition). + +### Admins + +**Deprecated:** These conditions should no longer be used. Prefer 'MAY', 'TO ROLE' or 'FROM ROLE'. + +Prosody allows certain JIDs to be declared as administrators of a host, component or the whole server. + + Condition Matches + ---------------- ------------------------------------------------------------------------------------- + `TO ADMIN` When the recipient of the stanza is admin of the current host + `FROM ADMIN` When the sender of the stanza is admin of the current host + `FROM ADMIN OF` When the sender of the stanza is an admin of the named host on the current server + `TO ADMIN OF` When the recipient of the stanza is an admin of the named host on the current server + +### Time and date + +#### TIME + +Matches stanzas sent during certain time periods. + + Condition Matches + ----------- ------------------------------------------------------------------------------------------- + TIME When the current server local time is within one of the comma-separated time ranges given + + TIME: 10pm-6am, 14:00-15:00 + REPLY=Zzzz. + +#### DAY + +It is also possible to match only on certain days of the week. + + Condition Matches + ----------- ----------------------------------------------------------------------------------------------------- + DAY When the current day matches one, or falls within a rage, in the given comma-separated list of days + +Example: + + DAY: Sat-Sun, Wednesday + REPLY=Sorry, I'm out enjoying life! + +All times and dates are handled in the server's local time. + +### Rate-limiting + +It is possible to selectively rate-limit stanzas, and use rules to +decide what to do with stanzas when over the limit. + +First, you must define any rate limits that you are going to use in your +script. Here we create a limiter called 'normal' that will allow 2 +stanzas per second, and then we define a rule to bounce messages when +over this limit. Note that the `RATE` definition is not part of a rule +(multiple rules can share the same limiter). + + %RATE normal: 2 (burst 3) + + KIND: message + LIMIT: normal + BOUNCE=policy-violation (Sending too fast!) + +The 'burst' parameter on the rate limit allows you to spread the limit +check over a given time period. For example the definition shown above +will allow the limit to be temporarily surpassed, as long as it is +within the limit after 3 seconds. You will almost always want to specify +a burst factor. + +Both the rate and the burst can be fractional values. For example a rate +of 0.1 means only one event is allowed every 10 seconds. + +The LIMIT condition actually does two things; first it counts against +the given limiter, and then it checks to see if the limiter over its +limit yet. If it is, the condition matches, otherwise it will not. + + Condition Matches + ----------- -------------------------------------------------------------------------------------------------- + `LIMIT` When the named limit is 'used up'. Using this condition automatically counts against that limit. + +**Note:** Reloading mod\_firewall resets the current state of any +limiters. + +#### Dynamic limits + +Sometimes you may want to have multiple throttles in a single condition, using some property of the session or stanza +to determine which throttle to use. For example, you might have a limit for incoming stanzas, but you want to limit by +sending JID, instead of all incoming stanzas sharing the same limit. + +You can use the 'on' keyword for this, like so: + + LIMIT: normal on EXPRESSION + +For more information on [expressions](#expressions), see the section later in this document. + +Each value of 'EXPRESSION' has to be tracked individually in a table, which uses a small amount of memory. To prevent +memory exhaustion, the number of tracked values is limited to 1000 by default. You can override this by setting the +maximum number of table entries when you define the rate: + + %RATE normal: 2 (burst 3) (entries 4096) + +Old values are automatically removed from the tracking table. However if the tracking table becomes full, new entries +will be rejected - it will behave as if the rate limit was reached, even for values that have not been seen before. Since +this opens up a potential denial of service (innocent users may be affected if malicious users can fill up the tracking +table within the limit period). You can choose to instead "fail open", and allow the rate limit to be temporarily bypassed +when the table is full. To choose this behaviour, add `(allow overflow)` to the RATE definition. + +### Session marking + +It is possible to 'mark' sessions (see the MARK_ORIGIN action below). To match stanzas from marked sessions, use the +`ORIGIN_MARKED` condition. + + Condition Description + ------------------------------- --------------------------------------------------------------- + ORIGIN MARKED: markname Matches if the origin has been marked with 'markname'. + ORIGIN MARKED: markname (Xs) Matches if the origin has been marked with 'markname' within the past X seconds. + +Example usage: + + # This rule drops messages from sessions that have been marked as spammers in the past hour + ORIGIN MARKED: spammer (3600s) + DROP. + + # This rule marks the origin session as a spammer if they send a message to a honeypot JID + KIND: message + TO: honeypot@example.com + MARK ORIGIN=spammer + +Actions +------- + +Actions come after all conditions in a rule block. There must be at +least one action, though conditions are optional. + +An action without parameters ends with a full-stop/period ('.'), and one +with parameters uses an equals sign ('='): + + # An action with no parameters: + DROP. + + # An action with a parameter: + REPLY=Hello, this is a reply. + +### Route modification + +The following common actions modify the stanza's route in some way. These +rules will halt further processing of the stanza - no further actions will be +executed, and no further rules will be checked. + + Action Description + ----------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- + `PASS.` Stop executing actions and rules on this stanza, and let it through this chain and any calling chains. + `DROP.` Stop executing actions and rules on this stanza, and discard it. + `DEFAULT.` Stop executing actions and rules on this stanza, prevent any other scripts/modules from handling it, to trigger the appropriate default "unhandled stanza" behaviour. Do not use in custom chains (it is treated as PASS). + `REDIRECT=jid` Redirect the stanza to the given JID. + `BOUNCE.` Bounce the stanza with the default error (usually service-unavailable) + `BOUNCE=error` Bounce the stanza with the given error (MUST be a defined XMPP stanza error, see [RFC6120](http://xmpp.org/rfcs/rfc6120.html#stanzas-error-conditions). + `BOUNCE=error (text)` As above, but include the supplied human-readable text with a description of the error + +**Note:** It is incorrect behaviour to reply to an 'error' stanza with another error, so BOUNCE will simply act the same as 'DROP' for stanzas that should not be bounced (error stanzas and iq results). + +### Replying and forwarding + +These actions cause a new stanza to be generated and sent somewhere. +Processing of the original stanza will continue beyond these actions. + + Action Description + ------------------------ --------------------------------------------------------------------------------------------------------------------------------------------------------- + `REPLY=text` Reply to the stanza (assumed to be a message) with the given text. + `COPY=jid` Make a copy of the stanza and send the copy to the specified JID. The copied stanza flows through Prosody's routing code, and as such is affected by firewall rules. Be careful to avoid loops. + `FORWARD=jid` Forward a copy of the stanza to the given JID (using XEP-0297). The stanza will be sent from the current host's JID. + +### Reporting + + Action Description + ------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------- + `REPORT TO=jid [reason] [text]` Forwards the full stanza to `jid` with a XEP-0377 abuse report attached. + +Only the `jid` is mandatory. The `reason` parameter should be either `abuse`, `spam` or a custom URI. If not specified, it defaults to `abuse`. +After the reason, some human-readable text may be included to explain the report. + +Example: + +``` +KIND: message +TO: honeypot@example.com +REPORT TO=antispam.example.com spam Caught by the honeypot! +DROP. +``` + +### Stanza modification + +These actions make it possible to modify the content and structure of a +stanza. + + Action Description + ------------------------ ------------------------------------------------------------------------ + `STRIP=name` Remove any child elements with the given name in the default namespace + `STRIP=name namespace` Remove any child elements with the given name and the given namespace + `INJECT=xml` Inject the given XML into the stanza as a child element + +### Sessions + +It is possible to mark sessions, and then use these marks to match rules later on. + + Action Description + ------------------------ -------------------------------------------------------------------------- + `MARK ORIGIN=mark` Marks the originating session with the given flag. + `UNMARK ORIGIN=mark` Removes the given mark from the origin session (if it is set). + +**Note:** Marks apply to sessions, not JIDs. E.g. if marking in a rule that matches a stanza received +over s2s, it is the s2s session that is marked. + +It is possible to have multiple marks on an origin at any given time. + +### Informational + + Action Description + --------------- ------------------------------------------------------------------------------------------------------------------------ + `LOG=message` Logs the given message to Prosody's log file. Optionally prefix it with a log level in square brackets, e.g. `[debug]` + +You can include [expressions](#expressions) in log messages, using `$(...)` syntax. For example, to log the stanza that matched the rule, +you can use `$(stanza)`, or to log just the top tag of the stanza, use `$(stanza:top_tag())`. To fetch the sender JID, use `$(stanza.attr.from)`. + +Example: + + # Log all stanzas to user@example.com: + TO: user@example.com + LOG=[debug] User received: $(stanza) + +More info about expressions can be found below. + +Chains +------ + +Rules are grouped into "chains", which are injected at particular points in Prosody's routing code. + +Available built-in chains are: + + Chain Description + -------------- ------------------------------------------------------------------------------------------- + deliver Applies to stanzas delivered to local recipients (regardless of the stanza's origin) + deliver_remote Applies to stanzas delivered to remote recipients (just before they leave the local server) + preroute Applies to incoming stanzas from local users, before any routing rules are applied + +A chain is begun by a line `::name` where 'name' is the name of the chain you want the following rules to be +inserted into. If no chain is specified, rules are put into the 'deliver' chain. + +It is possible to create custom chains (useful with the `JUMP CHAIN` action described below). User-created +chains must begin with "user/", e.g. "user/spam_filtering". + +Example of chain use: + + # example.com's firewall script + + # This line is optional, because 'deliver' is the default chain anyway: + ::deliver + + # This rule matches any stanzas delivered to our local user bob: + TO: bob@example.com + DROP. + + # Oops! This rule will never match, because alice is not a local user, + # and only stanzas to local users go through the 'deliver' chain: + TO: alice@remote.example.com + DROP. + + # Create a 'preroute' chain of rules (matched for incoming stanzas from local clients): + ::preroute + # These rules are matched for outgoing stanzas from local clients + + # This will match any stanzas sent to alice from a local user: + TO: alice@remote.example.com + DROP. + + Action Description + ------------------------ ------------------------------------------------------------------------ + `JUMP CHAIN=name` Switches chains, and passes the stanza through the rules in chain 'name'. If the new chain causes the stanza to be dropped/redirected, the current chain halts further processing. + `RETURN.` Stops executing the current chain and returns to the parent chain. For built-in chains, equivalent to PASS. RETURN is implicit at the end of every chain. + +It is possible to jump to chains defined by other scripts and modules. + +Expressions +----------- + +Some conditions and actions in rules support "expressions" in their parameters (their documentation will indicate if this is the case). Most parameters +are static once the firewall script is loaded and compiled internally, however parameters that allow expressions can be dynamically calculated when a +rule is being run. + +There are two kinds of expression that you can use: stanza expressions, and code expressions. + +### Stanza expressions + +Stanza expressions are of the form `$<...>`, where `...` is a stanza path. For syntax of stanza paths, see the documentation for the 'INSPECT' condition +above. + +Example: + + LOG=Matched a stanza from $<@from> to $<@to> + +There are built in functions which can be applied to the output of a stanza expression, by appending the pipe ('|') operator, followed by the function +name. These functions are: + + Function Description + ------------ --------------------------------------- + bare Given a JID, strip any resource + node Return the node ('user part') of a JID + host Return the host ('domain') part of a JID + resource Return the resource part of a JID + +For example, to apply a rate limit to stanzas per sender domain: + + LIMIT normal on $<@from|host> + +If the path does not match (e.g. the element isn't found, or the attribute doesn't exist) or any of the functions fail to produce an output (e.g. an invalid +JID was passed to a function that only handles valid JIDs) the expression will return the text `<undefined>`. You can override this by ending the expression +with a double pipe ('||') followed by a quoted string to use as a default instead. E.g. to default to the string "normal" when there is no 'type' attribute: + + LOG=Stanza type is $<@type||"normal"> + +### Code expressions + +Code expressions use `$(...)` syntax. Code expressions are powerful, and allow unconstrained access to Prosody's internal environment. Therefore +code expressions are typically for advanced use-cases only. You may want to refer to Prosody's [developer documentation](https://prosody.im/doc/developers) +for more information. In particular, within code expressions you may access the 'session' object, which is the session object of the origin of the stanza, +and the 'stanza' object, which is the stanza being considered within the current rule. Whatever value the expression returns will be converted to a string. + +Example to limit stanzas per session type: + + LIMIT: normal on $(session.type)
--- a/mod_flash_policy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Adds support for flash socket policy -... - -Introduction -============ - -This Prosody plugin adds support for flash socket policies. When -connecting with a flash client (from a webpage, not an exe) to prosody -the flash client requests for an xml "file" on port 584 or the -connecting port (5222 in the case of default xmpp). Responding on port -584 is tricky because it requires root priviliges to set up a socket on -a port \< 1024. - -This plugins filters the incoming data from the flash client. So when -the client connects with prosody it immediately sends a xml request -string (`<policy-file-request/>\0`). Prosody responds with a flash -cross-domain-policy. See -http://www.adobe.com/devnet/flashplayer/articles/socket\_policy\_files.html -for more information. - -Usage -===== - -Add "flash\_policy" to your modules\_enabled list. - -Configuration -============= - - --------------------- -------------------------------------------------------------------------------- - crossdomain\_file Optional. The path to a file containing an cross-domain-policy in xml format. - crossdomain\_string Optional. A cross-domain-policy as string. Should include the xml declaration. - --------------------- -------------------------------------------------------------------------------- - -Both configuration options are optional. If both are not specified a -cross-domain-policy with "`<allow-access-from domain="*" />`" is used as -default. - -Compatibility -============= - - ----- ------- - 0.7 Works - ----- ------- - -Caveats/Todos/Bugs -================== - -- The assumption is made that the first packet received will always - contain the policy request data, and all of it. This isn't robust - against fragmentation, but on the other hand I highly doubt you'll - be seeing that with such a small packet. -- Only tested by me on a single server :)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_flash_policy/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,55 @@ +--- +labels: +- 'Stage-Alpha' +summary: Adds support for flash socket policy +... + +Introduction +============ + +This Prosody plugin adds support for flash socket policies. When +connecting with a flash client (from a webpage, not an exe) to prosody +the flash client requests for an xml "file" on port 584 or the +connecting port (5222 in the case of default xmpp). Responding on port +584 is tricky because it requires root priviliges to set up a socket on +a port \< 1024. + +This plugins filters the incoming data from the flash client. So when +the client connects with prosody it immediately sends a xml request +string (`<policy-file-request/>\0`). Prosody responds with a flash +cross-domain-policy. See +http://www.adobe.com/devnet/flashplayer/articles/socket\_policy\_files.html +for more information. + +Usage +===== + +Add "flash\_policy" to your modules\_enabled list. + +Configuration +============= + + --------------------- -------------------------------------------------------------------------------- + crossdomain\_file Optional. The path to a file containing an cross-domain-policy in xml format. + crossdomain\_string Optional. A cross-domain-policy as string. Should include the xml declaration. + --------------------- -------------------------------------------------------------------------------- + +Both configuration options are optional. If both are not specified a +cross-domain-policy with "`<allow-access-from domain="*" />`" is used as +default. + +Compatibility +============= + + ----- ------- + 0.7 Works + ----- ------- + +Caveats/Todos/Bugs +================== + +- The assumption is made that the first packet received will always + contain the policy request data, and all of it. This isn't robust + against fragmentation, but on the other hand I highly doubt you'll + be seeing that with such a small packet. +- Only tested by me on a single server :)
--- a/mod_graceful_shutdown/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -::: {.alert .alert-warning} -This module is an experiment about a more graceful shutdown process. - -Graceful shutdown has now been implemented in Prosody trunk and will be -part 0.12. See [issue #1225](https://issues.prosody.im/1225) for -details. -::: - -Why -=== - -When shutting down, a number of sessions, connections and other things -are teared down. Due to all these things happening very quickly, -sometimes e.g. client unavailable notifications don't make it to all -remote contacts because the server-to-server connections are teared down -just after. - -How -=== - -This module works by breaking the shutdown process into separate steps -with a brief pause between them. - -It goes something like this - -1. Stop accepting new client connections. -2. Close all client connections. -3. Fire event for everything else. -4. Tell `net.server` to quit the main loop. -5. ??? -6. Still here? Kill itself. -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_graceful_shutdown/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,32 @@ +::: {.alert .alert-warning} +This module is an experiment about a more graceful shutdown process. + +Graceful shutdown has now been implemented in Prosody trunk and will be +part 0.12. See [issue #1225](https://issues.prosody.im/1225) for +details. +::: + +Why +=== + +When shutting down, a number of sessions, connections and other things +are teared down. Due to all these things happening very quickly, +sometimes e.g. client unavailable notifications don't make it to all +remote contacts because the server-to-server connections are teared down +just after. + +How +=== + +This module works by breaking the shutdown process into separate steps +with a brief pause between them. + +It goes something like this + +1. Stop accepting new client connections. +2. Close all client connections. +3. Fire event for everything else. +4. Tell `net.server` to quit the main loop. +5. ??? +6. Still here? Kill itself. +
--- a/mod_group_bookmarks/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'mod\_groups for chatrooms' -... - -Introduction -============ - -[mod\_groups](http://prosody.im/doc/modules/mod_groups) allows you to -insert contacts into users' contact lists. Well mod\_group\_bookmarks -allows you to insert chatrooms into the user's bookmarks. These are -fetched by their client and automatically joined when the log in. - -In short, if you want to automatically join users to rooms when they -sign in, this is the module you want. - -Details -======= - -Most clients support storing a private list of room "bookmarks" on the -server. When they log in, they fetch this list and join any that are -marked as "autojoin". Without affecting normal usage of the bookmarks -store this module dynamically inserts custom rooms into users' bookmarks -lists. - -Usage -===== - -Similar to [mod\_groups](http://prosody.im/doc/modules/mod_groups), you -need to make a text file in this format: - - [room@conferenceserver] - user1@example.com=User 1 - user2@example.com=User 2 - - [otherroom@conferenceserver] - user3@example.net=User 3 - -Add "group\_bookmarks" to your modules\_enabled list: - - modules_enabled = { - -- ...other modules here... -- - "group_bookmarks"; - -- ...maybe some more here... -- - } - -Configuration -============= - - ------------------------ --------------------------------------------------- - group\_bookmarks\_file The path to the text file you created (as above). - ------------------------ --------------------------------------------------- - -Compatibility -============= - - ----- ------------- - 0.8 Works - 0.7 Should work - 0.6 Should work - ----- ------------- - -Todo -==== - -- Support for injecting into ALL users bookmarks, without needing a - list -- Allow turning off the autojoin flag -- Perhaps support a friendly name for the bookmark (currently uses the - room address)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_group_bookmarks/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,71 @@ +--- +labels: +- 'Stage-Beta' +summary: 'mod\_groups for chatrooms' +... + +Introduction +============ + +[mod\_groups](http://prosody.im/doc/modules/mod_groups) allows you to +insert contacts into users' contact lists. Well mod\_group\_bookmarks +allows you to insert chatrooms into the user's bookmarks. These are +fetched by their client and automatically joined when the log in. + +In short, if you want to automatically join users to rooms when they +sign in, this is the module you want. + +Details +======= + +Most clients support storing a private list of room "bookmarks" on the +server. When they log in, they fetch this list and join any that are +marked as "autojoin". Without affecting normal usage of the bookmarks +store this module dynamically inserts custom rooms into users' bookmarks +lists. + +Usage +===== + +Similar to [mod\_groups](http://prosody.im/doc/modules/mod_groups), you +need to make a text file in this format: + + [room@conferenceserver] + user1@example.com=User 1 + user2@example.com=User 2 + + [otherroom@conferenceserver] + user3@example.net=User 3 + +Add "group\_bookmarks" to your modules\_enabled list: + + modules_enabled = { + -- ...other modules here... -- + "group_bookmarks"; + -- ...maybe some more here... -- + } + +Configuration +============= + + ------------------------ --------------------------------------------------- + group\_bookmarks\_file The path to the text file you created (as above). + ------------------------ --------------------------------------------------- + +Compatibility +============= + + ----- ------------- + 0.8 Works + 0.7 Should work + 0.6 Should work + ----- ------------- + +Todo +==== + +- Support for injecting into ALL users bookmarks, without needing a + list +- Allow turning off the autojoin flag +- Perhaps support a friendly name for the bookmark (currently uses the + room address)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_groups_internal/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,330 @@ +--- +labels: +- 'Stage-Beta' +summary: Equivalent of mod_groups but without a configuration file +--- + +## Introduction + +This module is functionally similar to [`mod_groups`], but it differs by working without a configuration file (allowing changes without a restart of the server) and by permanently adding users to each other's contact lists. To paraphrase [`mod_groups`]: + +> `mod_groups_internal` was designed to allow administrators to create virtual groups of users that automatically see each other in their contact lists. There is no need for the user to authorise these contacts in their contact list - this is done automatically on the server. +> +> As an example, if you have a team of people working together on a project, you can create a group for that team. They will automatically be added to each others' contact lists, and the list can easily be modified on the server at any time to add and remove people. + +::: {.alert .alert-info} +On `user-deleted` events, `mod_groups_internal` will automatically remove the deleted user from every group they were part of. +::: + +## Setup + +```lua +modules_enabled = { + -- Other modules + "groups_internal"; -- Enable mod_groups_internal +} +``` + +## Configuration + +| Option | Type | Default | Notes | +| ------ | ---- | ------- | ----- | +| `groups_muc_host` | string? | nil | Host where the group chats will be created. | + +## Usage + +### Exposed functions + +- #### `create(group_info, create_default_muc, group_id)` {#create} + + Creates a new group, optionally creating a default MUC chat on [`groups_muc_host`](#configuration). + + **Parameters:** + + 1. `group_info: { name: string }` + 2. `create_default_muc: boolean | nil`: Whether or not to create the default MUC chat. Defaults to `false`. + 3. `group_id: string | nil`: The desired group JID node part. Defaults to [`util.id.short`](https://prosody.im/doc/developers/util/id) (9-chars URL-safe base64). + + **Returns:** `group_id: string | nil, error: string` + +- #### `get_info(group_id)` {#get_info} + + Retrieves information about a group. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** + + ```lua + group_info: { + name: string, + muc_jid: string | nil + } + | nil + ``` + +- #### `set_info(group_id, info)` {#set_info} + + Allows one to change a group's name. If `muc_jid` is specified, this function will also update the group chat's name. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + 2. `group_info: { name: string, muc_jid: string | nil }` + + **Returns:** `true | nil, error: string` + +- #### `get_members(group_id)` {#get_members} + + Retrieves the list of members in a given group. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** `group_members: {string}` + +- #### `exists(group_id)` {#exists} + + Returns whether or not a group exists. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** `group_exists: boolean` + +- #### `get_user_groups(username)` {#get_user_groups} + + Lists which groups a given user is a part of. + + **Parameters:** + + 1. `username: string`: Node part of the user's JID. + + **Returns:** `user_groups: {string}` + +- #### `delete(group_id)` {#delete} + + Deletes a given group and its associated group chats. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** `true | nil, error: string` + +- #### `add_member(group_id, username, delay_update)` {#add_member} + + Adds a member to a given group, optionally delaying subscriptions until [`sync`](#sync) is called. + + ::: {.alert .alert-info} + This function emits a [`group-user-added`](#group-user-added) event on successful execution. + ::: + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + 2. `delay_update: boolean | nil`: Do not update subscriptions until [`sync`](#sync) is called. Defaults to `false`. + + **Returns:** `true | nil, error: string` + +- #### `remove_member(group_id, username)` {#remove_member} + + Removes a member from a given group. + + ::: {.alert .alert-info} + This function emits a [`group-user-removed`](#group-user-removed) event on successful execution. + ::: + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + 2. `username: string`: Node part of the user's JID. + + **Returns:** `true | nil, error: string` + +- #### `sync(group_id)` {#sync} + + Updates group subscriptions (used to apply pending changes from [`add_member`](#add_member)). + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** `nil` + +- #### `add_group_chat(group_id, name)` {#add_group_chat} + + Creates a new group chat for a given group. + + ::: {.alert .alert-info} + Its JID will be `<`[`util.id.short`](https://prosody.im/doc/developers/util/id)`>@<`[`option:groups_muc_host`](#configuration)`>`. + ::: + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + 2. `name: string`: Desired name of the group chat. + + **Returns:** + + ```lua + muc: { + jid: string, + name: string, + } + | nil, error: string + ``` + +- #### `remove_group_chat(group_id, muc_id)` {#remove_group_chat} + + Removes a group chat for a given group. + + ::: {.alert .alert-info} + This function emits a [`group-chat-removed`](#group-chat-removed) event on successful execution. + ::: + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + 2. `muc_id: string`: Node part of the MUC JID. + + **Returns:** `true | nil, error: string` + +- #### `get_group_chats(group_id)` {#get_group_chats} + + Lists group chats associated to a given group. + + ::: {.alert .alert-warning} + Make sure to check the `deleted` property on each chat as this function might return information about deleted chats. + ::: + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** + + ```lua + group_chats: { + { + id: string, -- muc_id (node part of the MUC JID) + jid: string, + name: string, + deleted: boolean, + } + } + | nil + ``` + +- #### `emit_member_events(group_id)` {#emit_member_events} + + Emits [`group-user-added`](#group-user-added) events for every member of a group. + + **Parameters:** + + 1. `group_id: string`: Node part of the group's JID. + + **Returns:** `true | false, error: string` + +- #### `groups()` {#groups} + + Returns info about all groups (for every `group_id` key, the value is the equivalent of calling `get_info(group_id)`). + + **Returns:** + + ```lua + groups: { + <group_id>: { + name: string, + muc_jid: string | nil + } + } + ``` + + (Where `<group_id>` is a + +### Emitted events {#events} + +- #### `group-user-added` {#group-user-added} + + Emitted on successful [`add_member`](#add_member) and on [`emit_member_events`](#emit_member_events). + + **Payload structure:** + + ```lua + { + id: string, -- group_id (node part of the group's JID) + user: string, -- username (node part of the user's JID) + host: string, -- <module.host> + group_info: { + name: string, + muc_jid: string | nil, + mucs: {string} | nil, + }, + } + ``` + +- #### `group-user-removed` {#group-user-removed} + + Emitted on successful [`remove_member`](#remove_member). + + **Payload structure:** + + ```lua + { + id: string, -- group_id (node part of the group's JID) + user: string, -- username (node part of the user's JID) + host: string, -- <module.host> + group_info: { + name: string, + muc_jid: string | nil, + mucs: {string} | nil, + }, + } + ``` + +- #### `group-chat-added` {#group-chat-added} + + Emitted on successful [`add_group_chat`](#add_group_chat). + + **Payload structure:** + + ```lua + { + group_id: string, + group_info: { + name: string, + mucs: {string}, + }, + muc: { + jid: string, + name: string, + }, + } + ``` + +- #### `group-chat-removed` {#group-chat-removed} + + Emitted on successful [`remove_group_chat`](#remove_group_chat). + + **Payload structure:** + + ```lua + { + group_id: string, -- group_id (node part of the group's JID) + group_info: { + name: string, + mucs: {string}, + }, + muc: { + id: string, -- muc_id (node part of the MUC JID) + jid: string, + }, + } + ``` + +[`mod_groups`]: https://prosody.im/doc/modules/mod_groups "mod_groups – Prosody IM"
--- a/mod_host_guard/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Granular remote host blacklisting plugin -... - -Details -======= - -As often it's undesiderable to employ only whitelisting logics in public -environments, this module let's you more selectively restrict access to -your hosts (component or server host) either disallowing access -completely (with optional exceptions) or blacklisting certain sources. - -Usage -===== - -Copy the plugin into your prosody's modules directory. And add it -between your enabled modules into the global section (modules\_enabled): - -- The plugin can work either by blocking all remote access (s2s) to a - certain resource with optional exceptions (useful for components) -- Or by selectively blocking certain remote hosts through blacklisting - (by using host\_guard\_selective and host\_guard\_blacklisting) - -Configuration -============= - - Option name Description - ----------------------------------- --------------------------------------------------------------------------------------------------- - host\_guard\_blockall A list of local hosts to protect from incoming s2s - host\_guard\_blockall\_exceptions A list of remote hosts that are always allowed to access hosts listed in host\_guard\_blockall - host\_guard\_selective A list of local hosts to allow selective filtering (blacklist) of incoming s2s connections - host\_guard\_blacklist A blacklist of remote hosts that are not allowed to access hosts listed in host\_guard\_selective - -Example -------- - -``` {.lua} - -host_guard_blockall = { "no_access.yourhost.com", "no_access2.yourhost.com" } -- insert here the local hosts where you want to forbid all remote traffic to. -host_guard_blockall_exceptions = { "i_can_access.no_access.yourhost.com" } -- optional exceptions for the above. -host_guard_selective = { "no_access_from_blsted.myhost.com", "no_access_from_blsted.mycomponent.com" } -- insert here the local hosts where you want to employ blacklisting. -host_guard_blacklist = { "remoterogueserver.com", "remoterogueserver2.com" } -- above option/mode mandates the use of a blacklist, you may blacklist remote servers here. -``` - -The above is updated when the server configuration is reloaded so that -you don't need to restart the server. - -Compatibility -============= - -- Works with 0.8.x, successive versions and trunk.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_host_guard/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,53 @@ +--- +labels: +- 'Stage-Stable' +summary: Granular remote host blacklisting plugin +... + +Details +======= + +As often it's undesiderable to employ only whitelisting logics in public +environments, this module let's you more selectively restrict access to +your hosts (component or server host) either disallowing access +completely (with optional exceptions) or blacklisting certain sources. + +Usage +===== + +Copy the plugin into your prosody's modules directory. And add it +between your enabled modules into the global section (modules\_enabled): + +- The plugin can work either by blocking all remote access (s2s) to a + certain resource with optional exceptions (useful for components) +- Or by selectively blocking certain remote hosts through blacklisting + (by using host\_guard\_selective and host\_guard\_blacklisting) + +Configuration +============= + + Option name Description + ----------------------------------- --------------------------------------------------------------------------------------------------- + host\_guard\_blockall A list of local hosts to protect from incoming s2s + host\_guard\_blockall\_exceptions A list of remote hosts that are always allowed to access hosts listed in host\_guard\_blockall + host\_guard\_selective A list of local hosts to allow selective filtering (blacklist) of incoming s2s connections + host\_guard\_blacklist A blacklist of remote hosts that are not allowed to access hosts listed in host\_guard\_selective + +Example +------- + +``` {.lua} + +host_guard_blockall = { "no_access.yourhost.com", "no_access2.yourhost.com" } -- insert here the local hosts where you want to forbid all remote traffic to. +host_guard_blockall_exceptions = { "i_can_access.no_access.yourhost.com" } -- optional exceptions for the above. +host_guard_selective = { "no_access_from_blsted.myhost.com", "no_access_from_blsted.mycomponent.com" } -- insert here the local hosts where you want to employ blacklisting. +host_guard_blacklist = { "remoterogueserver.com", "remoterogueserver2.com" } -- above option/mode mandates the use of a blacklist, you may blacklist remote servers here. +``` + +The above is updated when the server configuration is reloaded so that +you don't need to restart the server. + +Compatibility +============= + +- Works with 0.8.x, successive versions and trunk.
--- a/mod_host_status_check/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- Stage-Beta -summary: Host status check -... - -Introduction -============ - -This module allows you to monitor the state of hosts and components in your Prosody server. For example, -it will track whether components are connected and (if the component supports it) listen for heartbeats -sent by the component to indicate that it is functioning. - -This module does not expose any interface to access the status information itself. See [mod\_http\_host\_status\_check] -for a module that allows you to access host status information over HTTP(S). - -Configuration -============= - -There are no configuration options for this module. - -You should enable it on every host that you want to monitor, by adding it to modules\_enabled. Note -that to monitor components, adding to the global modules\_enabled list will not suffice - you will -need to add it to the component's own modules\_enabled, like this: - -``` {.lua} -Component "mycomponent.example.com" - modules_enabled = { "host_status_check" } -``` - -Compatibility -============= - -Works with Prosody 0.9.x and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_host_status_check/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,34 @@ +--- +labels: +- Stage-Beta +summary: Host status check +... + +Introduction +============ + +This module allows you to monitor the state of hosts and components in your Prosody server. For example, +it will track whether components are connected and (if the component supports it) listen for heartbeats +sent by the component to indicate that it is functioning. + +This module does not expose any interface to access the status information itself. See [mod\_http\_host\_status\_check] +for a module that allows you to access host status information over HTTP(S). + +Configuration +============= + +There are no configuration options for this module. + +You should enable it on every host that you want to monitor, by adding it to modules\_enabled. Note +that to monitor components, adding to the global modules\_enabled list will not suffice - you will +need to add it to the component's own modules\_enabled, like this: + +``` {.lua} +Component "mycomponent.example.com" + modules_enabled = { "host_status_check" } +``` + +Compatibility +============= + +Works with Prosody 0.9.x and later.
--- a/mod_host_status_heartbeat/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -labels: -- Stage-Beta -summary: Host status heartbeat -... - -Introduction -============ - -This module integrates with [mod\_host\_status\_check] to provide heartbeats at regular intervals. - -The only time you will generally want this, is if you are using [mod\_component\_client] to run Prosody as -an external component of another Prosody server that has [mod\_host\_status\_check] loaded and waiting for -heartbeats. - -Alternatively you can run this on the same Prosody host as [mod\_http\_host\_status\_check] and it will simply -update a variable periodically to indicate that Prosody and timers are functional. - -Configuration -============= - -The following configuration options are supported: - -```{.lua} --- The number of seconds to wait between sending heartbeats -status_check_heartbeat_interval = 5 - --- Set this to "remote" (the default) if you are using mod_component_client --- and you want to send a heartbeat to a remote server. Otherwise --- set it to "local" to send to mod_host_status_check on the same server. -status_check_heartbeat_mode = "remote" -``` - -Compatibility -============= - -Works with Prosody 0.9.x and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_host_status_heartbeat/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,37 @@ +--- +labels: +- Stage-Beta +summary: Host status heartbeat +... + +Introduction +============ + +This module integrates with [mod\_host\_status\_check] to provide heartbeats at regular intervals. + +The only time you will generally want this, is if you are using [mod\_component\_client] to run Prosody as +an external component of another Prosody server that has [mod\_host\_status\_check] loaded and waiting for +heartbeats. + +Alternatively you can run this on the same Prosody host as [mod\_http\_host\_status\_check] and it will simply +update a variable periodically to indicate that Prosody and timers are functional. + +Configuration +============= + +The following configuration options are supported: + +```{.lua} +-- The number of seconds to wait between sending heartbeats +status_check_heartbeat_interval = 5 + +-- Set this to "remote" (the default) if you are using mod_component_client +-- and you want to send a heartbeat to a remote server. Otherwise +-- set it to "local" to send to mod_host_status_check on the same server. +status_check_heartbeat_mode = "remote" +``` + +Compatibility +============= + +Works with Prosody 0.9.x and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_admin_api/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,36 @@ +--- +summary: "admin api from the snikket projects web portal" +labels: +- 'Stage-Beta' +rockspec: + dependencies: + - mod_cloud_notify + - mod_lastlog2 + - mod_groups_internal +... + +Introduction +============ + +...to be done... + +Configuration +============= +There is no configuration for this module, just add it to +modules\_enabled as normal. + +# Compatibility + +Depends on community modules: + + * [mod_cloud_notify] + * [mod_lastlog2] + * [mod_groups_internal] + + Prosody Version Support Status + ---------------- ---------------- + trunk[^1] Works + 0.12 unknown + ---------------- ---------------- + +[^1]: as of 2024-10-23
--- a/mod_http_altconnect/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -This module helps make BOSH and WebSocket connection endpoints -discoverable via the HTTP method described in -[XEP-0156](https://xmpp.org/extensions/xep-0156.html#http).
--- a/mod_http_auth_check/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -summary: 'Test account credentials using HTTP' -... - -Introduction ------------- - -This module lets you test whether a set of credentials are valid, -using Prosody's configured authentication mechanism. - -This is useful as an easy way to allow other (e.g. non-XMPP) applications -to authenticate users using their XMPP credentials. - -Syntax ------- - -To test credentials, issue a simple GET request with HTTP basic auth: - - GET /auth_check HTTP/1.1 - Authorization: Basic <base64(jid:password)> - -Prosody will return a 2xx code on success (user exists and credentials are -correct), or 401 if the credentials are invalid. Any other code may be returned -if there is a problem handling the request. - -### Example usage - -Here follows some example usage using `curl`. - - curl http://prosody.local:5280/auth_check -u user@example.com:secr1t
--- a/mod_http_authentication/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Enforces HTTP Basic authentication across all HTTP endpoints served by Prosody -... - -# mod_http_authentication - -This module enforces HTTP Basic authentication across all HTTP endpoints served by Prosody. - -## Configuration - - Name Default Description - ---------------------------------- --------------------------------- -------------------------------------------------------------------------------------------------------------------------------------- - http\_credentials "minddistrict:secretpassword" The credentials that HTTP clients must provide to access the HTTP interface. Should be a string with the syntax "username:password". - unauthenticated\_http\_endpoints { "/http-bind", "/http-bind/" } A list of paths that should be excluded from authentication. - -## Usage - -This is a global module, so should be added to the global `modules_enabled` option in your config file. It applies to all HTTP virtual hosts. - -## Compatibility - -The module use a new API in Prosody 0.10 and will not work with older -versions. - -## Details - -By Kim Alvefur \<zash@zash.se\>
--- a/mod_http_avatar/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -summary: Serve avatars from HTTP -... - -Introduction -============ - -This module serves avatars from local users who have one set in their -vCard, see XEP-0054 and XEP-0153. - -Configuring -=========== - -Simply load the module. Avatars are then available at -http://<host>:5280/avatar/<username> - - modules_enabled = { - ... - "http_avatar"; - } - -Compatibility -============= - - ------- -------------- - trunk Works - 0.11 Works - 0.10 Should work - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_avatar/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +summary: Serve avatars from HTTP +... + +Introduction +============ + +This module serves avatars from local users who have one set in their +vCard, see XEP-0054 and XEP-0153. + +Configuring +=========== + +Simply load the module. Avatars are then available at +http://<host>:5280/avatar/<username> + + modules_enabled = { + ... + "http_avatar"; + } + +Compatibility +============= + + ------- -------------- + trunk Works + 0.11 Works + 0.10 Should work + ------- --------------
--- a/mod_http_dir_listing/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ ---- -rockspec: - build: - copy_directories: - - http_dir_listing/resources - modules: - mod_http_dir_listing: http_dir_listing/mod_http_dir_listing.lua -summary: HTTP directory listing -... - -Introduction -============ - -This module generates directory listings when invoked by -`mod_http_files`. See [documentation on -`mod_http_files`](http://prosody.im/doc/modules/mod_http_files). - -Configuration -============= - -The module itself doesn't have any configuration of its own, just enable -the it along with `mod_http_files`. - - modules_enabled = { - ... - - "http_files"; - "http_dir_listing"; - } - - http_dir_listing = true; - -The layout, CSS and icons in the `resources/` directory can be -customized or replaced. All resources are cached in memory when the -module is loaded and the images are inlined in the CSS. - -Compatibility -============= - - version status - --------- -------- - trunk Works - 0.12 Works - 0.11 Works
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_dir_listing/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,44 @@ +--- +rockspec: + build: + copy_directories: + - http_dir_listing/resources + modules: + mod_http_dir_listing: http_dir_listing/mod_http_dir_listing.lua +summary: HTTP directory listing +... + +Introduction +============ + +This module generates directory listings when invoked by +`mod_http_files`. See [documentation on +`mod_http_files`](http://prosody.im/doc/modules/mod_http_files). + +Configuration +============= + +The module itself doesn't have any configuration of its own, just enable +the it along with `mod_http_files`. + + modules_enabled = { + ... + + "http_files"; + "http_dir_listing"; + } + + http_dir_listing = true; + +The layout, CSS and icons in the `resources/` directory can be +customized or replaced. All resources are cached in memory when the +module is loaded and the images are inlined in the CSS. + +Compatibility +============= + + version status + --------- -------- + trunk Works + 0.12 Works + 0.11 Works
--- a/mod_http_dir_listing2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ ---- -summary: HTTP directory listing -rockspec: - build: - copy_directories: - - resources ---- - -Introduction -============ - -This module generates directory listings when invoked by -`mod_http_files`. See [documentation on -`mod_http_files`](http://prosody.im/doc/modules/mod_http_files). - -It uses the [`util.interpolation`][doc:developers:util:interpolation] -template engine included with Prosody since 0.10. - -Configuration -============= - -The module itself doesn't have any configuration of its own, just enable -the it along with `mod_http_files`. - - modules_enabled = { - ... - - "http_files"; - "http_dir_listing"; - } - - http_dir_listing = true; - -The layout, CSS and icons in the `resources/` directory can be -customized or replaced. All resources are cached in memory when the -module is loaded and the images are inlined in the CSS. - -Compatibility -============= - - ------- -------------- - trunk Works - 0.10 Works - 0.9 Doesn't work - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_dir_listing2/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,45 @@ +--- +summary: HTTP directory listing +rockspec: + build: + copy_directories: + - resources +--- + +Introduction +============ + +This module generates directory listings when invoked by +`mod_http_files`. See [documentation on +`mod_http_files`](http://prosody.im/doc/modules/mod_http_files). + +It uses the [`util.interpolation`][doc:developers:util:interpolation] +template engine included with Prosody since 0.10. + +Configuration +============= + +The module itself doesn't have any configuration of its own, just enable +the it along with `mod_http_files`. + + modules_enabled = { + ... + + "http_files"; + "http_dir_listing"; + } + + http_dir_listing = true; + +The layout, CSS and icons in the `resources/` directory can be +customized or replaced. All resources are cached in memory when the +module is loaded and the images are inlined in the CSS. + +Compatibility +============= + + ------- -------------- + trunk Works + 0.10 Works + 0.9 Doesn't work + ------- --------------
--- a/mod_http_favicon/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -summary: HTTP favicon -... - -Introduction -============ - -This simple module serves a `favicon.ico` from prosodys HTTP server and -nothing else. - -Configuring -=========== - -Simply load the module. The icon can be replaced by adding a `favicon` -option to the config. - - modules_enabled = { - ... - "favicon"; - } - - favicon = "/path/to/my-favicon.ico" -- Override the built in one - -Compatibility -============= - - ------- -------------- - trunk Works - 0.9 Works - 0.8 Doesn't work - ------- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_favicon/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,31 @@ +--- +summary: HTTP favicon +... + +Introduction +============ + +This simple module serves a `favicon.ico` from prosodys HTTP server and +nothing else. + +Configuring +=========== + +Simply load the module. The icon can be replaced by adding a `favicon` +option to the config. + + modules_enabled = { + ... + "favicon"; + } + + favicon = "/path/to/my-favicon.ico" -- Override the built in one + +Compatibility +============= + + ------- -------------- + trunk Works + 0.9 Works + 0.8 Doesn't work + ------- --------------
--- a/mod_http_host_status_check/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- Stage-Beta -summary: HTTP Host Status Check -... - -Introduction -============ - -This module exposes serves over HTTP the information collected by [mod\_host\_status\_check] and -[mod\_host\_status\_heartbeat] in a convenient format for automated monitoring tools. - -Configuration -============= - -[mod\_http\_status\_check] relies on Prosodys HTTP server and mod\_http for -serving HTTP requests. See [Prosodys HTTP server -documentation][doc:http] for information about how to -configure ports, HTTP Host names etc. - -Simply add this module to modules\_enabled for the host you would like to serve it from. - -There is a single configuration option: - -``` {.lua} - -- The maximum number of seconds that a host can go without sending a heartbeat, - -- before we mark it as TIMEOUT (default: 5) - status_check_heartbeat_threshold = 5; -``` - -Compatibility -============= - -Works with Prosody 0.9.x and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_host_status_check/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,34 @@ +--- +labels: +- Stage-Beta +summary: HTTP Host Status Check +... + +Introduction +============ + +This module exposes serves over HTTP the information collected by [mod\_host\_status\_check] and +[mod\_host\_status\_heartbeat] in a convenient format for automated monitoring tools. + +Configuration +============= + +[mod\_http\_status\_check] relies on Prosodys HTTP server and mod\_http for +serving HTTP requests. See [Prosodys HTTP server +documentation][doc:http] for information about how to +configure ports, HTTP Host names etc. + +Simply add this module to modules\_enabled for the host you would like to serve it from. + +There is a single configuration option: + +``` {.lua} + -- The maximum number of seconds that a host can go without sending a heartbeat, + -- before we mark it as TIMEOUT (default: 5) + status_check_heartbeat_threshold = 5; +``` + +Compatibility +============= + +Works with Prosody 0.9.x and later.
--- a/mod_http_index/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -Introduction -============ - -This module produces a list of enabled HTTP "apps" exposed from Prosody -at `http://example.org:5280/`, e.g. [mod\_http\_muc\_log], -[mod\_http\_files][doc:modules:mod_http_files] or -[mod\_admin\_web]. If you think Prosodys default "root" web page (a -404 error usually) is boring, this might be the module for you! :) - -Configuration -============= - -Install and enable like any other module. Also see [Prosodys HTTP -documentation](https://prosody.im/doc/http). - -``` {.lua} -modules_enabled = { - -- other modules - "http_index"; -} -``` - -# Advanced - -## Listing all items - -By default only HTTP apps that include a human-readable title are -listed. This filtering can be disabled by setting: - -```lua -http_index_list_all = true -``` - -## Template - -The template can be customized by copying the included `http_index.html` -and pointing to it with the `http_index_template` setting: - -``` lua -http_index_template = "/path/to/template.html" -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_index/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,51 @@ +--- +summary: Generate an index of local HTTP services +labels: +- Stage-Beta +rockspec: + build: + copy_directories: + - html +--- + +Introduction +============ + +This module produces a list of enabled HTTP "apps" exposed from Prosody +at `http://example.org:5280/`, e.g. [mod\_http\_muc\_log], +[mod\_http\_files][doc:modules:mod_http_files] or +[mod\_admin\_web]. If you think Prosodys default "root" web page (a +404 error usually) is boring, this might be the module for you! :) + +Configuration +============= + +Install and enable like any other module. Also see [Prosodys HTTP +documentation](https://prosody.im/doc/http). + +``` {.lua} +modules_enabled = { + -- other modules + "http_index"; +} +``` + +# Advanced + +## Listing all items + +By default only HTTP apps that include a human-readable title are +listed. This filtering can be disabled by setting: + +```lua +http_index_list_all = true +``` + +## Template + +The template can be customized by copying the included `http_index.html` +and pointing to it with the `http_index_template` setting: + +``` lua +http_index_template = "/path/to/template.html" +```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_index/html/http_index.html Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<meta name="generator" value="prosody/{prosody_version} mod_{mod_name}"> +<link rel="canonical" href="{canonical}"> +<title>{title}</title> +<style> +:link,:visited{color:#3465a4;text-decoration:none;} +:link:hover,:visited:hover{color:#6197df;} +body{background-color:#eeeeec;margin:1ex 0;padding-bottom:3em;font-family:Arial,Helvetica,sans-serif;} +ul,ol{padding:0;} +li{list-style:none;} +hr{visibility:hidden;clear:both;} +br{clear:both;} +header,footer{margin:1ex 1em;} +footer{font-size:smaller;color:#babdb6;} +nav{font-size:large;margin:1ex 1ex;clear:both;line-height:1.5em;} +footer nav .up{display:none;} +@media screen and (min-width: 460px) { +nav {font-size:x-large;margin:1ex 1em;} +} +nav a{padding:1ex} +nav li,nav dt{margin:1ex} +.content{background-color:white;padding:1em;list-style-position:inside;} +@media (prefers-color-scheme: dark) { +html{color:#eee} +body{background-color:#161616} +.content{background-color:#1c1c1c} +footer{color:#444} +} +</style> +</head> +<body> +<header> +<h1>Prosody IM</h1> +<h2>HTTP Services</h2> +</header> +<hr> +<div class="content"> +<nav> +<ul>{items# +<li><a href="{item.url}" title="{item.module}">{item.title?{item.name}}</a></li>} +</ul> +</nav> +</div> +<hr> +<footer> +<br> +<div class="powered-by">Prosody {prosody_version?}</div> +</footer> +</body> +</html>
--- a/mod_http_index/http_index.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<meta charset="utf-8"> -<meta name="viewport" content="width=device-width, initial-scale=1"> -<meta name="generator" value="prosody/{prosody_version} mod_{mod_name}"> -<link rel="canonical" href="{canonical}"> -<title>{title}</title> -<style> -:link,:visited{color:#3465a4;text-decoration:none;} -:link:hover,:visited:hover{color:#6197df;} -body{background-color:#eeeeec;margin:1ex 0;padding-bottom:3em;font-family:Arial,Helvetica,sans-serif;} -ul,ol{padding:0;} -li{list-style:none;} -hr{visibility:hidden;clear:both;} -br{clear:both;} -header,footer{margin:1ex 1em;} -footer{font-size:smaller;color:#babdb6;} -nav{font-size:large;margin:1ex 1ex;clear:both;line-height:1.5em;} -footer nav .up{display:none;} -@media screen and (min-width: 460px) { -nav {font-size:x-large;margin:1ex 1em;} -} -nav a{padding:1ex} -nav li,nav dt{margin:1ex} -.content{background-color:white;padding:1em;list-style-position:inside;} -@media (prefers-color-scheme: dark) { -html{color:#eee} -body{background-color:#161616} -.content{background-color:#1c1c1c} -footer{color:#444} -} -</style> -</head> -<body> -<header> -<h1>Prosody IM</h1> -<h2>HTTP Services</h2> -</header> -<hr> -<div class="content"> -<nav> -<ul>{items# -<li><a href="{item.url}" title="{item.module}">{item.title?{item.name}}</a></li>} -</ul> -</nav> -</div> -<hr> -<footer> -<br> -<div class="powered-by">Prosody {prosody_version?}</div> -</footer> -</body> -</html>
--- a/mod_http_libjs/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'Serve common Javascript libraries' -... - -Introduction -============ - -This module serves common static CSS and Javascript libraries from the -filesystem, allowing other HTTP modules to easily reference them. - -The default configuration works out of the box with Debian (and derivatives) -`libjs-*` packages, such as libjs-jquery and libjs-bootstrap. - -You can override the filesystem location using the `libjs_path` configuration -option. The default is `/usr/share/javascript`. - -Compatibility -============= - - ----- ------- - 0.11 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_libjs/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,25 @@ +--- +labels: +- 'Stage-Stable' +summary: 'Serve common Javascript libraries' +... + +Introduction +============ + +This module serves common static CSS and Javascript libraries from the +filesystem, allowing other HTTP modules to easily reference them. + +The default configuration works out of the box with Debian (and derivatives) +`libjs-*` packages, such as `libjs-jquery` and `libjs-bootstrap`. + +You can override the filesystem location using the `libjs_path` configuration +option. The default is `/usr/share/javascript`. + +Compatibility +============= + + Prosody-Version Status + --------------- -------------------- + trunk Works as of 24-12-08 + 0.12 Works
--- a/mod_http_logging/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -This module produces more detailed HTTP logs for Prosodys built-in HTTP -server. The format is similar to that of Apache and go into Prosodys -normal logs at the `info` level.
--- a/mod_http_muc_log/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Provides a web interface to stored chatroom logs -rockspec: - build: - copy_directories: - - res - - static -... - -Introduction -============ - -This module provides a built-in web interface to view chatroom logs -stored by [mod\_muc\_mam]. - -Installation -============ - -Same as any other module, be sure to include the HTML template -`http_muc_log.html` alongside `mod_http_muc_log.lua`. - -Configuration -============= - -For example: - -``` lua -Component "conference.example.com" "muc" -modules_enabled = { - "muc_mam"; - "http_muc_log"; -} -storage = { - muc_log = "sql"; -- for example -} -``` - -The web interface would then be reachable at the address: - - http://conference.example.com:5280/muc_log/ - -See [the page about Prosodys HTTP server][doc:http] for info about the -address. - -## Styling - -The default HTML template lives in `http_muc_log.html` in the same -directory as the module, but can be changed by setting -`http_muc_log_template` to point to a different file. The same template, -with different input is used for every view. - -The module uses [util.interpolation][doc:developers:util:interpolation] -for rendering templates, with the pattern `"%b{}"` and HTML / XML -escaping enabled. - -## Default view - -To link to the latest day instead of calendar from the room listing -page: - -```lua -http_muc_log_default_view = "latest" -``` - -## Inline images - -Inline images can optionally be shown. This is disabled by default for -privacy reasons. - -``` {.lua} -http_muc_log_show_images = true -``` - -## Calendar optimization - -The calendar view relies on an optional part of the Prosody archive -storage API that provides a list of every valid date. If this is -unavailable then the module queries for the first and the last messages -and assumes that every date between those is valid. This may lead to -many empty pages in case the logs are sparse. - -This optimization can be turned off, to get a more accurate calendar -view, but it will likely be very slow. - -``` {.lua} -http_muc_log_lazy_calendar = false -``` - -## Pinned chatrooms - -The room list page is normally sorted by address, rooms having a -description before those that don't. To override this, or pin certain -rooms to the top: - -``` lua -http_muc_log_list_order = { - "general@channels.example.com", - "support@channels.example.com", -} -``` - -Compatibility -============= - -Requires Prosody 0.11 or later and a storage backend with support for -stanza archives. See [mod\_storage\_muc\_log] for using legacy data from -[mod\_muc\_log].
--- a/mod_http_oauth2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,289 +0,0 @@ ---- -labels: -- Stage-Alpha -rockspec: - build: - copy_directories: - - html -summary: OAuth 2.0 Authorization Server API ---- - -## Introduction - -This module implements an [OAuth2](https://oauth.net/2/)/[OpenID Connect -(OIDC)](https://openid.net/connect/) Authorization Server on top of -Prosody's usual internal authentication backend. - -OAuth and OIDC are web standards that allow you to provide clients and -third-party applications limited access to your account, without sharing your -password with them. - -With this module deployed, software that supports OAuth can obtain -"access tokens" from Prosody which can then be used to connect to XMPP -accounts using the [OAUTHBEARER SASL mechanism][rfc7628] or via non-XMPP -interfaces such as [mod_rest]. - -Although this module has been around for some time, it has recently been -significantly extended and largely rewritten to support OAuth/OIDC more fully. - -As of April 2023, it should be considered **alpha** stage. It works, we have -tested it, but it has not yet seen wider review, testing and deployment. At -this stage we recommend it for experimental and test deployments only. For -specific information, see the [deployment notes section](#deployment-notes) -below. - -Known client implementations: - -- [example shell script for mod_rest](https://hg.prosody.im/prosody-modules/file/tip/mod_rest/example/rest.sh) -- *(we need you!)* - -Support for [OAUTHBEARER][rfc7628] has been added to the Lua XMPP -library, [verse](https://code.matthewwild.co.uk/verse). If you know of -additional implementations, or are motivated to work on one, please let -us know! We'd be happy to help (e.g. by providing a test server). - -## Standards support - -Notable supported standards: - -- [RFC 6749: The OAuth 2.0 Authorization Framework](https://www.rfc-editor.org/rfc/rfc6749) -- [RFC 7009: OAuth 2.0 Token Revocation](https://www.rfc-editor.org/rfc/rfc7009) -- [RFC 7591: OAuth 2.0 Dynamic Client Registration](https://www.rfc-editor.org/rfc/rfc7591.html) -- [RFC 7628: A Set of Simple Authentication and Security Layer (SASL) Mechanisms for OAuth](https://www.rfc-editor.org/rfc/rfc7628) -- [RFC 7636: Proof Key for Code Exchange by OAuth Public Clients](https://www.rfc-editor.org/rfc/rfc7636) -- [RFC 7662: OAuth 2.0 Token Introspection](https://www.rfc-editor.org/rfc/rfc7662) -- [RFC 8628: OAuth 2.0 Device Authorization Grant](https://www.rfc-editor.org/rfc/rfc8628) -- [RFC 9207: OAuth 2.0 Authorization Server Issuer Identification](https://www.rfc-editor.org/rfc/rfc9207.html) -- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html) -- [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html) (_partial, e.g. missing JWKS_) -- [OpenID Connect Dynamic Client Registration 1.0](https://openid.net/specs/openid-connect-registration-1_0.html) - -## Configuration - -### Interface - -The module presents a web page to users to allow them to authenticate when -a client requests access. Built-in pages are provided, but you may also theme -or entirely override them. - -This module honours the `site_name` configuration option that is also used by -a number of other modules: - -```lua -site_name = "My XMPP Server" -``` - -To provide custom templates, specify the path to the template directory: - -```lua -oauth2_template_path = "/etc/prosody/custom-oauth2-templates" -``` - -If you know what features your templates use use you can adjust the -`Content-Security-Policy` header to only allow what is needed: - -```lua -oauth2_security_policy = "default-src 'self'" -- this is the default -``` - -### Token parameters - -The following options configure the lifetime of tokens issued by the module. -The defaults are recommended. - -```lua -oauth2_access_token_ttl = 3600 -- one hour -oauth2_refresh_token_ttl = 604800 -- one week -``` - -### Dynamic client registration - -To allow users to connect any compatible software, you should enable dynamic -client registration. - -Dynamic client registration can be enabled by configuring a JWT key. Algorithm -defaults to *HS256*, lifetime defaults to forever. - -```lua -oauth2_registration_key = "securely generated JWT key here" -oauth2_registration_algorithm = "HS256" -oauth2_registration_ttl = nil -- unlimited by default -``` - -Registering a client is described in -[RFC7591](https://www.rfc-editor.org/rfc/rfc7591.html). - -In addition to the requirements in the RFC, the following requirements -are enforced: - -`client_name` -: **MUST** be present, is shown to users in consent screen. - -`client_uri` -: **MUST** be present and **MUST** be a `https://` URL. - -`redirect_uris` - -: **MUST** contain at least one valid URI. Different rules apply - depending on the value of `application_type`, see below. - -`application_type` - -: Optional, defaults to `web`. Determines further restrictions for - `redirect_uris`. The following values are supported: - - `web` *(default)* - : For web clients. With this, `redirect_uris` **MUST** be - `https://` URIs and **MUST** use the same hostname part as the - `client_uri`. - - `native` - : For native e.g. desktop clients etc. `redirect_uris` **MUST** - match one of: - - - Loopback HTTP URI, e.g. `http://127.0.0.1/` or - `http://[::1]` - - Application-specific scheme, e.g. `com.example.app:/` - - The special OOB URI `urn:ietf:wg:oauth:2.0:oob` - -`tos_uri`, `policy_uri` -: Informative URLs pointing to Terms of Service and Service Policy - document **MUST** use the same scheme (i.e. `https://`) and hostname - as the `client_uri`. - -#### Registration Examples - -In short registration works by POST-ing a JSON structure describing your -client to an endpoint: - -``` bash -curl -sSf https://xmpp.example.net/oauth2/register \ - -H Content-Type:application/json \ - -H Accept:application/json \ - --data ' -{ - "client_name" : "My Application", - "client_uri" : "https://app.example.com/", - "redirect_uris" : [ - "https://app.example.com/redirect" - ] -} -' -``` - -Another example with more fields: - -``` bash -curl -sSf https://xmpp.example.net/oauth2/register \ - -H Content-Type:application/json \ - -H Accept:application/json \ - --data ' -{ - "application_type" : "native", - "client_name" : "Desktop Chat App", - "client_uri" : "https://app.example.org/", - "contacts" : [ - "support@example.org" - ], - "policy_uri" : "https://app.example.org/about/privacy", - "redirect_uris" : [ - "http://localhost:8080/redirect", - "org.example.app:/redirect" - ], - "scope" : "xmpp", - "software_id" : "32a0a8f3-4016-5478-905a-c373156eca73", - "software_version" : "3.4.1", - "tos_uri" : "https://app.example.org/about/terms" -} -' -``` - -### Supported flows - -- Authorization Code grant, optionally with Proof Key for Code Exchange -- Device Authorization Grant -- Resource owner password grant *(disabled by default)* -- Implicit flow *(disabled by default)* -- Refresh Token grants - -Various flows can be disabled and enabled with -`allowed_oauth2_grant_types` and `allowed_oauth2_response_types`: - -```lua --- These examples reflect the defaults -allowed_oauth2_grant_types = { - "authorization_code"; -- authorization code grant - "device_code"; - -- "password"; -- resource owner password grant disabled by default -} - -allowed_oauth2_response_types = { - "code"; -- authorization code flow - -- "token"; -- implicit flow disabled by default -} -``` - -The [Proof Key for Code Exchange][RFC 7636] mitigation method is -required by default but can be made optional: - -```lua -oauth2_require_code_challenge = false -- default is true -``` - -Further, individual challenge methods can be enabled or disabled: - -```lua --- These reflects the default -allowed_oauth2_code_challenge_methods = { - -- "plain"; -- insecure but backwards-compatible - "S256"; -} -``` - -### Policy documents - -Links to Terms of Service and Service Policy documents can be advertised -for use by OAuth clients: - -```lua -oauth2_terms_url = "https://example.com/terms-of-service.html" -oauth2_policy_url = "https://example.com/service-policy.pdf" --- These are unset by default -``` - -## Deployment notes - -### Access management - -This module does not provide an interface for users to manage what they have -granted access to their account! (e.g. to view and revoke clients they have -previously authorized). It is recommended to join this module with -[mod_client_management] to provide such access. However, at the time of writing, -no XMPP clients currently support the protocol used by that module. We plan to -work on additional interfaces in the future. - -### Scopes - -OAuth supports "scopes" as a way to grant clients limited access. - -There are currently no standard scopes defined for XMPP. This is -something that we intend to change, e.g. by definitions provided in a -future XEP. This means that clients you authorize currently have to -choose between unrestricted access to your account (including the -ability to change your password and lock you out!) and zero access. So, -for now, while using OAuth clients can prevent leaking your password to -them, it is not currently suitable for connecting untrusted clients to -your account. - -As a first step, the `xmpp` scope is supported, and corresponds to -whatever permissions the user would have when logged in over XMPP. - -Further, known Prosody roles can be used as scopes. - -OpenID scopes such as `openid` and `profile` can be used for "Login -with XMPP" without granting access to more than limited profile details. - -## Compatibility - -Requires Prosody trunk (April 2023), **not** compatible with Prosody 0.12 or -earlier.
--- a/mod_http_pep_avatar/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ ---- -summary: Serve PEP avatars from HTTP ---- - -# Introduction - -This module serves avatars from local users who have published -[XEP-0084: User Avatar] via [PEP][doc:modules:mod_pep]. - -# Configuring - -Simply load the module. Avatars are then available at -`http://<host>:5280/pep_avatar/<username>` - - modules_enabled = { - ... - "http_pep_avatar"; - } - -# Access - -Users must [configure] their Avatar PEP nodes to be public, otherwise -access is denied. - -# Compatibility - - ------- --------------- - trunk Works - 0.11 Works - 0.10 Does not work - ------- --------------- - -[configure]: https://xmpp.org/extensions/xep-0060.html#owner-configure
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_pep_avatar/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,33 @@ +--- +summary: Serve PEP avatars from HTTP +--- + +# Introduction + +This module serves avatars from local users who have published +[XEP-0084: User Avatar] via [PEP][doc:modules:mod_pep]. + +# Configuring + +Simply load the module. Avatars are then available at +`http://<host>:5280/pep_avatar/<username>` + + modules_enabled = { + ... + "http_pep_avatar"; + } + +# Access + +Users must [configure] their Avatar PEP nodes to be public, otherwise +access is denied. + +# Compatibility + + ------- --------------- + trunk Works + 0.11 Works + 0.10 Does not work + ------- --------------- + +[configure]: https://xmpp.org/extensions/xep-0060.html#owner-configure
--- a/mod_http_prebind/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Implements BOSH pre-bind -... - -For why this can be useful, see -https://metajack.im/2009/12/14/fastest-xmpp-sessions-with-http-prebinding/ - -# To enable this module - -Add `"http_prebind"` to `modules_enabled` on an anonymous virtual host. - -This only works on anonymous ones for now.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_prebind/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,14 @@ +--- +labels: +- 'Stage-Alpha' +summary: Implements BOSH pre-bind +... + +For why this can be useful, see +https://metajack.im/2009/12/14/fastest-xmpp-sessions-with-http-prebinding/ + +# To enable this module + +Add `"http_prebind"` to `modules_enabled` on an anonymous virtual host. + +This only works on anonymous ones for now.
--- a/mod_http_rest/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Send XMPP stanzas via REST/HTTP -... - -This module provides a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)ful -method for sending XMPP stanzas. - -This enables you to send stanzas by making HTTP requests to `http://${prosody-url}/rest`. - -**DANGER/ACHTUNG!: This module does NOT enforce any authentication or user-checking. -This means that by default stanzas can be sent *anyone* on behalf of *any* user.** - -You should enable [mod_http_authentication](https://modules.prosody.im/mod_http_authentication.html), -to require authentication for calls made to this module, or alternatively, you -could use a reverse proxy like Nginx. - -# To enable this module - -Add `"http_rest"` to `modules_enabled`, either globally or for a particular virtual -host. - -# How to test: - -You can use curl to make the HTTP request to Prosody, to test whether this -module is working properly: - - curl -k http://localhost:5280/rest -u username:password -H "Content-Type: text/xml" -d '<iq to="pubsub.localhost" type="set" id="4dd1a1e3-ef91-4017-a5aa-eaba0a82eb94-1" from="user@localhost"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="Test mod_rest.lua"><item>Hello World!</item></publish></pubsub></iq>'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_rest/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +labels: +- 'Stage-Alpha' +summary: Send XMPP stanzas via REST/HTTP +... + +This module provides a [REST](https://en.wikipedia.org/wiki/Representational_state_transfer)ful +method for sending XMPP stanzas. + +This enables you to send stanzas by making HTTP requests to `http://${prosody-url}/rest`. + +**DANGER/ACHTUNG!: This module does NOT enforce any authentication or user-checking. +This means that by default stanzas can be sent *anyone* on behalf of *any* user.** + +You should enable [mod_http_authentication](https://modules.prosody.im/mod_http_authentication.html), +to require authentication for calls made to this module, or alternatively, you +could use a reverse proxy like Nginx. + +# To enable this module + +Add `"http_rest"` to `modules_enabled`, either globally or for a particular virtual +host. + +# How to test: + +You can use curl to make the HTTP request to Prosody, to test whether this +module is working properly: + + curl -k http://localhost:5280/rest -u username:password -H "Content-Type: text/xml" -d '<iq to="pubsub.localhost" type="set" id="4dd1a1e3-ef91-4017-a5aa-eaba0a82eb94-1" from="user@localhost"><pubsub xmlns="http://jabber.org/protocol/pubsub"><publish node="Test mod_rest.lua"><item>Hello World!</item></publish></pubsub></iq>'
--- a/mod_http_roster_admin/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Delegate roster management to an external service -... - -*NOTE: THIS MODULE IS RELEASED UNDER THE MOZILLA PUBLIC LICENSE VERSION 2.* - -Normally the XMPP server will store and maintain the users' contact -rosters. This module lets you delegate roster management to an external -service. - -Prosody will make an HTTP request to fetch the roster from the external -service. The service will need to notify Prosody whenever a user's roster -changes, so that Prosody can fetch a new roster for that user. - -## Configuring this module - -This module relies on `mod_storage_memory` and `mod_block_subscriptions`. - -In `.parts/prosody/etc/prosody/prosody.cfg.lua`, where your particular -`VirtualHost` is being configured, add the following: - -``` lua -modules_enabled = { - "http_roster_admin", - "block_subscriptions", - "storage_memory", - "http_files" -} -modules_disabled = { - -- Prosody will get the roster from the backend app, - -- so we disable the default roster module. - "roster" -} -storage = { roster = "memory" } -http_roster_url = "http://localhost/contacts/%s" -- %s will be replaced by an URL-encoded username -``` - -The `http_roster_url` parameter needs to be configured to point to the -URL in the backend application which returns users' contacts rosters. - -In this URL, the pattern `%s` is replaced by an URL-encoded username. - -When the user *john* then connects to Prosody, and `http_roster_url` is -set to “http://app.example.org/contacts/%s”, then Prosody will make a -GET request to http://app.example.org/contacts/john - -## Protocol - -### Fetching rosters (Prosody to web app) - -Prosody considers the web application to always hold the most accurate and up-to-date -version of the user's roster. When a user first connects, Prosody fetches the roster -from the web application and caches it internally. - -Prosody will make a GET request to the URL specified in Prosody's configuration parameter -'http_roster_url'. In this URL, the pattern '%s' is replaced by an URL-encoded username. - -For example, when the user 'john' connects to Prosody, and http_roster_url is set -to "http://app.example.com/contacts/%s", Prosody will make a GET request to "http://app.example.com/contacts/john". - -The web app must return a JSON object, where each key is the JID of a contact, and the corresponding -value is data about that contact. - -If the user 'john' has friends 'marie' and 'michael', the web app would return a HTTP '200 OK' response -with the following contents: - -``` json -{ - "marie@example.com": { - "name": "Marie" - }, - - "michael@example.com": { - "name": "Michael" - } -} -``` - -### Notifying Prosody of roster changes - -The external service needs to notify Prosody whenever a user's roster -changes. To do this, it must make an HTTP POST request to either: - -- http://localhost:5280/roster_admin/refresh -- https://localhost:5281/roster_admin/refresh - -Make sure that the "http_files" module is enabled in Prosody's configuration, -for the above URLs to served. - -Ports 5280/5281 can be firewalled and the web server (i.e. Apache or Nginx) -can be configured to reverse proxy those URLs to for example -https://example.org/http-bind. - -The contents of the POST should be a JSON encoded array of usernames whose -rosters have changed. - -For example, if user ‘john’ became friends with ‘aaron’, both john’s -contact list and aaron’s contact lists have changed: - -``` json -["john", "aaron"] -``` - -When the operation is complete Prosody will reply with a summary of the -operation - a JSON object containing: - -- **status**: either “ok” (success) or “error” (operation completely failed) -- **message**: A human-readable message (for logging and debugging purposes) -- **updated**: The number of rosters successfully updated -- **errors**: The number of rosters that failed to update - -Example: - -``` json -{ - "status": "ok", - "message": "roster update complete", - "updated": 2, - "errors": 0 -} -``` - -Prosody may also return status codes `400` or `500` in case of errors (such -as a missing/malformed body).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_roster_admin/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,126 @@ +--- +labels: +- 'Stage-Beta' +summary: Delegate roster management to an external service +... + +*NOTE: THIS MODULE IS RELEASED UNDER THE MOZILLA PUBLIC LICENSE VERSION 2.* + +Normally the XMPP server will store and maintain the users' contact +rosters. This module lets you delegate roster management to an external +service. + +Prosody will make an HTTP request to fetch the roster from the external +service. The service will need to notify Prosody whenever a user's roster +changes, so that Prosody can fetch a new roster for that user. + +## Configuring this module + +This module relies on `mod_storage_memory` and `mod_block_subscriptions`. + +In `.parts/prosody/etc/prosody/prosody.cfg.lua`, where your particular +`VirtualHost` is being configured, add the following: + +``` lua +modules_enabled = { + "http_roster_admin", + "block_subscriptions", + "storage_memory", + "http_files" +} +modules_disabled = { + -- Prosody will get the roster from the backend app, + -- so we disable the default roster module. + "roster" +} +storage = { roster = "memory" } +http_roster_url = "http://localhost/contacts/%s" -- %s will be replaced by an URL-encoded username +``` + +The `http_roster_url` parameter needs to be configured to point to the +URL in the backend application which returns users' contacts rosters. + +In this URL, the pattern `%s` is replaced by an URL-encoded username. + +When the user *john* then connects to Prosody, and `http_roster_url` is +set to “http://app.example.org/contacts/%s”, then Prosody will make a +GET request to http://app.example.org/contacts/john + +## Protocol + +### Fetching rosters (Prosody to web app) + +Prosody considers the web application to always hold the most accurate and up-to-date +version of the user's roster. When a user first connects, Prosody fetches the roster +from the web application and caches it internally. + +Prosody will make a GET request to the URL specified in Prosody's configuration parameter +'http_roster_url'. In this URL, the pattern '%s' is replaced by an URL-encoded username. + +For example, when the user 'john' connects to Prosody, and http_roster_url is set +to "http://app.example.com/contacts/%s", Prosody will make a GET request to "http://app.example.com/contacts/john". + +The web app must return a JSON object, where each key is the JID of a contact, and the corresponding +value is data about that contact. + +If the user 'john' has friends 'marie' and 'michael', the web app would return a HTTP '200 OK' response +with the following contents: + +``` json +{ + "marie@example.com": { + "name": "Marie" + }, + + "michael@example.com": { + "name": "Michael" + } +} +``` + +### Notifying Prosody of roster changes + +The external service needs to notify Prosody whenever a user's roster +changes. To do this, it must make an HTTP POST request to either: + +- http://localhost:5280/roster_admin/refresh +- https://localhost:5281/roster_admin/refresh + +Make sure that the "http_files" module is enabled in Prosody's configuration, +for the above URLs to served. + +Ports 5280/5281 can be firewalled and the web server (i.e. Apache or Nginx) +can be configured to reverse proxy those URLs to for example +https://example.org/http-bind. + +The contents of the POST should be a JSON encoded array of usernames whose +rosters have changed. + +For example, if user ‘john’ became friends with ‘aaron’, both john’s +contact list and aaron’s contact lists have changed: + +``` json +["john", "aaron"] +``` + +When the operation is complete Prosody will reply with a summary of the +operation - a JSON object containing: + +- **status**: either “ok” (success) or “error” (operation completely failed) +- **message**: A human-readable message (for logging and debugging purposes) +- **updated**: The number of rosters successfully updated +- **errors**: The number of rosters that failed to update + +Example: + +``` json +{ + "status": "ok", + "message": "roster update complete", + "updated": 2, + "errors": 0 +} +``` + +Prosody may also return status codes `400` or `500` in case of errors (such +as a missing/malformed body).
--- a/mod_http_stats_stream/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ ---- -depends: -- 'mod\_http' -provides: -- http ---- - -# Introduction - -This module provides a streaming interface to [Prosodys internal statistics][doc:statistics] via -[Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events). - -## Example - -```js -var evtSource = new EventSource("/stats_stream"); - -/* - * An event with all current statistics in the form of a JSON object. - * Normally sent only once, when first connected to the stream. - */ -evtSource.addEventListener("stats-full", function(e) { - var initial_stats = JSON.parse(e.data); - console.log(initial_stats); -}, false); - -/* - * An event containing only statistics that have changed since the last - * 'stats-full' or 'stats-updated' event. - */ -evtSource.addEventListener("stats-updated", function(e) { - var updated_stats = JSON.parse(e.data); - console.log(updated_stats); -}, false); -``` - -# Compatibility - -* Prosody \>= 0.11.0 -* Trunk < [5f15ab7c6ae5](https://hg.prosody.im/trunk/rev/5f15ab7c6ae5)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_stats_stream/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,40 @@ +--- +depends: +- 'mod\_http' +provides: +- http +--- + +# Introduction + +This module provides a streaming interface to [Prosodys internal statistics][doc:statistics] via +[Server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events). + +## Example + +```js +var evtSource = new EventSource("/stats_stream"); + +/* + * An event with all current statistics in the form of a JSON object. + * Normally sent only once, when first connected to the stream. + */ +evtSource.addEventListener("stats-full", function(e) { + var initial_stats = JSON.parse(e.data); + console.log(initial_stats); +}, false); + +/* + * An event containing only statistics that have changed since the last + * 'stats-full' or 'stats-updated' event. + */ +evtSource.addEventListener("stats-updated", function(e) { + var updated_stats = JSON.parse(e.data); + console.log(updated_stats); +}, false); +``` + +# Compatibility + +* Prosody \>= 0.11.0 +* Trunk < [5f15ab7c6ae5](https://hg.prosody.im/trunk/rev/5f15ab7c6ae5)
--- a/mod_http_upload/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ ---- -description: HTTP File Upload -labels: -- Stage-Alpha ---- - -Introduction -============ - -This module implements [XEP-0363], versions 0.2 and 0.3, which let -clients upload files over HTTP. - -Configuration -============= - -mod\_http\_upload relies on Prosodys HTTP server and mod\_http for -serving HTTP requests. See [Prosodys HTTP server documentation][doc:http] -for information about how to configure ports, HTTP Host names etc. - -The module can be added as a new Component definition: - -``` {.lua} -Component "upload.example.org" "http_upload" -``` - -It should **not** be added to modules_enabled. - -## Discoverability - -Prosody makes subdomains of your VirtualHosts easily discoverable by -clients. To make the component discoverable by other hosts where the -component is **not a subdomain** of the VirtualHost, you can use -[`disco_items`][doc:modules:mod_disco#configuration]. - -``` {.lua} -VirtualHost "foo.example.org" -disco_items = { - { "upload.example.com" }, -} -``` - -## Access - -You may want to give upload access to additional entities such as components -by using the `http_upload_access` config option. - -``` {.lua} -http_upload_access = {"gateway.example.com"}; -``` - -Limits ------- - -### Max size - -A maximum file size can be set by: - -``` {.lua} -http_upload_file_size_limit = 123 -- bytes -``` - -Default is 1MB (1024\*1024). - -This can not be set over the value of `http_max_content_size` (default 10M). -Consider [mod_http_upload_external] instead of attempting to increase -this limit. - -### Max age - -Files can be set to be deleted after some time: - -``` lua -http_upload_expire_after = 60 * 60 * 24 * 7 -- a week in seconds -``` - -Expired files are deleted when a new upload slot is requested, - -A command exists to invoke expiry: - -``` -prosodyctl mod_http_upload expire [list of users or hosts] -``` - -### User quota - -A total maximum size of all uploaded files per user can be set by: - -``` lua -http_upload_quota = 1234 -- bytes -``` - -A request for a slot that would take an user over quota is denied. - -Path ----- - -By default, uploaded files are put in a sub-directory of the default -Prosody storage path (usually `/var/lib/prosody`). This can be changed: - -``` {.lua} -http_upload_path = "/path/to/uploaded/files" -``` - -Compatibility -============= - -Works with Prosody 0.11.x and later. - -In Prosody 0.12 and later, consider switching to [mod_http_file_share](https://prosody.im/doc/modules/mod_http_file_share) -which is distributed with Prosody. You can migrate existing files using -[mod_migrate_http_upload](https://modules.prosody.im/mod_migrate_http_upload.html).
--- a/mod_http_upload/mod_http_upload.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,450 +0,0 @@ --- mod_http_upload --- --- Copyright (C) 2015-2018 Kim Alvefur --- --- This file is MIT/X11 licensed. --- --- Implementation of HTTP Upload file transfer mechanism used by Conversations --- - --- depends -module:depends("http"); -module:depends("disco"); - -if module:http_url():match("^http://") then - error("File upload MUST happen with TLS but it isn’t enabled, see https://prosody.im/doc/http for how to fix this issue"); -end - --- imports -local st = require"util.stanza"; -local lfs = require"lfs"; -local url = require "socket.url"; -local dataform = require "util.dataforms".new; -local datamanager = require "util.datamanager"; -local array = require "util.array"; -local t_concat = table.concat; -local t_insert = table.insert; -local s_upper = string.upper; -local httpserver = require "net.http.server"; -local have_id, id = pcall(require, "util.id"); -- Only available in 0.10+ -local uuid = require"util.uuid".generate; -local jid = require "util.jid"; -if have_id then - uuid = id.medium; -end - -local function join_path(...) -- COMPAT util.path was added in 0.10 - return table.concat({ ... }, package.config:sub(1,1)); -end - --- config -local file_size_limit = module:get_option_number(module.name .. "_file_size_limit", 1024 * 1024); -- 1 MB -local quota = module:get_option_number(module.name .. "_quota"); -local max_age = module:get_option_number(module.name .. "_expire_after"); -local access = module:get_option_set(module.name .. "_access", {}); - ---- sanity -local parser_body_limit = module:context("*"):get_option_number("http_max_content_size", 10*1024*1024); -if file_size_limit > parser_body_limit then - module:log("warn", "%s_file_size_limit exceeds HTTP parser limit on body size, capping file size to %d B", - module.name, parser_body_limit); - file_size_limit = parser_body_limit; -end - -if prosody.hosts[module.host].type == "local" then - module:log("warn", "mod_%s loaded on a user host, this may be incompatible with some client software, see docs for correct usage", module.name); -end - -local http_files; - -if prosody.process_type == "prosody" then - http_files = require "net.http.files"; -else - http_files = module:depends"http_files"; -end - -local mime_map = module:shared("/*/http_files/mime").types; -if not mime_map then - mime_map = { - html = "text/html", htm = "text/html", - xml = "application/xml", - txt = "text/plain", - css = "text/css", - js = "application/javascript", - png = "image/png", - gif = "image/gif", - jpeg = "image/jpeg", jpg = "image/jpeg", - svg = "image/svg+xml", - }; - module:shared("/*/http_files/mime").types = mime_map; - - local mime_types, err = io.open(module:get_option_path("mime_types_file", "/etc/mime.types", "config"), "r"); - if mime_types then - local mime_data = mime_types:read("*a"); - mime_types:close(); - setmetatable(mime_map, { - __index = function(t, ext) - local typ = mime_data:match("\n(%S+)[^\n]*%s"..(ext:lower()).."%s") or "application/octet-stream"; - t[ext] = typ; - return typ; - end - }); - end -end - --- namespaces -local namespace = "urn:xmpp:http:upload:0"; -local legacy_namespace = "urn:xmpp:http:upload"; - --- identity and feature advertising -module:add_identity("store", "file", module:get_option_string("name", "HTTP File Upload")); -module:add_feature(namespace); -module:add_feature(legacy_namespace); - -module:add_extension(dataform { - { name = "FORM_TYPE", type = "hidden", value = namespace }, - { name = "max-file-size", type = "text-single" }, -}:form({ ["max-file-size"] = ("%d"):format(file_size_limit) }, "result")); - -module:add_extension(dataform { - { name = "FORM_TYPE", type = "hidden", value = legacy_namespace }, - { name = "max-file-size", type = "text-single" }, -}:form({ ["max-file-size"] = ("%d"):format(file_size_limit) }, "result")); - --- state -local pending_slots = module:shared("upload_slots"); - -local storage_path = module:get_option_string(module.name .. "_path", join_path(prosody.paths.data, module.name)); -lfs.mkdir(storage_path); - -local function expire(username, host) - if not max_age then return true; end - local uploads, err = datamanager.list_load(username, host, module.name); - if err then return false, err; end - if not uploads then return true; end - uploads = array(uploads); - local expiry = os.time() - max_age; - local upload_window = os.time() - 900; - local before = #uploads; - uploads:filter(function (item) - local filename = item.filename; - if item.dir then - filename = join_path(storage_path, item.dir, item.filename); - end - if item.time < expiry then - local deleted, whynot = os.remove(filename); - if not deleted then - module:log("warn", "Could not delete expired upload %s: %s", filename, whynot or "delete failed"); - end - os.remove(filename:match("^(.*)[/\\]")); - return false; - elseif item.time < upload_window and not lfs.attributes(filename) then - return false; -- File was not uploaded or has been deleted since - end - return true; - end); - local after = #uploads; - if before == after then return true end -- nothing changed, skip write - return datamanager.list_store(username, host, module.name, uploads); -end - -local function check_quota(username, host, does_it_fit) - if not quota then return true; end - local uploads, err = datamanager.list_load(username, host, module.name); - if err then - return false; - elseif not uploads then - if does_it_fit then - return does_it_fit < quota; - end - return true; - end - local sum = does_it_fit or 0; - for _, item in ipairs(uploads) do - sum = sum + item.size; - end - return sum < quota; -end - -local measure_slot = function () end -if module.measure then - -- COMPAT 0.9 - -- module:measure was added in 0.10 - measure_slot = module:measure("slot", "sizes"); -end - -local function handle_request(origin, stanza, xmlns, filename, filesize) - local username, host = origin.username, origin.host; - - local user_bare = jid.bare(stanza.attr.from); - local user_host = jid.host(user_bare); - - -- local clients or whitelisted jids/hosts only - if not (origin.type == "c2s" or access:contains(user_bare) or access:contains(user_host)) then - module:log("debug", "Request for upload slot from a %s", origin.type); - return nil, st.error_reply(stanza, "cancel", "not-authorized"); - end - -- validate - if not filename or filename:find("/") then - module:log("debug", "Filename %q not allowed", filename or ""); - return nil, st.error_reply(stanza, "modify", "bad-request", "Invalid filename"); - end - expire(username, host); - if not filesize then - module:log("debug", "Missing file size"); - return nil, st.error_reply(stanza, "modify", "bad-request", "Missing or invalid file size"); - elseif filesize > file_size_limit then - module:log("debug", "File too large (%d > %d)", filesize, file_size_limit); - return nil, st.error_reply(stanza, "modify", "not-acceptable", "File too large") - :tag("file-too-large", {xmlns=xmlns}) - :tag("max-file-size"):text(("%d"):format(file_size_limit)); - elseif not check_quota(username, host, filesize) then - module:log("debug", "Upload of %dB by %s would exceed quota", filesize, user_bare); - return nil, st.error_reply(stanza, "wait", "resource-constraint", "Quota reached"); - end - - local random_dir = uuid(); - local created, err = lfs.mkdir(join_path(storage_path, random_dir)); - - if not created then - module:log("error", "Could not create directory for slot: %s", err); - return nil, st.error_reply(stanza, "wait", "internal-server-error"); - end - - local ok = datamanager.list_append(username, host, module.name, { - filename = filename, dir = random_dir, size = filesize, time = os.time() }); - - if not ok then - return nil, st.error_reply(stanza, "wait", "internal-server-error"); - end - - local slot = random_dir.."/"..filename; - pending_slots[slot] = user_bare; - - module:add_timer(900, function() - pending_slots[slot] = nil; - end); - - measure_slot(filesize); - - origin.log("debug", "Given upload slot %q", slot); - - local base_url = module:http_url(); - local slot_url = url.parse(base_url); - slot_url.path = url.parse_path(slot_url.path or "/"); - t_insert(slot_url.path, random_dir); - t_insert(slot_url.path, filename); - slot_url.path.is_directory = false; - slot_url.path = url.build_path(slot_url.path); - slot_url = url.build(slot_url); - return slot_url; -end - --- hooks -module:hook("iq/host/"..namespace..":request", function (event) - local stanza, origin = event.stanza, event.origin; - local request = stanza.tags[1]; - local filename = request.attr.filename; - local filesize = tonumber(request.attr.size); - - local slot_url, err = handle_request(origin, stanza, namespace, filename, filesize); - if not slot_url then - origin.send(err); - return true; - end - - local reply = st.reply(stanza) - :tag("slot", { xmlns = namespace }) - :tag("get", { url = slot_url }):up() - :tag("put", { url = slot_url }):up() - :up(); - origin.send(reply); - return true; -end); - -module:hook("iq/host/"..legacy_namespace..":request", function (event) - local stanza, origin = event.stanza, event.origin; - local request = stanza.tags[1]; - local filename = request:get_child_text("filename"); - local filesize = tonumber(request:get_child_text("size")); - - local slot_url, err = handle_request(origin, stanza, legacy_namespace, filename, filesize); - if not slot_url then - origin.send(err); - return true; - end - - local reply = st.reply(stanza) - :tag("slot", { xmlns = legacy_namespace }) - :tag("get"):text(slot_url):up() - :tag("put"):text(slot_url):up() - :up(); - origin.send(reply); - return true; -end); - -local measure_upload = function () end -if module.measure then - -- COMPAT 0.9 - -- module:measure was added in 0.10 - measure_upload = module:measure("upload", "sizes"); -end - --- http service -local function set_cross_domain_headers(response) - local headers = response.headers; - headers.access_control_allow_methods = "GET, PUT, OPTIONS"; - headers.access_control_allow_headers = "Content-Type"; - headers.access_control_max_age = "7200"; - headers.access_control_allow_origin = response.request.headers.origin or "*"; - return response; -end - -local function upload_data(event, path) - set_cross_domain_headers(event.response); - - local uploader = pending_slots[path]; - if not uploader then - module:log("warn", "Attempt to upload to unknown slot %q", path); - return; -- 404 - end - local random_dir, filename = path:match("^([^/]+)/([^/]+)$"); - if not random_dir then - module:log("warn", "Invalid file path %q", path); - return 400; - end - if #event.request.body > file_size_limit then - module:log("warn", "Uploaded file too large %d bytes", #event.request.body); - return 400; - end - pending_slots[path] = nil; - local full_filename = join_path(storage_path, random_dir, filename); - if lfs.attributes(full_filename) then - module:log("warn", "File %s exists already, not replacing it", full_filename); - return 409; - end - local fh, ferr = io.open(full_filename, "w"); - if not fh then - module:log("error", "Could not open file %s for upload: %s", full_filename, ferr); - return 500; - end - local ok, err = fh:write(event.request.body); - if not ok then - module:log("error", "Could not write to file %s for upload: %s", full_filename, err); - os.remove(full_filename); - return 500; - end - ok, err = fh:close(); - if not ok then - module:log("error", "Could not write to file %s for upload: %s", full_filename, err); - os.remove(full_filename); - return 500; - end - measure_upload(#event.request.body); - module:log("info", "File uploaded by %s to slot %s", uploader, random_dir); - return 201; -end - --- FIXME Duplicated from net.http.server - -local codes = require "net.http.codes"; -local headerfix = setmetatable({}, { - __index = function(t, k) - local v = "\r\n"..k:gsub("_", "-"):gsub("%f[%w].", s_upper)..": "; - t[k] = v; - return v; - end -}); - -local function send_response_sans_body(response, body) - if response.finished then return; end - response.finished = true; - response.conn._http_open_response = nil; - - local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]); - local headers = response.headers; - if type(body) == "string" then - headers.content_length = #body; - elseif io.type(body) == "file" then - headers.content_length = body:seek("end"); - body:close(); - end - - local output = { status_line }; - for k,v in pairs(headers) do - t_insert(output, headerfix[k]..v); - end - t_insert(output, "\r\n\r\n"); - -- Here we *don't* add the body to the output - - response.conn:write(t_concat(output)); - if response.on_destroy then - response:on_destroy(); - response.on_destroy = nil; - end - if response.persistent then - response:finish_cb(); - else - response.conn:close(); - end -end - -local serve_uploaded_files = http_files.serve({ path = storage_path, mime_map = mime_map }); - -local function serve_head(event, path) - set_cross_domain_headers(event.response); - event.response.send = send_response_sans_body; - event.response.send_file = send_response_sans_body; - return serve_uploaded_files(event, path); -end - -if httpserver.send_head_response then - -- Prosody will take care of HEAD requests since hg:3f4c25425589 - serve_head = nil -end - -local function serve_hello(event) - event.response.headers.content_type = "text/html;charset=utf-8" - return "<!DOCTYPE html>\n<h1>Hello from mod_"..module.name.." on "..module.host.."!</h1>\n"; -end - -module:provides("http", { - route = { - ["GET"] = serve_hello; - ["GET /"] = serve_hello; - ["GET /*"] = serve_uploaded_files; - ["HEAD /*"] = serve_head; - ["PUT /*"] = upload_data; - - ["OPTIONS /*"] = function (event) - set_cross_domain_headers(event.response); - return ""; - end; - }; -}); - -module:log("info", "URL: <%s> - Ensure this can be reached by users", module:http_url()); -module:log("info", "Storage path: '%s'", storage_path); - -function module.command(args) - datamanager = require "core.storagemanager".olddm; - -- luacheck: ignore 421/user - if args[1] == "expire" and args[2] then - local split = require "util.jid".prepped_split; - for i = 2, #args do - local user, host = split(args[i]); - if user then - assert(expire(user, host)); - else - for user in assert(datamanager.users(host, module.name, "list")) do - expire(user, host); - end - end - end - else - print("prosodyctl mod_http_upload expire [host or user@host]+") - print("\tProcess upload expiry for the given list of hosts and/or users"); - return 1; - end -end -
--- a/mod_http_upload_external/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,234 +0,0 @@ ---- -description: HTTP File Upload (external service) -labels: -- Stage-Alpha ---- - -Introduction -============ - -This module implements [XEP-0363], which lets clients upload files -over HTTP to an external web server. - -This module generates URLs that are signed using a HMAC. Any web service that can authenticate -these URLs can be used. - -Implementations ---------------- - -* [PHP implementation](https://hg.prosody.im/prosody-modules/raw-file/tip/mod_http_upload_external/share.php) -* [Python3+Flask implementation](https://github.com/horazont/xmpp-http-upload) -* [Go implementation, Prosody Filer](https://github.com/ThomasLeister/prosody-filer) -* [Perl implementation for nginx](https://github.com/weiss/ngx_http_upload) -* [Rust implementation](https://gitlab.com/nyovaya/xmpp-http-upload) - -To implement your own service compatible with this module, check out the implementation notes below -(and if you publish your implementation - let us know!). - -Configuration -============= - -The module can be added as a new Component definition: - -``` {.lua} -Component "upload.example.org" "http_upload_external" -http_upload_external_base_url = "https://your.example.com/upload/service" -http_upload_external_secret = "your shared secret" -``` - -It should **not** be added to modules_enabled. - - -External URL ------------- - -You need to provide the path to the external service. Ensure it ends with '/'. - -For example, to use the PHP implementation linked above, you might set it to: - -``` {.lua} -http_upload_external_base_url = "https://your.example.com/path/to/share.php/" -``` - -Secret ------- - -Set a long and unpredictable string as your secret. This is so the upload service can verify that -the upload comes from mod_http_upload_external, and random strangers can't upload to your server. - -``` {.lua} -http_upload_external_secret = "this is a secret string!" -``` - -You need to set exactly the same secret string in your external service. - -Limits ------- - -A maximum file size can be set by: - -``` {.lua} -http_upload_external_file_size_limit = 123 -- bytes -``` - -Default is 100MB (100\*1024\*1024). - -Access ------- - -You may want to give upload access to additional entities such as components -by using the `http_upload_external_access` config option. - -``` {.lua} -http_upload_external_access = {"gateway.example.com"}; -``` - -Compatibility -============= - -Works with Prosody 0.9.x and later. - -Implementation -============== - -To implement your own external service that is compatible with this module, you need to expose a -simple API that allows the HTTP GET, HEAD and PUT methods on arbitrary URLs located on your service. - -For example, if http_upload_external_base_url is set to `https://example.com/upload/` then your service -might receive the following requests: - -Upload a new file: - -``` -PUT https://example.com/upload/foo/bar.jpg?v=49e9309ff543ace93d25be90635ba8e9965c4f23fc885b2d86c947a5d59e55b2 -``` - -Recipient checks the file size and other headers: - -``` -HEAD https://example.com/upload/foo/bar.jpg -``` - -Recipient downloads the file: - -``` -GET https://example.com/upload/foo/bar.jpg -``` - -The only tricky logic is in validation of the PUT request. Firstly, don't overwrite existing files (return 409 Conflict). - -Then you need to validate the auth token. - -### Validating the auth token - - -| Version | Supports | -|:--------|:--------------------------------------------------------------------------------------------------------| -| v | Validates only filename and size. Does not support file type restrictions by the XMPP server. | -| v2 | Validates the filename, size and MIME type. This allows the server to implement MIME type restrictions. | - -It is probable that a future v3 will be specified that allows carrying information about the uploader identity, allowing -the implementation of per-user quotas and limits. - -Implementations may implement one or more versions of the protocol simultaneously. The XMPP server generates the URLs and -ultimately selects which version will be used. - -XMPP servers MUST only generate URLs with **one** of the versions listed here. However in case multiple parameters are -present, upload services MUST **only** use the token from the highest parameter version that they support. - -#### Version 1 (v) - -The token will be in the URL query parameter 'v'. If it is absent, fail with 403 Forbidden. - -Calculate the expected auth token by reading the value of the Content-Length header of the PUT request. E.g. for a 1MB file -will have a Content-Length of '1048576'. Append this to the uploaded file name, separated by a space (0x20) character. - -For the above example, you would end up with the following string: "foo/bar.jpg 1048576" - -The auth token is a SHA256 HMAC of this string, using the configured secret as the key. E.g. - -``` -calculated_auth_token = hmac_sha256("foo/bar.jpg 1048576", "secret string") -``` - -If this is not equal to the 'v' parameter provided in the upload URL, reject the upload with 403 Forbidden. - -**Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string -comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such -as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. - -#### Version 2 (v2) - -The token will be in the URL query parameter 'v2'. If it is absent, fail with 403 Forbidden. - -| Input | Example |Read from | -|:----------------|:------------|:--------------------------------------------------------------------| -|`file_path` | foo/bar.jpg | The URL of the PUT request, with the service's base prefix removed. | -|`content_length` | 1048576 | Content-Length header | -|`content_type` | image/jpeg | Content-Type header | - -The parameters should be joined into a single string, separated by NUL bytes (`\0`): - -``` - signed_string = ( file_path + '\0' + content_length + '\0' + content_type ) -``` - -``` - signed_string = "foo/bar.jpg\01048576\0image/jpeg" -``` - -The expected auth token is the SHA256 HMAC of this string, using the configured secret key as the key. E.g.: - -``` -calculated_auth_token = hmac_sha256(signed_string, "secret string") -``` - -If this is not equal to the 'v2' parameter provided in the upload URL, reject the upload with 403 Forbidden. - -**Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string -comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such -as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. - -### Security considerations - -#### HTTPS - -All uploads and downloads should only be over HTTPS. The security of the served content is protected only -by the uniqueness present in the URLs themselves, and not using HTTPS may leak the URLs and contents to third-parties. - -Implementations should consider including HSTS and HPKP headers, with consent of the administrator. - -#### MIME types - -If the upload Content-Type header matches any of the following MIME types, it MUST be preserved and included in the Content-Type -of any GET requests made to download the file: - -- `image/*` -- `video/*` -- `audio/*` -- `text/plain` - -It is recommended that other MIME types are preserved, but served with the addition of the following header: - -``` -Content-Disposition: attachment -``` - -This prevents the browser interpreting scripts and other resources that may potentially be malicious. - -Some browsers may also benefit from explicitly telling them not to try guessing the type of a file: - -``` -X-Content-Type-Options: nosniff -``` - -#### Security headers - -The following headers should be included to provide additional sandboxing of resources, considering the uploaded -content is not understood or trusted by the upload service: - -``` -Content-Security-Policy: default-src 'none' -X-Content-Security-Policy: default-src 'none' -X-WebKit-CSP: default-src 'none' -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_upload_external/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,238 @@ +--- +description: HTTP File Upload (external service) +labels: +- Stage-Beta +--- + +Introduction +============ + +This module implements [XEP-0363], which lets clients upload files +over HTTP to an external web server. + +This module generates URLs that are signed using a HMAC. Any web service that can authenticate +these URLs can be used. + +Implementations +--------------- + +* [PHP implementation](https://hg.prosody.im/prosody-modules/raw-file/tip/mod_http_upload_external/share.php) +* [Python3+Flask implementation](https://github.com/horazont/xmpp-http-upload) +* [Go implementation, Prosody Filer](https://github.com/ThomasLeister/prosody-filer) +* [Go implementation, HMAC File Server](https://github.com/PlusOne/hmac-file-server) +* [Perl implementation for nginx](https://github.com/weiss/ngx_http_upload) +* [Rust implementation](https://gitlab.com/nyovaya/xmpp-http-upload) + +To implement your own service compatible with this module, check out the implementation notes below +(and if you publish your implementation - let us know!). + +Configuration +============= + +The module can be added as a new Component definition: + +``` {.lua} +Component "upload.example.org" "http_upload_external" +http_upload_external_base_url = "https://your.example.com/upload/service" +http_upload_external_secret = "your shared secret" +``` + +It should **not** be added to modules_enabled. + + +External URL +------------ + +You need to provide the path to the external service. Ensure it ends with '/'. + +For example, to use the PHP implementation linked above, you might set it to: + +``` {.lua} +http_upload_external_base_url = "https://your.example.com/path/to/share.php/" +``` + +Secret +------ + +Set a long and unpredictable string as your secret. This is so the upload service can verify that +the upload comes from mod_http_upload_external, and random strangers can't upload to your server. + +``` {.lua} +http_upload_external_secret = "this is a secret string!" +``` + +You need to set exactly the same secret string in your external service. + +Limits +------ + +A maximum file size can be set by: + +``` {.lua} +http_upload_external_file_size_limit = 123 -- bytes +``` + +Default is 100MB (100\*1024\*1024). + +Access +------ + +You may want to give upload access to additional entities such as components +by using the `http_upload_external_access` config option. + +``` {.lua} +http_upload_external_access = {"gateway.example.com"}; +``` + +Compatibility +============= + + Prosody-Version Status + ---------------- -------------------- + trunk Works as of 24-12-12 + 0.12 Works + +Implementation +============== + +To implement your own external service that is compatible with this module, you need to expose a +simple API that allows the HTTP GET, HEAD and PUT methods on arbitrary URLs located on your service. + +For example, if http_upload_external_base_url is set to `https://example.com/upload/` then your service +might receive the following requests: + +Upload a new file: + +``` +PUT https://example.com/upload/foo/bar.jpg?v=49e9309ff543ace93d25be90635ba8e9965c4f23fc885b2d86c947a5d59e55b2 +``` + +Recipient checks the file size and other headers: + +``` +HEAD https://example.com/upload/foo/bar.jpg +``` + +Recipient downloads the file: + +``` +GET https://example.com/upload/foo/bar.jpg +``` + +The only tricky logic is in validation of the PUT request. Firstly, don't overwrite existing files (return 409 Conflict). + +Then you need to validate the auth token. + +### Validating the auth token + + +| Version | Supports | +|:--------|:--------------------------------------------------------------------------------------------------------| +| v | Validates only filename and size. Does not support file type restrictions by the XMPP server. | +| v2 | Validates the filename, size and MIME type. This allows the server to implement MIME type restrictions. | + +It is probable that a future v3 will be specified that allows carrying information about the uploader identity, allowing +the implementation of per-user quotas and limits. + +Implementations may implement one or more versions of the protocol simultaneously. The XMPP server generates the URLs and +ultimately selects which version will be used. + +XMPP servers MUST only generate URLs with **one** of the versions listed here. However in case multiple parameters are +present, upload services MUST **only** use the token from the highest parameter version that they support. + +#### Version 1 (v) + +The token will be in the URL query parameter 'v'. If it is absent, fail with 403 Forbidden. + +Calculate the expected auth token by reading the value of the Content-Length header of the PUT request. E.g. for a 1MB file +will have a Content-Length of '1048576'. Append this to the uploaded file name, separated by a space (0x20) character. + +For the above example, you would end up with the following string: "foo/bar.jpg 1048576" + +The auth token is a SHA256 HMAC of this string, using the configured secret as the key. E.g. + +``` +calculated_auth_token = hmac_sha256("foo/bar.jpg 1048576", "secret string") +``` + +If this is not equal to the 'v' parameter provided in the upload URL, reject the upload with 403 Forbidden. + +**Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string +comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such +as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. + +#### Version 2 (v2) + +The token will be in the URL query parameter 'v2'. If it is absent, fail with 403 Forbidden. + +| Input | Example |Read from | +|:----------------|:------------|:--------------------------------------------------------------------| +|`file_path` | foo/bar.jpg | The URL of the PUT request, with the service's base prefix removed. | +|`content_length` | 1048576 | Content-Length header | +|`content_type` | image/jpeg | Content-Type header | + +The parameters should be joined into a single string, separated by NUL bytes (`\0`): + +``` + signed_string = ( file_path + '\0' + content_length + '\0' + content_type ) +``` + +``` + signed_string = "foo/bar.jpg\01048576\0image/jpeg" +``` + +The expected auth token is the SHA256 HMAC of this string, using the configured secret key as the key. E.g.: + +``` +calculated_auth_token = hmac_sha256(signed_string, "secret string") +``` + +If this is not equal to the 'v2' parameter provided in the upload URL, reject the upload with 403 Forbidden. + +**Security note:** When comparing `calculated_auth_token` with the token provided in the URL, you must use a constant-time string +comparison, otherwise an attacker may be able to discover your secret key. Most languages/environments provide such a function, such +as `hash_equals()` in PHP, `hmac.compare_digest()` in Python, or `ConstantTimeCompare()` from `crypto/subtle` in Go. + +### Security considerations + +#### HTTPS + +All uploads and downloads should only be over HTTPS. The security of the served content is protected only +by the uniqueness present in the URLs themselves, and not using HTTPS may leak the URLs and contents to third-parties. + +Implementations should consider including HSTS and HPKP headers, with consent of the administrator. + +#### MIME types + +If the upload Content-Type header matches any of the following MIME types, it MUST be preserved and included in the Content-Type +of any GET requests made to download the file: + +- `image/*` +- `video/*` +- `audio/*` +- `text/plain` + +It is recommended that other MIME types are preserved, but served with the addition of the following header: + +``` +Content-Disposition: attachment +``` + +This prevents the browser interpreting scripts and other resources that may potentially be malicious. + +Some browsers may also benefit from explicitly telling them not to try guessing the type of a file: + +``` +X-Content-Type-Options: nosniff +``` + +#### Security headers + +The following headers should be included to provide additional sandboxing of resources, considering the uploaded +content is not understood or trusted by the upload service: + +``` +Content-Security-Policy: default-src 'none' +X-Content-Security-Policy: default-src 'none' +X-WebKit-CSP: default-src 'none' +```
--- a/mod_idlecompat/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'XEP-0319 compatibility module' -... - -Introduction -============ - -This module adds [XEP-0319](http://xmpp.org/extensions/xep-0319.html) -idle tags to presence stanzas containing [XEP-0012: Last -Activity](http://xmpp.org/extensions/xep-0012.html) tags for idle -indication (e.g. supported by libpurple clients). It works on outgoing -and incoming presence stanzas. - -Install and enable it like any other module. It has no configuration. - -Compatibility -============= - - ------- ------- - trunk Works - 0.9 Works - ------- -------
--- a/mod_ignore_host_chatstates/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -This module discards typing notifications sent to a bare host JID, -preventing error replies to be sent. These errors are harmless but can -be annoying sometimes if your client shows them prominently.
--- a/mod_incidents_handling/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Incidents Handling plugin -... - -Introduction -============ - -This module implements -[XEP-268](http://xmpp.org/extensions/xep-0268.html). - -Details -======= - -It will let you manage reports, inquiries, requests and responses -through an Adhoc interface. The following new adhoc admin commands will -be available: - -- List Incidents -- List all available incidents and let's you reply - requests. -- Send Incident Inquiry -- Inquiry a remote server about an incident. -- Send Incident Report -- Send an incident report to a remote server. -- Send Incident Request -- Send an incident request to a remote - server. - -Each Adhoc form provides syntax instructions through `<desc/>` elements -(they may currently be stripped by Prosody), although it's encouraged to -read the [IODEF specifications](https://tools.ietf.org/html/rfc5070). - -Usage -===== - -Copy the module folder into your prosody modules directory. Place the -module between your enabled modules either into the global or a vhost -section. - -Optional configuration directives: - -``` {.lua} -incidents_expire_time = 86400 -- Expiral of "closed" incidents in seconds. -``` - -Info -==== - -- to be 0.9, works.
--- a/mod_inject_ecaps2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ ---- -summary: Injects ecaps2 elements (XEP-0390) in presence ---- - -Description -=========== - -This module injects [XEP-0390: Entity Capabilities 2.0] elements in the -presence once the legacy caps (XEP-0115) are received. - -Compatibility -============= - - ------- --------------- - trunk Works - 0.11 Does not work - 0.10 Does not work - ------- ---------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_inject_ecaps2/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,18 @@ +--- +summary: Injects ecaps2 elements (XEP-0390) in presence +--- + +Description +=========== + +This module injects [XEP-0390: Entity Capabilities 2.0] elements in the +presence once the legacy caps (XEP-0115) are received. + +Compatibility +============= + + ------- --------------- + trunk Works + 0.11 Does not work + 0.10 Does not work + ------- ---------------
--- a/mod_invite/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ ---- -labels: -- 'Stage-Deprecated' -summary: 'Allows users to invite new users' -... - -**NOTE:** This module has been deprecated. Its functionality has been -moved to other modules, see the mod_invites documentation for details. - -Introduction -============ - -This module allows users with an account to generate single-use invite URLs -using an ad-hoc command. The invite URLs allow new users to create an account -even if public registration is disabled. - -After the account is created, the inviter and the invitee are automatically -added to the other's roster. The inviter of a user is stored, so can be used -later (for example, for detecting spammers). - -This module depends on Prosody's internal webserver. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invite/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,28 @@ +--- +labels: +- 'Stage-Deprecated' +summary: 'Allows users to invite new users' +... + +**NOTE:** This module has been deprecated. Its functionality has been +moved to other modules, see the mod_invites documentation for details. + +Introduction +============ + +This module allows users with an account to generate single-use invite URLs +using an ad-hoc command. The invite URLs allow new users to create an account +even if public registration is disabled. + +After the account is created, the inviter and the invitee are automatically +added to the other's roster. The inviter of a user is stored, so can be used +later (for example, for detecting spammers). + +This module depends on Prosody's internal webserver. + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_invites/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ ---- -labels: -- 'Stage-Merged' -summary: 'Invite management module for Prosody' -... - -Introduction -============ - -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites][doc:modules:mod_invites]. Users of Prosody **0.12** -and later should not install this version. -::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] -- [mod_invites_register][doc:modules:mod_invites_register] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -This module manages the creation and consumption of invite codes for the -host(s) it is loaded onto. It currently does not expose any admin/user-facing -functionality (though in the future it will probably gain a way to view/manage -pending invites). - -Instead, other modules can use the API from this module to create invite tokens -which can be used to e.g. register accounts or create automatic subscription -approvals. - -This module should not be confused with the similarly named mod_invite (note the -missing 's'!). That module was a precursor to this one that helped test and prove -the concept of invite-based registration, and is now deprecated. - -# Configuration - -This module exposes just one option - the length of time that a generated invite -should be valid for by default. - -``` {.lua} --- Configure the number of seconds a token is valid for (default 7 days) -invite_expiry = 86400 * 7 -``` - -# Invites setup - -For a fully-featured invite-based setup, the following provides an example -configuration: - -``` {.lua} --- Specify the external URL format of the invite links - -VirtualHost "example.com" - invites_page = "https://example.com/invite?{invite.token}" - http_external_url = "https://example.com/" - http_paths = { - invites_page = "/invite"; - invites_register_web = "/register"; - } - modules_enabled = { - "invites"; - "invites_adhoc"; - "invites_page"; - "invites_register"; - "invites_register_web"; - - "http_libjs"; -- See 'external dependencies' below - } -``` - -Restart Prosody and create a new invite using an ad-hoc command in an XMPP client connected -to your admin account, or use the command line: - - prosodyctl mod_invites generate example.com - -## External dependencies - -The default HTML templates for the web-based modules depend on some CSS and Javascript -libraries. They expect these to be available at `https://example.com/share`. An easy -way of doing this if you are on Debian 10 (buster) is to enable mod_http_libjs and install -the following packages: - - apt install libjs-bootstrap4 libjs-jquery - -On other systems you will need to manually put these libraries somewhere on the filesystem -that Prosody can read, and serve them using mod_http_libjs with a custom `libjs_path` -setting. - -# Compatibility - -0.11 and later.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,95 @@ +--- +labels: +- 'Stage-Merged' +summary: 'Invite management module for Prosody' +... + +Introduction +============ + +::: {.alert .alert-info} +This module has been merged into Prosody as +[mod_invites][doc:modules:mod_invites]. Users of Prosody **0.12** +and later should not install this version. +::: + +This module is part of the suite of modules that implement invite-based +account registration for Prosody. The other modules are: + +- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] +- [mod_invites_register][doc:modules:mod_invites_register] +- [mod_invites_page] +- [mod_invites_register_web] +- [mod_invites_api] +- [mod_register_apps] + +This module manages the creation and consumption of invite codes for the +host(s) it is loaded onto. It currently does not expose any admin/user-facing +functionality (though in the future it will probably gain a way to view/manage +pending invites). + +Instead, other modules can use the API from this module to create invite tokens +which can be used to e.g. register accounts or create automatic subscription +approvals. + +This module should not be confused with the similarly named mod_invite (note the +missing 's'!). That module was a precursor to this one that helped test and prove +the concept of invite-based registration, and is now deprecated. + +# Configuration + +This module exposes just one option - the length of time that a generated invite +should be valid for by default. + +``` {.lua} +-- Configure the number of seconds a token is valid for (default 7 days) +invite_expiry = 86400 * 7 +``` + +# Invites setup + +For a fully-featured invite-based setup, the following provides an example +configuration: + +``` {.lua} +-- Specify the external URL format of the invite links + +VirtualHost "example.com" + invites_page = "https://example.com/invite?{invite.token}" + http_external_url = "https://example.com/" + http_paths = { + invites_page = "/invite"; + invites_register_web = "/register"; + } + modules_enabled = { + "invites"; + "invites_adhoc"; + "invites_page"; + "invites_register"; + "invites_register_web"; + + "http_libjs"; -- See 'external dependencies' below + } +``` + +Restart Prosody and create a new invite using an ad-hoc command in an XMPP client connected +to your admin account, or use the command line: + + prosodyctl mod_invites generate example.com + +## External dependencies + +The default HTML templates for the web-based modules depend on some CSS and Javascript +libraries. They expect these to be available at `https://example.com/share`. An easy +way of doing this if you are on Debian 10 (buster) is to enable mod_http_libjs and install +the following packages: + + apt install libjs-bootstrap4 libjs-jquery + +On other systems you will need to manually put these libraries somewhere on the filesystem +that Prosody can read, and serve them using mod_http_libjs with a custom `libjs_path` +setting. + +# Compatibility + +0.11 and later.
--- a/mod_invites_adhoc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ ---- -labels: -- 'Stage-Merged' -summary: 'Enable ad-hoc command for XMPP clients to create invitations' -... - -Introduction -============ - -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites_adhoc][doc:modules:mod_invites_adhoc]. Users of Prosody **0.12** -and later should not install this version. -::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites][doc:modules:mod_invites] -- [mod_invites_register][doc:modules:mod_invites_register] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -For details and a full overview, start with the [mod_invites] documentation. - -Details -======= - -mod_invites_adhoc allows XMPP clients to create new invites on the server. -Clients must support either XEP-0401 (Easy Onboarding) or XEP-0050 (Ad-hoc -commands). - -There are three types of invitation that can be created: - -| Invite type | Description | -|--|--| -| Account-only invites | These can be used to register a new account | -| Contact-only invites | These can be shared with a contact so they can easily add you to their contact list | -| Account-and-contact invites | Like a contact-only invite, but also allows the contact to register on the current server if they don't already have an XMPP account | - -Only configured admins of the server are able to create account-only invites. By default -normal users may only create contact-only invites, but account-and-contact invites can -be enabled with the `allow_user_invites` option. - -Configuration -============= - -| Name | Description | Default | -|-----------------------|-----------------------------------------------------------------------|-------------------------------------------| -| allow_user_invites | Whether non-admin users can invite contacts to register on this server| `false` | -| allow_contact_invites | Whether non-admin users can invite contacts to their roster | `true` | - -The `allow_user_invites` option should be set as desired. However it is -strongly recommended to leave the other option (`allow_contact_invites`) -at its default to provide the best user experience.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_adhoc/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,57 @@ +--- +labels: +- 'Stage-Merged' +summary: 'Enable ad-hoc command for XMPP clients to create invitations' +... + +Introduction +============ + +::: {.alert .alert-info} +This module has been merged into Prosody as +[mod_invites_adhoc][doc:modules:mod_invites_adhoc]. Users of Prosody **0.12** +and later should not install this version. +::: + +This module is part of the suite of modules that implement invite-based +account registration for Prosody. The other modules are: + +- [mod_invites][doc:modules:mod_invites] +- [mod_invites_register][doc:modules:mod_invites_register] +- [mod_invites_page] +- [mod_invites_register_web] +- [mod_invites_api] +- [mod_register_apps] + +For details and a full overview, start with the [mod_invites] documentation. + +Details +======= + +mod_invites_adhoc allows XMPP clients to create new invites on the server. +Clients must support either XEP-0401 (Easy Onboarding) or XEP-0050 (Ad-hoc +commands). + +There are three types of invitation that can be created: + +| Invite type | Description | +|--|--| +| Account-only invites | These can be used to register a new account | +| Contact-only invites | These can be shared with a contact so they can easily add you to their contact list | +| Account-and-contact invites | Like a contact-only invite, but also allows the contact to register on the current server if they don't already have an XMPP account | + +Only configured admins of the server are able to create account-only invites. By default +normal users may only create contact-only invites, but account-and-contact invites can +be enabled with the `allow_user_invites` option. + +Configuration +============= + +| Name | Description | Default | +|-----------------------|-----------------------------------------------------------------------|-------------------------------------------| +| allow_user_invites | Whether non-admin users can invite contacts to register on this server| `false` | +| allow_contact_invites | Whether non-admin users can invite contacts to their roster | `true` | + +The `allow_user_invites` option should be set as desired. However it is +strongly recommended to leave the other option (`allow_contact_invites`) +at its default to provide the best user experience.
--- a/mod_invites_register/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ ---- -labels: -- 'Stage-Merged' -summary: 'Allow account registration using invite tokens' -... - -Introduction -============ - -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites_register][doc:modules:mod_invites_register]. Users of -Prosody **0.12** and later should not install this version. -::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites][doc:modules:mod_invites] -- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -For details and a full overview, start with the [mod_invites] documentation. - -Details -======= - -This module allows clients to register an account using an invite ('preauth') -token generated by mod_invites. It implements the protocol described at -[docs.modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites) -and [XEP-0401 version 0.3.0](https://xmpp.org/extensions/attic/xep-0401-0.3.0.html). - -**Note to developers:** the XEP-0401 protocol is expected to change in the future, -though Prosody will attempt to maintain backwards compatibility with the 0.3.0 protocol -for as long as necessary. - -This module is also responsible for implementing the optional server-side part -of [XEP-0379: Pre-Authenticated Roster Subscriptions](https://xmpp.org/extensions/xep-0379.html). - -**Note to admins:** Loading this module will disable registration for users -without an invite token by default. Control this behaviour - -# Configuration - -| Name | Description | Default | -|--------------------------|----------------------------------------------------------|---------| -| registration_invite_only | Require an invitation token for all account registration | `true` | - -## Example: Invite-only registration - -This setup enables registration **only** for users that have a valid -invite token. - -``` {.lua} -allow_registration = true -registration_invite_only = true -``` - -## Example: Open registration - -This setup allows completely **open registration**, even without -an invite token. - -``` {.lua} -allow_registration = true -registration_invite_only = false -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_invites_register/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,70 @@ +--- +labels: +- 'Stage-Merged' +summary: 'Allow account registration using invite tokens' +... + +Introduction +============ + +::: {.alert .alert-info} +This module has been merged into Prosody as +[mod_invites_register][doc:modules:mod_invites_register]. Users of +Prosody **0.12** and later should not install this version. +::: + +This module is part of the suite of modules that implement invite-based +account registration for Prosody. The other modules are: + +- [mod_invites][doc:modules:mod_invites] +- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] +- [mod_invites_page] +- [mod_invites_register_web] +- [mod_invites_api] +- [mod_register_apps] + +For details and a full overview, start with the [mod_invites] documentation. + +Details +======= + +This module allows clients to register an account using an invite ('preauth') +token generated by mod_invites. It implements the protocol described at +[docs.modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites) +and [XEP-0401 version 0.3.0](https://xmpp.org/extensions/attic/xep-0401-0.3.0.html). + +**Note to developers:** the XEP-0401 protocol is expected to change in the future, +though Prosody will attempt to maintain backwards compatibility with the 0.3.0 protocol +for as long as necessary. + +This module is also responsible for implementing the optional server-side part +of [XEP-0379: Pre-Authenticated Roster Subscriptions](https://xmpp.org/extensions/xep-0379.html). + +**Note to admins:** Loading this module will disable registration for users +without an invite token by default. Control this behaviour + +# Configuration + +| Name | Description | Default | +|--------------------------|----------------------------------------------------------|---------| +| registration_invite_only | Require an invitation token for all account registration | `true` | + +## Example: Invite-only registration + +This setup enables registration **only** for users that have a valid +invite token. + +``` {.lua} +allow_registration = true +registration_invite_only = true +``` + +## Example: Open registration + +This setup allows completely **open registration**, even without +an invite token. + +``` {.lua} +allow_registration = true +registration_invite_only = false +```
--- a/mod_ipcheck/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'XEP-0279: Server IP Check' -... - -Introduction -============ - -Sometimes for various reasons a client might want to know its IP address -as it appears to the server. This [simple -XEP](http://xmpp.org/extensions/xep-0279.html) allows the client to ask -the server for the IP address it is connected from. - -Compatibility -============= - - ----- ------- - 0.7 Works - 0.6 Works - ----- -------
--- a/mod_isolate_host/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Prevent communication between hosts -... - -Introduction -============ - -In some environments it is desirable to isolate one or more hosts, and -prevent communication with external, or even other internal domains. - -Loading mod\_isolate\_host on a host will prevent all communication with -JIDs outside of the current domain, though it is possible to configure -exceptions. - -**Note:** if you just want to prevent communication with external -domains, this is possible without a plugin. See [Prosody: Disabling -s2s](http://prosody.im/doc/s2s#disabling) for more information. - -This module was sponsored by [Exa Networks](http://exa-networks.co.uk/). - -Configuration -============= - -To isolate all hosts by default, add the module to your global -modules\_enabled: - -``` {.lua} -modules_enabled = { - ... - "isolate_host"; - ... -} -``` - -Alternatively you can isolate a single host by putting a -modules\_enabled line under the VirtualHost directive: - -``` {.lua} -VirtualHost "example.com" -modules_enabled = { "isolate_host" } -``` - -After enabling the module, you can add further options to add exceptions -for the isolation: - - Option Description - -------------------------- ----------------------------------------------------------------------------------------- - isolate\_except\_domains A list of domains to allow communication with. - isolate\_except\_users A list of user JIDs allowed to bypass the isolation and communicate with other domains. - -**Note:** Admins of hosts are always allowed to communicate with other -domains - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_jid_prep/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Implement XEP-0328: JID Prep for clients' -... - -Introduction -============ - -This is a plugin that implements the JID prep protocol defined in -<https://xmpp.org/extensions/xep-0328.html> - -Details -======= - -JID prep requests can happen over XMPP using the protocol defined in the -document linked above, or alternatively over HTTP. Simply request: - - http://server:5280/jid_prep/USER@HOST - -The result will be the stringprepped JID, or a 400 Bad Request if the -given JID is invalid. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_json_streams/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: JSON Encodings for XMPP -... - -Introduction -============ - -This plugin encodes XMPP as JSON. This is an implementation of -[XEP-0295: JSON Encodings for -XMPP](http://xmpp.org/extensions/xep-0295.html). - -Simply loading this module makes Prosody accept JSON on C2S streams -(legacy XML clients are still supported). - -For BOSH, it requires mod\_bosh be loaded, and JSON should be directed -at the `/jsonstreams` HTTP path. - -JSON for S2S isn't supported due to the lack of a discovery mechanism, -so we have left that disabled to stay compatible with legacy XML -servers. - -Configuration -============= - -Just add `"json_streams"` in your config's global `modules_enabled` -list, for example: - - modules_enabled = { - ... - "json_streams"; - } - -Strophe.js plugin -================= - -We also developed a [JSON streams -plugin](http://prosody-modules.googlecode.com/hg/mod_json_streams/strophe.jsonstreams.js) -for the popular [strophe.js](http://code.stanziq.com/strophe) library. - -Just include it like this after including the strophe library, and your -strophe-based client will be speaking JSON: - - <script type="text/javascript" src="strophe.jsonstreams.js"></script> - -Be sure to set the HTTP path to `/jsonstreams`. No other changes are -required. - -Compatibility -============= - - ------- ------- - 0.8 Works - trunk Works - ------- ------- - -Quirks -====== - -- This plugin does not currently work with Prosody's [port - multiplexing](http://prosody.im/doc/port_multiplexing) feature
--- a/mod_json_streams/mod_json_streams.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ --- --- XEP-0295: JSON Encodings for XMPP --- - -module.host = "*" - -local httpserver = require "net.httpserver"; -local filters = require "util.filters" -local json = require "util.json" - -local json_escapes = { - ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", - ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"}; - -local s_char = string.char; -for i=0,31 do - local ch = s_char(i); - if not json_escapes[ch] then json_escapes[ch] = ("\\u%.4X"):format(i); end -end - -local state_out = 0; -local state_key_before = 1; -local state_key_in = 2; -local state_key_escape = 3; -local state_key_after = 4; -local state_val_before = 5; -local state_val_in = 6; -local state_val_escape = 7; -local state_val_after = 8; - -local whitespace = { [" "] = true, ["\n"] = true, ["\r"] = true, ["\t"] = true }; -function json_decoder() - local state = state_out; - local quote; - local output = ""; - local buffer = ""; - return function(input) - for ch in input:gmatch(".") do - module:log("debug", "%s | %d", ch, state) - local final = false; - if state == state_out then - if whitespace[ch] then - elseif ch ~= "{" then return nil, "{ expected"; - else state = state_key_before end - elseif state == state_key_before then - if whitespace[ch] then - elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected"; - else quote = ch; state = state_key_in; end - elseif state == state_key_in then - if ch == quote then state = state_key_after; - elseif ch ~= "s" then return nil, "invalid key, 's' expected"; -- only s as key allowed - else end -- ignore key - elseif state == state_key_after then - if whitespace[ch] then - elseif ch ~= ":" then return nil, ": expected"; - else state = state_val_before; end - elseif state == state_val_before then - if whitespace[ch] then - elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected"; - else quote = ch; state = state_val_in; end - elseif state == state_val_in then - if ch == quote then state = state_val_after; - elseif ch == "\\" then state = state_val_escape; - else end - elseif state == state_val_after then - if whitespace[ch] then - elseif ch ~= "}" then return nil, "} expected"; - else state = state_out; - final = true; - end - elseif state == state_val_escape then - state = state_val_in; - else - module:log("error", "Unhandled state: "..state); - return nil, "Unhandled state in parser" - end - buffer = buffer..ch; - if final then - module:log("debug", "%s", buffer) - local tmp; - pcall(function() tmp = json.decode(buffer); end); - if not tmp then return nil, "Invalid JSON"; end - output, buffer = output..tmp.s, ""; - end - end - local _ = output; output = ""; - return _; - end; -end - -function filter_hook(session) - local determined = false; - local is_json = false; - local function in_filter(t) - if not determined then - is_json = (t:sub(1,1) == "{") and json_decoder(); - determined = true; - end - if is_json then - local s, err = is_json(t); - if not err then return s; end - session:close("not-well-formed"); - return; - end - return t; - end - local function out_filter(t) - if is_json then - return '{"s":"' .. t:gsub(".", json_escapes) .. '"}'; -- encode - end - return t; - end - filters.add_filter(session, "bytes/in", in_filter, 100); - filters.add_filter(session, "bytes/out", out_filter, 100); -end - -function module.load() - filters.add_filter_hook(filter_hook); -end -function module.unload() - filters.remove_filter_hook(filter_hook); -end - -function encode(data) - if type(data) == "string" then - data = json.encode({ s = data }); - elseif type(data) == "table" and data.body then - data.body = json.encode({ s = data.body }); - data.headers["Content-Type"] = "application/json"; - end - return data; -end -function handle_request(method, body, request) - local mod_bosh = modulemanager.get_module("*", "bosh") - if mod_bosh then - if body and method == "POST" then - pcall(function() body = json.decode(body).s; end); - end - local _send = request.send; - function request:send(data) return _send(self, encode(data)); end - return encode(mod_bosh.handle_request(method, body, request)); - end - return "<html><body>mod_bosh not loaded</body></html>"; -end - -local function setup() - local ports = module:get_option("jsonstreams_ports") or { 5280 }; - httpserver.new_from_config(ports, handle_request, { base = "jsonstreams" }); -end -if prosody.start_time then -- already started - setup(); -else - prosody.events.add_handler("server-started", setup); -end
--- a/mod_json_streams/strophe.jsonstreams.js Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ - -/* jsonstreams plugin -** -** This plugin upgrades Strophe to support XEP-0295: JSON Encodings for XMPP -** -*/ - -Strophe.addConnectionPlugin('jsonstreams', { - init: function (conn) { - - var parseXMLString = function(xmlStr) { - var xmlDoc = null; - if (window.ActiveXObject) { - xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); - xmlDoc.async=false; - xmlDoc.loadXML(xmlStr); - } else { - var parser = new DOMParser(); - xmlDoc = parser.parseFromString(xmlStr, "text/xml"); - } - return xmlDoc; - } - - // replace Strophe.Request._newXHR with new jsonstreams version - // if JSON is detected - if (window.JSON) { - var _newXHR = Strophe.Request.prototype._newXHR; - Strophe.Request.prototype._newXHR = function () { - var _xhr = _newXHR.apply(this, arguments); - var xhr = { - readyState: 0, - responseText: null, - responseXML: null, - status: null, - open: function(a, b, c) { return _xhr.open(a, b, c) }, - abort: function() { _xhr.abort(); }, - send: function(data) { - data = JSON.stringify({"s":data}); - return _xhr.send(data); - } - }; - var req = this; - xhr.onreadystatechange = this.func.bind(null, this); - _xhr.onreadystatechange = function() { - xhr.readyState = _xhr.readyState; - if (xhr.readyState != 4) { - xhr.status = 0; - xhr.responseText = ""; - xhr.responseXML = null; - } else { - xhr.status = _xhr.status; - xhr.responseText = _xhr.responseText; - xhr.responseXML = _xhr.responseXML; - if (_xhr.responseText && !(_xhr.responseXML - && _xhr.responseXML.documentElement - && _xhr.responseXML.documentElement.tagName != "parsererror")) { - var data = JSON.parse(_xhr.responseText); - if (data && data.s) { - xhr.responseText = data.s; - xhr.responseXML = parseXMLString(data.s); - } - } - } - if ("function" == typeof xhr.onreadystatechange) { xhr.onreadystatechange(req); } - } - return xhr; - }; - } else { - Strophe.error("jsonstreams plugin loaded, but JSON not found." + - " Falling back to native XHR implementation."); - } - } -});
--- a/mod_jsxc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -rockspec: - build: - copy_directories: - - templates - dependencies: - - mod_http_libjs -summary: JSXC demo ---- - -Try out JSXC easily by serving it from Prosodys built-in HTTP server. - -Uses [mod_http_libjs] to serve jQuery, on Debian you can `apt install -libjs-jquery`. - -# Configuration - -mod_jsxc can be set up to either use resources from a separate HTTP -server or serve resources from Prosody's built-in HTTP server. - -## Using CDN - -`jsxc_cdn` -: String. Base URL where JSXC resources are served from. Defaults to - empty string. - -`jsxc_version` -: String. Concatenated with the CDN URL. Defaults to empty string. - -## Local resources - -Download a JSXC release archive and unpack it somewhere on your server. - -`jsxc_resources` -: String. Path to the `dist` directory containing JSXC resources on -the local file system. Disabled by default. - -## Other options - -`jquery_url` -: String. URL or relative path to jQuery. Defaults to - `"/share/jquery/jquery.min.js"` which will work with - [mod_http_libjs].
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_jsxc/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,43 @@ +--- +rockspec: + build: + copy_directories: + - templates + dependencies: + - mod_http_libjs +summary: JSXC demo +--- + +Try out JSXC easily by serving it from Prosodys built-in HTTP server. + +Uses [mod_http_libjs] to serve jQuery, on Debian you can `apt install +libjs-jquery`. + +# Configuration + +mod_jsxc can be set up to either use resources from a separate HTTP +server or serve resources from Prosody's built-in HTTP server. + +## Using CDN + +`jsxc_cdn` +: String. Base URL where JSXC resources are served from. Defaults to + empty string. + +`jsxc_version` +: String. Concatenated with the CDN URL. Defaults to empty string. + +## Local resources + +Download a JSXC release archive and unpack it somewhere on your server. + +`jsxc_resources` +: String. Path to the `dist` directory containing JSXC resources on +the local file system. Disabled by default. + +## Other options + +`jquery_url` +: String. URL or relative path to jQuery. Defaults to + `"/share/jquery/jquery.min.js"` which will work with + [mod_http_libjs].
--- a/mod_lastlog/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Log last login time -... - -Introduction -============ - -Simple module that stores the timestamp of when a user logs in. - -Usage -===== - -As with all modules, copy it to your plugins directory and then add it -to the modules\_enabled list: - -``` lua -modules_enabled = { - -- other modules - "lastlog", -} -``` - -Configuration -============= - -There are some options you can add to your config file: - - Name Type Default Description - ------------------------- ------- ------- ------------------------------------ - lastlog\_ip\_address boolean false Log the IP address of the user? - lastlog\_stamp\_offline boolean false Add timestamp to offline presence? - -Usage -===== - -You can check a user's last activity by running: - - prosodyctl mod_lastlog username@example.com - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_lastlog/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,47 @@ +--- +labels: +- 'Stage-Beta' +summary: Log last login time +... + +Introduction +============ + +Simple module that stores the timestamp of when a user logs in. + +Usage +===== + +As with all modules, copy it to your plugins directory and then add it +to the modules\_enabled list: + +``` lua +modules_enabled = { + -- other modules + "lastlog", +} +``` + +Configuration +============= + +There are some options you can add to your config file: + + Name Type Default Description + ------------------------- ------- ------- ------------------------------------ + lastlog\_ip\_address boolean false Log the IP address of the user? + lastlog\_stamp\_offline boolean false Add timestamp to offline presence? + +Usage +===== + +You can check a user's last activity by running: + + prosodyctl mod_lastlog username@example.com + +Compatibility +============= + + ----- ------- + 0.9 Works + ----- -------
--- a/mod_lastlog2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Record last timestamp of events ---- - -# Introduction - -Similar to [mod_lastlog], this module records the last timestamp of -various events, but keeps the last timestamp per type of event, instead -of the last event. - -# Usage - -As with all modules, copy it to your plugins directory and then add it -to the modules\_enabled list: - -``` {.lua} -modules_enabled = { - -- other modules - "lastlog2", -} -``` - -# Configuration - -There are some options you can add to your config file: - - Name Type Default Description - ---------------------- --------- --------- --------------------------------- - lastlog\_ip\_address boolean false Log the IP address of the user? - -# Usage - -You can check a user's last activity by running: - - prosodyctl mod_lastlog2 username@example.com - -# Compatibility - - Version State - --------- ------- - Any *TBD*
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_lastlog2/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,47 @@ +--- +labels: +- 'Stage-Beta' +summary: Record last timestamp of events +--- + +# Introduction + +Similar to [mod_lastlog], this module records the last timestamp of +various events, but keeps the last timestamp per type of event, instead +of the last event. + +# Usage + +As with all modules, copy it to your plugins directory and then add it +to the modules\_enabled list: + +``` {.lua} +modules_enabled = { + -- other modules + "lastlog2", +} +``` + +# Configuration + +There are some options you can add to your config file: + + Name Type Default Description + ---------------------- --------- --------- --------------------------------- + lastlog\_ip\_address boolean false Log the IP address of the user? + +# Usage + +You can check a user's last activity by running: + + prosodyctl mod_lastlog2 username@example.com + +With Prosody trunk the command can be used via the shell: + + prosodyctl shell lastlog show username@example.com + +# Compatibility + + Version State + --------- ------- + Any *TBD*
--- a/mod_latex/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Replace LaTeX markup in messages with embedded images -... - -Introduction -============ - -This module intercepts messages between users and into chatrooms, and -attaches a links to a rendered version of any -[LaTeX](http://en.wikipedia.org/wiki/LaTeX) in the message. This -requires client support for -[XHTML-IM](http://xmpp.org/extensions/xep-0071.html), and fetching -images via HTTP. - -This module was tested with the [Gajim](http://gajim.org/) client. - -Details -======= - -There is no configuration (yet). The module uses -[MathTran](http://www.mathtran.org/) to render the LaTeX. - -Todo -==== - -- Support for other rendering services (easy) -- Provide a built-in rendering service (e.g. mimetex) -- Send the images inline over XMPP (little client support at the - moment) - -Compatibility -============= - - ----- ------- - 0.6 Works - 0.7 Works - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_latex/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,39 @@ +--- +labels: +- 'Stage-Beta' +summary: Replace LaTeX markup in messages with embedded images +... + +Introduction +============ + +This module intercepts messages between users and into chatrooms, and +attaches a links to a rendered version of any +[LaTeX](http://en.wikipedia.org/wiki/LaTeX) in the message. This +requires client support for +[XHTML-IM](http://xmpp.org/extensions/xep-0071.html), and fetching +images via HTTP. + +This module was tested with the [Gajim](http://gajim.org/) client. + +Details +======= + +There is no configuration (yet). The module uses +[MathTran](http://www.mathtran.org/) to render the LaTeX. + +Todo +==== + +- Support for other rendering services (easy) +- Provide a built-in rendering service (e.g. mimetex) +- Send the images inline over XMPP (little client support at the + moment) + +Compatibility +============= + + ----- ------- + 0.6 Works + 0.7 Works + ----- -------
--- a/mod_limit_auth/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ ---- -summary: Throttle authentication attempts with optional tarpit -... - -Introduction -============ - -This module lets you put a per-IP limit on the number of failed -authentication attempts. - -It features an optioanal -[tarpit](https://en.wikipedia.org/wiki/Tarpit_%28networking%29), i.e. -waiting some time before returning an "authentication failed" response. - -Configuration -============= - -``` {.lua} -modules_enabled = { - -- your other modules - "limit_auth"; -} - -limit_auth_period = 30 -- over 30 seconds - -limit_auth_max = 5 -- tolerate no more than 5 failed attempts - - -- Will only work with Prosody trunk: -limit_auth_tarpit_delay = 10 -- delay answer this long -``` - -Compatibility -============= - -Requires 0.9 or later. The tarpit feature requires Prosody trunk.
--- a/mod_limits/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: 'Connection-level rate limiting' -superseded_by: mod_limits -... - -Since Prosody 0.10, this module is [included in Prosody](https://prosody.im/doc/modules/mod_limits), you will be redirected there shortly.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_limits/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,8 @@ +--- +labels: +- 'Stage-Obsolete' +summary: 'Connection-level rate limiting' +superseded_by: mod_limits +... + +Since Prosody 0.10, this module is [included in Prosody](https://prosody.im/doc/modules/mod_limits), you will be redirected there shortly.
--- a/mod_limits_exception/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -summary: Allow specified JIDs to bypass rate limits -... - -This module allows you to configure a list of JIDs that should be allowed to -bypass rate limit restrictions. - -It is designed for Prosody 0.11.x. Prosody 0.12.x supports this feature -natively. - -## Configuration - -First, enable this module by adding `"limits_exception"` to your -`modules_enabled` list. - -Next, configure a list of JIDs to exclude from rate limiting: - -``` -unlimited_jids = { "user1@example.com", "user2@example.net" } -``` - -## Compatibility - -Made for Prosody 0.11.x only. - -Using this module with Prosody trunk/0.12 may cause unexpected behaviour.
--- a/mod_list_active/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -Description -=========== - -This module lists those users, who **have** used their account in a -defined time-frame, basically the inverse of [mod_list_inactive]. - -Dependencies -============ - -[mod_lastlog] must be enabled to collect the data used by this module. - -Usage -===== - - prosodyctl mod_list_active example.com time [format] - -Time is a number followed by 'day', 'week', 'month' or 'year' - -Formats are: - - --------- ------------------------------------------------ - default `user@example.com` - event `user@example.com last action` - --------- ------------------------------------------------ - -Example -======= - - prosodyctl mod_list_active example.com 1year - -Help -==== - - prosodyctl mod_list_active
--- a/mod_list_inactive/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -Description -=========== - -This module lists those users, who haven't used their account in a -defined time-frame. - -Dependencies -============ - -[mod_lastlog] must be enabled to collect the data used by this module. - -Usage -===== - - prosodyctl mod_list_inactive example.com time [format] - -Time is a number followed by 'day', 'week', 'month' or 'year' - -Formats are: - - --------- ------------------------------------------------ - delete `user:delete"user@example.com" -- last action` - default `user@example.com` - event `user@example.com last action` - --------- ------------------------------------------------ - -Example -======= - - prosodyctl mod_list_inactive example.com 1year - -Help -==== - - prosodyctl mod_list_inactive
--- a/mod_listusers/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' ---- - -::: {.alert .alert-warning} -As of Prosody 0.12.x, `prosodyctl shell user list HOST` can be used -instead of this module. -::: - -This module adds a command to `prosodyctl` for listing users. - -``` sh -prosodyctl mod_listusers -```
--- a/mod_log_auth/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Log failed authentication attempts with their IP address -... - -Introduction -============ - -Prosody doesn't write IP addresses to its log file by default for -privacy reasons (unless debug logging is enabled). - -This module enables logging of the IP address in a failed authentication -attempt so that those trying to break into accounts for example can be -blocked. - -fail2ban configuration -====================== - -fail2ban is a utility for monitoring log files and automatically -blocking "bad" IP addresses at the firewall level. - -With this module enabled in Prosody you can use the following example -configuration for fail2ban: - - # /etc/fail2ban/filter.d/prosody-auth.conf - # Fail2Ban configuration file for prosody authentication - [Definition] - failregex = Failed authentication attempt \(not-authorized\) for user .* from IP: <HOST> - ignoreregex = - -And at the appropriate place (usually the bottom) of -/etc/fail2ban/jail.conf add these lines: - - [prosody] - enabled = true - port = 5222 - filter = prosody-auth - logpath = /var/log/prosody/prosody*.log - maxretry = 6 - -Compatibility -------------- - - ------- -------------- - trunk Works - 0.9 Works - 0.8 Doesn't work - ------- --------------
--- a/mod_log_events_by_cpu_usage/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -This module logs events where more than a certain amount of CPU time was -spent. - -``` lua -log_cpu_threshold = 0.01 -- in seconds, so this is 10 milliseconds -``` - -Uses the Lua -[`os.clock()`](http://www.lua.org/manual/5.2/manual.html#pdf-os.clock) -function to estimate CPU usage.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_events_by_cpu_usage/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,10 @@ +This module logs events where more than a certain amount of CPU time was +spent. + +``` lua +log_cpu_threshold = 0.01 -- in seconds, so this is 10 milliseconds +``` + +Uses the Lua +[`os.clock()`](http://www.lua.org/manual/5.2/manual.html#pdf-os.clock) +function to estimate CPU usage.
--- a/mod_log_events_by_memory/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -This module compares the memory usage reported by Lua before and after -each event and reports it to the log if it exceeds the configuration -setting `log_memory_threshold` (in bytes). - -``` lua -log_memory_threshold = 20*1024 -``` - -If you are looking to identify memory leaks, please first read [Three -kinds of memory -leaks](https://blog.nelhage.com/post/three-kinds-of-leaks/). - -Prosody runs on Lua which uses automatic memory management with garbage -collection, so the numbers reported by this module are very likely to be -useless for the purpose of identifying memory leaks. Large, but -temporary, increases in memory usage can however highlight other kinds -of performance problems and sometimes even provide hits for where to -look for memory leaks.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_events_by_memory/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,18 @@ +This module compares the memory usage reported by Lua before and after +each event and reports it to the log if it exceeds the configuration +setting `log_memory_threshold` (in bytes). + +``` lua +log_memory_threshold = 20*1024 +``` + +If you are looking to identify memory leaks, please first read [Three +kinds of memory +leaks](https://blog.nelhage.com/post/three-kinds-of-leaks/). + +Prosody runs on Lua which uses automatic memory management with garbage +collection, so the numbers reported by this module are very likely to be +useless for the purpose of identifying memory leaks. Large, but +temporary, increases in memory usage can however highlight other kinds +of performance problems and sometimes even provide hits for where to +look for memory leaks.
--- a/mod_log_http/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -summary: HTTP request logging -... - -Introduction -============ - -This module logs *outgoing* requests that go via the internal net.http API. - -Output format liable to change. - -Configuration -============= - -One option is required, set `log_http_file` to the file path you would like to log to. - -Compatibility -============= - - ----- ------- - 0.10 Works (requires 375cf924fce1 or later) - ----- -------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_http/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,22 @@ +--- +summary: HTTP request logging +... + +Introduction +============ + +This module logs *outgoing* requests that go via the internal net.http API. + +Output format liable to change. + +Configuration +============= + +One option is required, set `log_http_file` to the file path you would like to log to. + +Compatibility +============= + + ----- ------- + 0.10 Works (requires 375cf924fce1 or later) + ----- -------
--- a/mod_log_json/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ ---- -summary: JSON Log Sink ---- - -Conifiguration -============== - -Here we log to `/var/log/prosody/prosody.json`: - -``` {.lua} -log = { - -- your other log sinks - info = "/var/log/prosody/prosody.log" - -- add this: - { to = "json", filename = "/var/log/prosody/prosody.json" }; -} -``` - -## UDP - -Alternatively, it can send logs via UDP: - -```lua -log = { - { to = "json", udp_host = "10.1.2.3", udp_port = "9999" }; -} -``` - -Format -====== - -JSON log files consist of a series of `\n`-separated JSON objects, -suitable for mangling with tools like -[`jq`](https://stedolan.github.io/jq/). - -Example (with whitespace and indentation for readability): - -``` {.json} -{ - "args" : [], - "datetime" : "2019-11-03T13:38:28Z", - "level" : "info", - "message" : "Client connected", - "source" : "c2s55f267f5b9d0" -} -{ - "args" : [ - "user@example.net" - ], - "datetime" : "2019-11-03T13:38:28Z", - "level" : "debug", - "message" : "load_roster: asked for: %s", - "source" : "rostermanager" -} -``` - -`datetime` -: [XEP-0082]-formatted timestamp. - -`source` -: Log source, usually a module or a connected session. - -`level` -: `debug`, `info`, `warn` or `error` - -`message` -: The log message in `printf` format. Combine with `args` to get the - final message. - -`args` -: Array of extra arguments, corresponding to `printf`-style `%s` - formatting in the `message`. - -Formatted message ------------------ - -If desired, at the obvious expense of performance, the formatted version of -the string can be included in the JSON object by specifying the `formatted_as` -key in the logger config: - -``` {.lua} -log = { - -- ... other sinks ... - { to = "json", formatted_as = "msg_formatted", filename = "/var/log/prosody/prosody.json" }; -} - -``` - -This will expose the formatted message in the JSON as separate `msg_formatted` -key. It is possible to override existing keys using this (for example, the -`message` key), but not advisible.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_json/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,91 @@ +--- +summary: JSON Log Sink +--- + +Conifiguration +============== + +Here we log to `/var/log/prosody/prosody.json`: + +``` {.lua} +log = { + -- your other log sinks + info = "/var/log/prosody/prosody.log" + -- add this: + { to = "json", filename = "/var/log/prosody/prosody.json" }; +} +``` + +## UDP + +Alternatively, it can send logs via UDP: + +```lua +log = { + { to = "json", udp_host = "10.1.2.3", udp_port = "9999" }; +} +``` + +Format +====== + +JSON log files consist of a series of `\n`-separated JSON objects, +suitable for mangling with tools like +[`jq`](https://stedolan.github.io/jq/). + +Example (with whitespace and indentation for readability): + +``` {.json} +{ + "args" : [], + "datetime" : "2019-11-03T13:38:28Z", + "level" : "info", + "message" : "Client connected", + "source" : "c2s55f267f5b9d0" +} +{ + "args" : [ + "user@example.net" + ], + "datetime" : "2019-11-03T13:38:28Z", + "level" : "debug", + "message" : "load_roster: asked for: %s", + "source" : "rostermanager" +} +``` + +`datetime` +: [XEP-0082]-formatted timestamp. + +`source` +: Log source, usually a module or a connected session. + +`level` +: `debug`, `info`, `warn` or `error` + +`message` +: The log message in `printf` format. Combine with `args` to get the + final message. + +`args` +: Array of extra arguments, corresponding to `printf`-style `%s` + formatting in the `message`. + +Formatted message +----------------- + +If desired, at the obvious expense of performance, the formatted version of +the string can be included in the JSON object by specifying the `formatted_as` +key in the logger config: + +``` {.lua} +log = { + -- ... other sinks ... + { to = "json", formatted_as = "msg_formatted", filename = "/var/log/prosody/prosody.json" }; +} + +``` + +This will expose the formatted message in the JSON as separate `msg_formatted` +key. It is possible to override existing keys using this (for example, the +`message` key), but not advisible.
--- a/mod_log_mark/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ ---- -summary: Log a message once per minute -... - -This module sends `-- MARK --` to the log once per minute. This may be -useful to give a sense of how busy the server is or see that logging and -timers are still working.
--- a/mod_log_messages_sql/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -# Introduction - -::: {.alert .alert-danger} -Consider using [mod_mam][doc:modules:mod_mam] together with -[mod_readonly] instead. -::: - -This module logs messages to a SQL database. - -SQL connection options are configured in a `message_log_sql` option, -which has the same syntax as the `sql` option for -[mod_storage_sql][doc:modules:mod_storage_sql]. - -# Usage - -You will need to create the following table in the configured database: - -``` sql -CREATE TABLE `prosodyarchive` ( - `host` TEXT, - `user` TEXT, - `store` TEXT, - `id` INTEGER PRIMARY KEY AUTOINCREMENT, - `when` INTEGER, - `with` TEXT, - `resource` TEXT, - `stanza` TEXT); -``` - -# Compatibility - -Does *NOT* work with 0.10 due to a conflict with the new archiving -support in `mod_storage_sql`·
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_log_messages_sql/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,33 @@ +# Introduction + +::: {.alert .alert-danger} +Consider using [mod_mam][doc:modules:mod_mam] together with +[mod_readonly] instead. +::: + +This module logs messages to a SQL database. + +SQL connection options are configured in a `message_log_sql` option, +which has the same syntax as the `sql` option for +[mod_storage_sql][doc:modules:mod_storage_sql]. + +# Usage + +You will need to create the following table in the configured database: + +``` sql +CREATE TABLE `prosodyarchive` ( + `host` TEXT, + `user` TEXT, + `store` TEXT, + `id` INTEGER PRIMARY KEY AUTOINCREMENT, + `when` INTEGER, + `with` TEXT, + `resource` TEXT, + `stanza` TEXT); +``` + +# Compatibility + +Does *NOT* work with 0.10 due to a conflict with the new archiving +support in `mod_storage_sql`·
--- a/mod_log_rate/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ ---- -summary: Collect statistics on rate of log messages -... - -Introduction -============ - -If you ever wanted to collect statistics on the number of log messages, -this is the module for you! - -Setup -===== - -After [installing the module](https://prosody.im/doc/installing_modules) -and adding it to modules\_enabled as most other modules, you also need -to add it to your logging config: - -``` {.lua} -log = { - -- your other log sinks - info = "/var/log/prosody/prosody.log" - -- add this: - { to = "measure" }; -} -``` - -Then log messages will be counted by -[statsmanager](https://prosody.im/doc/developers/core/statsmanager). - -Compatibility -============= - -Reqires Prosody 0.10 or above.
--- a/mod_log_ringbuffer/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Log to in-memory ringbuffer' -... - -Introduction -============ - -Sometimes debug logs are too verbose for continuous logging to disk. However -occasionally you may be interested in the debug logs when a certain event occurs. - -This module allows you to store all logs in a fixed-size buffer in Prosody's memory, -and dump them to a file whenever you want. - -# Configuration - -First of all, you need to load the module by adding it to your global `modules_enabled`: - -``` {.lua} -modules_enabled = { - ... - "log_ringbuffer"; - ... -} -``` - -By default the module will do nothing - you need to configure a log sink, using Prosody's -usual [logging configuration](https://prosody.im/doc/advanced_logging). - -``` {.lua} -log = { - -- Log errors to a file - error = "/var/log/prosody/prosody.err"; - - -- Log debug and higher to a 2MB buffer - { to = "ringbuffer", size = 1024*1024*2, filename_template = "debug-logs-{pid}-{count}.log", signal = "SIGUSR2" }; -} -``` - -The possible fields of the logging config entry are: - -`to` -: Set this to `"ringbuffer"`. - -`levels` -: The log levels to capture, e.g. `{ min = "debug" }`. By default, all levels are captured. - -`size` -: The size, in bytes, of the buffer. When the buffer fills, - old data will be overwritten by new data. - -`lines` -: If specified, preserves the latest N complete lines in the - buffer. The `size` option is ignored when this option is set. - -`filename` -: The name of the file to dump logs to when triggered. - -`filename_template` -: This parameter may optionally be specified instead of `filename. It - may contain a number of variables, described below. Defaults to - `"{paths.data}/ringbuffer-logs-{pid}-{count}.log"`. - -Only one of the following triggers may be specified: - -`signal` -: A signal that will cause the buffer to be dumped, e.g. `"SIGUSR2"`. - Do not use any signal that is used by any other Prosody module, to - avoid conflicts. - -`event` -: Alternatively, the name of a Prosody global event that will trigger - the logs to be dumped, e.g. `"config-reloaded"`. - -## Filename variables - -If `filename_template` is specified instead of `filename`, it may contain -any of the following variables in curly braces, e.g. `{pid}`. - -`pid` -: The PID of the current process - -`count` -: A counter that begins at 0 and increments for each dump made by - the current process. - -`time` -: The unix timestamp at which the dump is made. It can be formatted - to human-readable local time using `{time|yyyymmdd}` and `{time|hhmmss}`. - -`paths` -: Allows access to Prosody's known filesystem paths, use e.g. `{paths.data}` - for the path to Prosody's data directory. - -The filename does not have to be unique for every dump - if a file with the same -name already exists, it will be appended to. - -## Integration with mod_debug_traceback - -This module can be used in combination with [mod_debug_traceback] so that debug -logs are dumped at the same time as the traceback. Use the following configuration: - -``` {.lua} -log = { - --- - -- other optional logging config here -- - --- - - { - to = "ringbuffer"; - filename_template = "{paths.data}/traceback-{pid}-{count}.log"; - event = "debug_traceback/triggered"; - }; -} -``` - -If the filename template matches the traceback path, both logs and traceback will -be combined into the same file. Of course separate files can be specified if preferred. - -# Compatibility - -0.12 and later.
--- a/mod_log_slow_events/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Log warning when event handlers take too long -... - -Introduction -============ - -Most activities in Prosody take place within our built-in events framework, for -example stanza processing and HTTP request handling, authentication, etc. - -Modules are able to execute code when an event occurs, and they should return -as quickly as possible. Poor performance (e.g. slow or laggy server) can be caused -by event handlers that are slow to respond. - -This module is able to monitor how long each event takes to be processed, and -logs a warning if an event takes above a certain amount of time, including -providing any details about the event such as the user or stanza that triggered it. - -The aim is to help debug why a server may not be as responsive as it should be, -and ultimately which module is to blame for that. - -Configuration -====================== - -There is a single configuration option: - -``` - -- Set the number of seconds an event may take before - -- logging a warning (fractional values are ok) - log_slow_events_threshold = 0.5 -``` - -Metrics -======= - -In addition to the log messages, a new 'slow_events' metric will be exported to -your configured stats backend (if any). - -Compatibility -------------- - - ------- -------------- - trunk Works - 0.10 Works - 0.9 Doesn't work - 0.8 Doesn't work - ------- --------------
--- a/mod_mam/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: 'XEP-0313: Message Archive Management' -superseded_by: mod_mam -... - -Since Prosody 0.10, this module is [included in Prosody](https://prosody.im/doc/modules/mod_mam), you will be redirected there shortly.
--- a/mod_mam_adhoc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Ad-hoc interface to Message Archive Management Settings' -... - -Introduction -============ - -This module complements mod\_mam by allowing clients to change archiving -preferences through an Ad-hoc command. - -Details -======= - -When enabled, an "Archive Settings" command should appear in the list of -Ad-hoc commands available. This allows the user to change default policy -(always, never, roster) and which JIDs to always store or never store. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "mam\_adhoc" to your modules\_enabled list: - -``` {.lua} -modules_enabled = { - -- ... - "mam", - "mam_adhoc", - -- ... -} -```
--- a/mod_mam_archive/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0136: Message Archiving frontend for mod\_mam' -... - -Introduction -============ - -Implementation of [XEP-0136: Message -Archiving](http://xmpp.org/extensions/xep-0136.html) for -[mod\_mam](mod_mam.html). - -Details -======= - -See [mod\_mam] for details. - -Usage -===== - -First configure mod\_mam as specified in it's [wiki][mod\_mam]. Make -sure it uses sql2 storage backend. - -Then add "mam\_archive" to your modules\_enabled list: - - modules_enabled = { - -- ... - "mam_archive", - -- ... - } - -Configuration -============= - -Because of the fact that [XEP-0136] defines a 'conversation' concept not -present in [XEP-0313], we have to assume some periods of chat history as -'conversations'. - -Conversation interval defaults to one day, to provide for a convenient -usage. - - archive_conversation_interval = 86400; -- defined in seconds. One day by default - -That is the only reason SQL database is required as well. - -Compatibility -============= - - ------ --------------- - 0.10 Works - 0.9 Does not work - ------ --------------- - - ------------ ------------ - PostgreSQL Tested - MySQL Not tested - SQLite Tested - ------------ ------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_mam_archive/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,59 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'XEP-0136: Message Archiving frontend for mod\_mam' +... + +Introduction +============ + +Implementation of [XEP-0136: Message +Archiving](http://xmpp.org/extensions/xep-0136.html) for +[mod\_mam](mod_mam.html). + +Details +======= + +See [mod\_mam] for details. + +Usage +===== + +First configure mod\_mam as specified in it's [wiki][mod\_mam]. Make +sure it uses sql2 storage backend. + +Then add "mam\_archive" to your modules\_enabled list: + + modules_enabled = { + -- ... + "mam_archive", + -- ... + } + +Configuration +============= + +Because of the fact that [XEP-0136] defines a 'conversation' concept not +present in [XEP-0313], we have to assume some periods of chat history as +'conversations'. + +Conversation interval defaults to one day, to provide for a convenient +usage. + + archive_conversation_interval = 86400; -- defined in seconds. One day by default + +That is the only reason SQL database is required as well. + +Compatibility +============= + + ------ --------------- + 0.10 Works + 0.9 Does not work + ------ --------------- + + ------------ ------------ + PostgreSQL Tested + MySQL Not tested + SQLite Tested + ------------ ------------
--- a/mod_mam_muc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: 'XEP-0313: Message Archive Management for MUC' -superseded_by: mod_muc_mam -... - -Since Prosody 0.11, this module is [included in Prosody](https://prosody.im/doc/modules/mod_muc_mam), you will be redirected there shortly.
--- a/mod_manifesto/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ ---- -summary: Module for raising awareness about the Security Test Days -... - -Introduction -============ - -This module informs users about the XMPP Test day and whether their -contacts are affected. For mor info about the test day, see -<https://stpeter.im/journal/1496.html> - -Configuration -============= - -``` lua -manifesto_contact_encryption_warning = [[ - Your rant about security here -]] -admin_contact_address = "mailto:xmpp@example.com" -``` - -`admin_contact_address` can be a JID or a `mailto:` URI. - -The default for `manifesto_contact_encryption_warning` is the following: - - Hello there. - - This is a brief system message to let you know about some upcoming changes to the $HOST service. - - Some of your contacts are on other Jabber/XMPP services that do not support encryption. As part of an initiative to increase the security of the Jabber/XMPP network, this service ($HOST) will be participating in a series of tests to discover the impact of our planned changes, and you may lose the ability to communicate with some of your contacts. - - The test days will be on the following dates: January 4, February 22, March 22 and April 19. On these days we will require that all client and server connections are encrypted. Unless they enable encryption before that, you will be unable to communicate with your contacts that use these services: - - $SERVICES - - Your affected contacts are: - - $CONTACTS - - What can you do? You may tell your contacts to inform their service administrator about their lack of encryption. Your contacts may also switch to a more secure service. A list of public services can be found at https://xmpp.net/directory.php - - For more information about the Jabber/XMPP security initiative that we are participating in, please read the announcement at https://stpeter.im/journal/1496.html - - If you have any questions or concerns, you may contact us via $CONTACTVIA at $CONTACT - -Translations would be appreciated. There is currently a Swedish -translation residing in a text file in the same directory as the module.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_manifesto/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,47 @@ +--- +summary: Module for raising awareness about the Security Test Days +... + +Introduction +============ + +This module informs users about the XMPP Test day and whether their +contacts are affected. For mor info about the test day, see +<https://stpeter.im/journal/1496.html> + +Configuration +============= + +``` lua +manifesto_contact_encryption_warning = [[ + Your rant about security here +]] +admin_contact_address = "mailto:xmpp@example.com" +``` + +`admin_contact_address` can be a JID or a `mailto:` URI. + +The default for `manifesto_contact_encryption_warning` is the following: + + Hello there. + + This is a brief system message to let you know about some upcoming changes to the $HOST service. + + Some of your contacts are on other Jabber/XMPP services that do not support encryption. As part of an initiative to increase the security of the Jabber/XMPP network, this service ($HOST) will be participating in a series of tests to discover the impact of our planned changes, and you may lose the ability to communicate with some of your contacts. + + The test days will be on the following dates: January 4, February 22, March 22 and April 19. On these days we will require that all client and server connections are encrypted. Unless they enable encryption before that, you will be unable to communicate with your contacts that use these services: + + $SERVICES + + Your affected contacts are: + + $CONTACTS + + What can you do? You may tell your contacts to inform their service administrator about their lack of encryption. Your contacts may also switch to a more secure service. A list of public services can be found at https://xmpp.net/directory.php + + For more information about the Jabber/XMPP security initiative that we are participating in, please read the announcement at https://stpeter.im/journal/1496.html + + If you have any questions or concerns, you may contact us via $CONTACTVIA at $CONTACT + +Translations would be appreciated. There is currently a Swedish +translation residing in a text file in the same directory as the module.
--- a/mod_map/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ ---- -labels: -- 'Stage-Experimental' -summary: Prototype MAM summary ---- - -This is a prototype for an experimental archive summary API recently -added in [Prosody trunk](https://hg.prosody.im/trunk/rev/2c5546cc5c70). - -# Protocol - -::: {.alert .alert-danger} -This is not a finished protocol, but a prototype meant for testing. -::: - -A basic query: - -``` {.xml} -<iq id="lx7" type="get"> - <summary xmlns="xmpp:prosody.im/mod_map"/> -</iq> -``` - -Answered like: - -``` {.xml} -<iq type="result" id="lx7"> - <summary xmlns="xmpp:prosody.im/mod_map"> - <item jid="juliet@capulet.lit"> - <count>3</count> - <start>2019-02-25T15:48:00+0100</start> - <end>2019-08-23T01:39:50+02:00</end> - <body>O Romeo, Romeo! wherefore art thou Romeo?</body> - </item> - </summary> -</iq> -``` - -It can also take dataform and RSM parameters similar to a [filtered MAM -query](https://xmpp.org/extensions/xep-0313.html#filter). - -E.g if the last message you received had an id `09af3-cc343-b409f` then -the following query would tell you who sent you messages since: - -``` {.xml} -<iq id="lx8" type="get"> - <summary xmlns="xmpp:prosody.im/mod_map"> - <set xmlns="http://jabber.org/protocol/rsm"> - <max>10</max> - <after>09af3-cc343-b409f</after> - </set> - </summary> -</iq> -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_map/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,54 @@ +--- +labels: +- 'Stage-Experimental' +summary: Prototype MAM summary +--- + +This is a prototype for an experimental archive summary API recently +added in [Prosody trunk](https://hg.prosody.im/trunk/rev/2c5546cc5c70). + +# Protocol + +::: {.alert .alert-danger} +This is not a finished protocol, but a prototype meant for testing. +::: + +A basic query: + +``` {.xml} +<iq id="lx7" type="get"> + <summary xmlns="xmpp:prosody.im/mod_map"/> +</iq> +``` + +Answered like: + +``` {.xml} +<iq type="result" id="lx7"> + <summary xmlns="xmpp:prosody.im/mod_map"> + <item jid="juliet@capulet.lit"> + <count>3</count> + <start>2019-02-25T15:48:00+0100</start> + <end>2019-08-23T01:39:50+02:00</end> + <body>O Romeo, Romeo! wherefore art thou Romeo?</body> + </item> + </summary> +</iq> +``` + +It can also take dataform and RSM parameters similar to a [filtered MAM +query](https://xmpp.org/extensions/xep-0313.html#filter). + +E.g if the last message you received had an id `09af3-cc343-b409f` then +the following query would tell you who sent you messages since: + +``` {.xml} +<iq id="lx8" type="get"> + <summary xmlns="xmpp:prosody.im/mod_map"> + <set xmlns="http://jabber.org/protocol/rsm"> + <max>10</max> + <after>09af3-cc343-b409f</after> + </set> + </summary> +</iq> +```
--- a/mod_measure_client_features/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on client features ---- - -Description -=========== - -This module measures the features supported by each connected client. - -It requires mod\_cache\_c2s\_caps to be loaded on each VirtualHost to -provide the disco#info data.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_measure_client_features/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,13 @@ +--- +labels: +- Statistics +summary: Collect statistics on client features +--- + +Description +=========== + +This module measures the features supported by each connected client. + +It requires mod\_cache\_c2s\_caps to be loaded on each VirtualHost to +provide the disco#info data.
--- a/mod_measure_client_identities/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on user identities ---- - -Description -=========== - -This module measures the number of connected clients per identity. - -It requires mod\_cache\_c2s\_caps to be loaded on each VirtualHost to -provide the data identifying a user’s client.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_measure_client_identities/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,13 @@ +--- +labels: +- Statistics +summary: Collect statistics on user identities +--- + +Description +=========== + +This module measures the number of connected clients per identity. + +It requires mod\_cache\_c2s\_caps to be loaded on each VirtualHost to +provide the data identifying a user’s client.
--- a/mod_measure_client_presence/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on user presences ---- - -Description -=========== - -This module measures the number of resources of a certain show (available, -away, etc.) currently connected, and reports using Prosody 0.10 APIs
--- a/mod_measure_cpu/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -labels: -- Statistics -summary: Measure CPU usage -... - -Description -=========== - -This module measures CPU usage and reports using Prosody 0.10 APIs
--- a/mod_measure_malloc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- Statistics -summary: 'Report malloc() stats' ---- - -Description -=========== - -This module collects stats from `util.pposix.meminfo` usage and reports using Prosody 0.12 APIs -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_measure_malloc/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,11 @@ +--- +labels: +- Statistics +summary: 'Report malloc() stats' +--- + +Description +=========== + +This module collects stats from `util.pposix.meminfo` usage and reports using Prosody 0.12 APIs +
--- a/mod_measure_memory/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -labels: -- Statistics -summary: Measure memory usage ---- - -Description -=========== - -This module measures memory usage and reports using Prosody 0.10 APIs
--- a/mod_measure_message_e2ee/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on message encryption ---- - -Description -=========== - -This module measures the rate at which text messages are exchanged, and their -encryption status. - -::: {.alert .alert-warning} -This module can leak information about your users’ behaviour to anyone who can -access these statistics, so avoid enabling it on a server with few users. -:::
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_measure_message_e2ee/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,16 @@ +--- +labels: +- Statistics +summary: Collect statistics on message encryption +--- + +Description +=========== + +This module measures the rate at which text messages are exchanged, and their +encryption status. + +::: {.alert .alert-warning} +This module can leak information about your users’ behaviour to anyone who can +access these statistics, so avoid enabling it on a server with few users. +:::
--- a/mod_measure_message_length/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -Simple module that collects statistics on message length in bytes, word count and line count.
--- a/mod_measure_muc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on Grout Chat -... - -Description -=========== - -This module collects statistics from group chat component. - -It collects current count of hidden, persistent, archive-enabled, password -protected rooms. The current count of room is also exposed (hidden+public). - - -Configuration -============= - -mod\_measure\_muc must be load on MUC components (not globally): - -```lua -Component "conference.example.com" "muc" - modules_enabled = { - "measure_muc"; - } -``` - -See also the documentation of Prosody’s [MUC module](https://prosody.im/doc/modules/mod_muc). - -Compatibility -============= - - ------- ------------- - trunk Works - 0.11 Works - 0.10 Unknown - 0.9 Does not work - ------- -------------
--- a/mod_measure_process/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ ---- -labels: -- Statistics -summary: Measure process resource use metrics (cpu, memory, file descriptors) ---- - -Description -=========== - -This module exposes process resource use metrics similar to those exposed by -default when using a Prometheus client library. Specifically, the following -metrics are exposed: - -- CPU use -- Resident set and virtual memory size -- Number of open file descriptors and their limit - -This module uses the new OpenMetrics API and thus requires a recent version -of Prosody trunk (0.12+). - -Compatibility -============= - - ------- ------------- - trunk Works - 0.11 Does not work - ------- -------------
--- a/mod_measure_registration/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on user registration ---- - -Description -=========== - -This module measures the rate at which users register on the server (through -providers like `mod_register_ibr` or `mod_register_web`).
--- a/mod_measure_stanza_counts/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -- Statistics -summary: Collect statistics on number of stanzas processed ---- - -Description -=========== - -This module measures the number of stanzas handled and reports using -Prosody 0.12+ APIs
--- a/mod_measure_storage/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- Statistics -summary: Measure storage API operations ---- - -Introduction -============ - -This module collects statistics from storage operations. - -Configuration -============= - -Enable tagged metrics, whatever that is: - -``` {.lua} -measure_storage_tagged_metric = true -``` - -Compatibility -============= - -Requires 0.10
--- a/mod_message_logging/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Log/archive all user messages' -... - -Introduction -============ - -Often service administrators need to log their users' messages for -reasons such as auditing and compliance. This module simply logs user -messages to simple text files, which can be easily searched, archived or -removed on a regular basis. - -Usage -===== - -Simply load the module and it will start logging. Reloading Prosody -(e.g. with a SIGHUP) will cause it to close any open logs (and re-open -them if necessary). - -Configuration -============= - - Option name Description - ----------------------- ----------------------------------------------------------------------------------------------------------------------- - message\_logging\_dir The directory to save message logs in. Default is to create a 'message\_logs' subdirectory inside the data directory. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_migrate/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ ---- -summary: prosodyctl cross storage driver migration tool -... - -Introduction -============ - -This module adds a command to `prosodyctl` for copying data between -storage drivers. - -Usage -===== - - prosodyctl mod_migrate example.com <source-store>[-<store-type>] <target-driver> [users]* - -`<source-store>` would be e.g. `accounts` or `private`. To migrate -archives, the optional suffix `<store-type>` would be set to `archive`, -so e.g. `archive2-archive` or `muc_log-archive`. Multiple stores can be -given if separated by commas. - -`<target-driver>` is the storage driver to copy data to, sans the -`mod_storage_` prefix. - -`mod_migrate` tries to request a list of users from `usermanager`, but -this does not always work. If so, you can supply usernames as arguments -after the target driver. - -The process is something like this: - -1. Decide on the future configuration and add for example SQL - connection details to your prosody config, but don't change the - `store` option yet. -2. With Prosody shut down, run - `prosodyctl mod_migrate example.com accounts sql` -3. Repeat for each store, substituting 'accounts'. E.g. vcards, - private... -4. Change the [`storage` configuration](https://prosody.im/doc/storage) - to use the new driver. -5. Start prosody again. - -Examples -======== - -``` sh -prosodyctl mod_migrate example.com accounts,roster,private,vcard sql -``` - -Compatibility -============= - -Should work with 0.8 and later.
--- a/mod_migrate_http_upload/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -summary: mod_http_upload to mod_http_file_share migrator -labels: -- Stage-Alpha ---- - - -This is a migration script for converting records of [mod_http_upload] -into the format used by the new [mod_http_file_share][doc:modules:mod_http_file_share] -which will be available with Prosody 0.12 (currently in trunk). - -# Usage - -If your main `VirtualHost` is called "example.com" and your HTTP upload -`Component` is called "upload.example.com", then this command would -convert records of existing uploads via [mod_http_upload] to -[mod_http_file_share][doc:modules:mod_http_file_share]: - -```bash -sudo prosodyctl mod_migrate_http_upload upload.example.com example.com -``` - -In order to preserve URLs you will need to configure the -[path][doc:http#path_configuration] to be the same as mod_http_upload: - -```lua -Component "upload.example.com" "http_file_share" -http_paths = { - file_share = "/upload" -} -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_migrate_http_upload/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,31 @@ +--- +summary: mod_http_upload to mod_http_file_share migrator +labels: +- Stage-Alpha +--- + + +This is a migration script for converting records of [mod_http_upload] +into the format used by the new [mod_http_file_share][doc:modules:mod_http_file_share] +which will be available with Prosody 0.12 (currently in trunk). + +# Usage + +If your main `VirtualHost` is called "example.com" and your HTTP upload +`Component` is called "upload.example.com", then this command would +convert records of existing uploads via [mod_http_upload] to +[mod_http_file_share][doc:modules:mod_http_file_share]: + +```bash +sudo prosodyctl mod_migrate_http_upload upload.example.com example.com +``` + +In order to preserve URLs you will need to configure the +[path][doc:http#path_configuration] to be the same as mod_http_upload: + +```lua +Component "upload.example.com" "http_file_share" +http_paths = { + file_share = "/upload" +} +```
--- a/mod_minimix/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -# Account based MUC joining - -Normally when joining a MUC groupchat, it is each individual client that -joins. This means their presence in the group is tied to the session, -which can be short-lived or unstable, especially in the case of mobile -clients. - -This has a few problems. For one, for every message to the groupchat, a -copy is sent to each joined client. This means that at the account -level, each message would pass by once for each client that is joined, -making it difficult to archive these messages in the users personal -archive. - -A potentially better approach would be that the user account itself is -the entity that joins the groupchat. Since the account is an entity that -lives in the server itself, and the server tends to be online on a good -connection most of the time, this may improve the experience and -simplify some problems. - -This is one of the essential changes in the MIX architecture, which is -being designed to replace MUC. - -`mod_minimix` is an experiment meant to determine if things can be -improved without replacing the entire MUC standard. It works by -pretending to each client that nothing is different and that they are -joining MUCs directly, but behind the scenes, it arranges it such that -only the account itself joins each groupchat. Which sessions have joined -which groups are kept track of. Groupchat messages are then forked to -those sessions, similar to how normal chat messages work. - -## Known issues - -- You can never leave. -- You will never see anyone leave. -- Being kicked is not handled. - -## Unknown issues - -- Probably many. - -## TODO - -- Integrate with bookmarks -- tracking outgoing presence -- leaving rooms -- nickname management -- bookmark sync - -# Compatibility - -Briefly tested with Prosody trunk (as of this writing).
--- a/mod_motd_sequential/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Sequential MOTD messages -... - -Introduction -============ - -mod\_motd\_sequential is a variant of -[mod\_motd](https://prosody.im/doc/modules/mod_motd) that lets you -specify a sequence of MOTD messages instead of a single static one. Each -message is only sent once and the module keeps track of who as seen -which message. - -Configuration -============= - -``` lua -modules_enabled = { - -- other modules - "motd_sequential"; -} -motd_sequential_messages = { - "Hello and welcome to our service!", -- First login - "Lorem ipsum dolor sit amet", -- Second time they login - -- Add more messages here. -} -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_motd_sequential/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,29 @@ +--- +labels: +- 'Stage-Beta' +summary: Sequential MOTD messages +... + +Introduction +============ + +mod\_motd\_sequential is a variant of +[mod\_motd](https://prosody.im/doc/modules/mod_motd) that lets you +specify a sequence of MOTD messages instead of a single static one. Each +message is only sent once and the module keeps track of who as seen +which message. + +Configuration +============= + +``` lua +modules_enabled = { + -- other modules + "motd_sequential"; +} +motd_sequential_messages = { + "Hello and welcome to our service!", -- First login + "Lorem ipsum dolor sit amet", -- Second time they login + -- Add more messages here. +} +```
--- a/mod_muc_adhoc_bots/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: Install adhoc command bots in MUCs ---- - -# Introduction - -This module allows you to "install" bots on a MUC service (via config for -now, via adhoc command and on just one MUC to follow). All the adhoc commands -defined on the bot become adhoc commands on the service's MUCs, and the bots -can send XEP-0356 messages to the MUC to send messages as any participant. - -# Configuration - -List all bots to install. You must specify full JID. - - adhoc_bots = { "some@bot.example.com/bot" } - -And enable the module on the MUC service as usual - - modules_enabled = { "muc_adhoc_bots" }
--- a/mod_muc_archive/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Log MUC messages to disk -... - -# Introduction - -This module logs the conversations of chatrooms running on the server to Prosody's data store. - -This is a fork of [mod_muc_log](https://modules.prosody.im/mod_muc_log.html) which uses the newer storage API. -This allows you to also log messages to a SQL backend. - -## Changes between mod_muc_archive and mod_muc_log: - -- Use newer module storage API so that you can also store in SQL -- Adhere to config option `muc_log_all_rooms` (also used by mod_muc_mam) -- Add affiliation information in the logged stanza -- Remove code that set (and then removed) an "alreadyJoined" dummy element - -NOTE: The changes are unlikely to be entirely backwards compatible because the stanza -being logged is no longer wrapped with `<stanza time=...>`. - -Details -======= - -mod\_muc\_archive must be loaded individually for the components that need it. - -Assuming you have a MUC component already running on -conference.example.org then you can add muc\_archive to it like so: - - Component "conference.example.org" "muc" - modules_enabled = { - "muc_archive"; - } - - -Compatibility -============= - - ------ ----- - 0.11 Works - ------ -----
--- a/mod_muc_auto_member/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: "Automatically register new MUC participants as members" -... - -# Introduction - -This module automatically makes anybody who joins a MUC become a registered -member. This can be useful for certain use cases. - -Note: there is no automatic cleanup of members. If you enable this on a server -with busy public channels, your member list will perpetually increase in size. - -Also, there is currently no per-room option for this behaviour. That may be -added in the future, along with membership expiry. - -# Configuration - -There is currently no configuration for this module. The module should be -enabled on your MUC component, i.e. in the modules_enabled option under your -Component: - -``` {.lua} -Component "conference.example.com" "muc" - modules_enabled = { - "muc_auto_member"; - } -``` - -# Compatibility - -0.12 and later.
--- a/mod_muc_badge/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ ---- -depends: -- 'mod\_http' -- 'mod\_muc' -provides: -- http -title: 'mod\_muc\_badge' ---- - -# Introduction - -This module generates a badge for MUC rooms at a HTTP URL like -`https://conference.example.com:5281/muc_badge/room@conference.example.org` -containing the number of occupants. - -Inspiration -: <https://opkode.com/blog/xmpp-chat-badge/> - -# Configuration - - Option Type Default - ------------------ -------- -------------------------- - `badge_count` string `"%d online"` - `badge_template` string A SVG image (see source) - -The template must be valid XML. If it contains `{label}` then this is -replaced by `badge_label`, similarly, `{count}` is substituted by -`badge_count` with `%d` changed to the number of occupants. - -Details of the HTTP URL is determined by [standard Prosody HTTP server -configuration][doc:http]. - -# Example - -```lua -Component "conference.example.com" "muc" -modules_enabled = { - "muc_badge" -} -```
--- a/mod_muc_ban_ip/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Ban users from chatrooms by their IP address -... - -Introduction -============ - -One frequent complaint about XMPP chatrooms (MUCs) compared to IRC is -the inability for a room admin to ban a user based on their IP address. -This is because an XMPP user is not identified on the network by their -IP address, only their JID. - -This means that it is possible to create a new account (usually quite -easily), and rejoin the room that you were banned from. - -This module allows the **user's** server to enforce bans by IP address, -which is very desirable for server admins who want to prevent their -server being used for spamming and abusive behaviour. - -Details -======= - -An important point to note is that this module enforces the IP ban on -the banned user's server, not on the MUC server. This means that: - -- The user's server MUST have this module loaded, however - -- The module works even when the MUC is on a different server to the - user -- The MUC server does not need this module (it only needs to support - the [standard ban - protocol](http://xmpp.org/extensions/xep-0045.html#ban)) -- The module works for effectively banning [anonymous - users](http://prosody.im/doc/anonymous_logins) - -Also note that IP bans are not saved permanently, and are reset upon a -server restart. - -Configuration -============= - -There is no extra configuration for this module except for loading it. -Remember... do not load it on the MUC host, simply add it to your global -`modules_enabled` list, or under a specific host like: - -``` lua -VirtualHost "anon.example.com" - authentication = "anonymous" - modules_enabled = { "muc_ban_ip" } -``` - -Compatibility -============= - - ----- -------------- - 0.9 Works - 0.8 Doesn't work - ----- --------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_ban_ip/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,59 @@ +--- +labels: +- 'Stage-Alpha' +summary: Ban users from chatrooms by their IP address +... + +Introduction +============ + +One frequent complaint about XMPP chatrooms (MUCs) compared to IRC is +the inability for a room admin to ban a user based on their IP address. +This is because an XMPP user is not identified on the network by their +IP address, only their JID. + +This means that it is possible to create a new account (usually quite +easily), and rejoin the room that you were banned from. + +This module allows the **user's** server to enforce bans by IP address, +which is very desirable for server admins who want to prevent their +server being used for spamming and abusive behaviour. + +Details +======= + +An important point to note is that this module enforces the IP ban on +the banned user's server, not on the MUC server. This means that: + +- The user's server MUST have this module loaded, however - +- The module works even when the MUC is on a different server to the + user +- The MUC server does not need this module (it only needs to support + the [standard ban + protocol](http://xmpp.org/extensions/xep-0045.html#ban)) +- The module works for effectively banning [anonymous + users](http://prosody.im/doc/anonymous_logins) + +Also note that IP bans are not saved permanently, and are reset upon a +server restart. + +Configuration +============= + +There is no extra configuration for this module except for loading it. +Remember... do not load it on the MUC host, simply add it to your global +`modules_enabled` list, or under a specific host like: + +``` lua +VirtualHost "anon.example.com" + authentication = "anonymous" + modules_enabled = { "muc_ban_ip" } +``` + +Compatibility +============= + + ----- -------------- + 0.9 Works + 0.8 Doesn't work + ----- --------------
--- a/mod_muc_batched_probe/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -# mod_muc_batched_probe - -This module allows you to probe the presences of multiple MUC occupants or members. - -XEP-0045 makes provision for MUC presence probes, which allows an entity to -probe for the presence information of a MUC occupant (or offline member). - -See here: https://xmpp.org/extensions/xep-0045.html#bizrules-presence - -This module creates the possibility to probe with a single IQ stanza the -presence information of multiple JIDs, instead of having to send out a presence -probe stanza per JID. - -The IQ stanza needs to look as follows: - -``` - <iq from="hag66@shakespeare.lit/pda" - id="zb8q41f4" - to="chat.shakespeare.lit" - type="get"> - - <query xmlns="http://jabber.org/protocol/muc#user"> - <item jid="hecate@shakespeare.lit"/> - <item jid="crone1@shakespeare.lit"/> - <item jid="wiccarocks@shakespeare.lit"/> - <item jid="hag66@shakespeare.lit"/> - </query> - </iq> -``` - - - -## Configuration - -Under your MUC component, add `muc_batched_probe` to `modules_enabled` - -``` - Component "conference.example.org" "muc" - modules_enabled = { - "muc_batched_probe"; - } -``` - - -## Client Support - -Converse.js has a plugin which supports this feature. - -https://www.npmjs.com/package/@converse-plugins/muc-presence-probe
--- a/mod_muc_block_pm/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -summary: Prevent MUC participants from sending PMs ---- - -# Introduction - -This module prevents *participants* from sending private messages to -anyone except *moderators*. - -# Configuration - -The module doesn't have any options, just load it onto a MUC component. - -``` lua -Component "muc" -modules_enabled = { - "muc_block_pm"; -} -``` - -# Compatibility - - Branch State - -------- ----------------- - 0.11 Will **not** work - 0.12 Should work
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_block_pm/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,26 @@ +--- +summary: Prevent MUC participants from sending PMs +--- + +# Introduction + +This module prevents *participants* from sending private messages to +anyone except *moderators*. + +# Configuration + +The module doesn't have any options, just load it onto a MUC component. + +``` lua +Component "muc" +modules_enabled = { + "muc_block_pm"; +} +``` + +# Compatibility + + Branch State + -------- ----------------- + 0.11 Will **not** work + 0.12 Should work
--- a/mod_muc_bot/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ ---- -summary: Module for improving the life of bot authors ---- - -This module makes it easier to write MUC bots by removing the -requirement that the bot be online and joined to the room. - -All the bot needs to do is send a message and this module handles the -rest. - -# Configuration - -Example configuration in Prosody: - -```lua -Component "muc.example.com" "muc" - -modules_enabled = { - "muc_bot", -} -known_bots = { "bot@example.com" } -bots_get_messages = false -ignore_bot_errors = true -``` - -# Sending messages - -Simply send a stanza like this from your bot: - -```xml -<message type="groupchat" to="channel@muc.example.com"> - <body>Beep boop, I'm a bot!</body> - <nick xmlns="http://jabber.org/protocol/nick">Botty</nick> -</message> -``` - -## Use with mod_rest - -Using [mod_rest] to interact with MUC suffers from the same need to join -with an online resource, so this module helps with that as well! - -```bash -curl https://xmpp.example.com/rest/message/groupchat/room@muc.example.com \ - -d body="beep boop" \ - -d nick="Botty" -``` - -# Compatibility - -Works with Prosody 0.12 or later.
--- a/mod_muc_cloud_notify/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,71 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-XXX: Cloud push notifications for MUC' ---- - -# Introduction - -This is an experimental fork of [mod_cloud_notify](https://modules.prosody.im/mod_cloud_notify.html) -which allows a [XEP-0357 Push Notifications App Servers](https://xmpp.org/extensions/xep-0357.html#general-architecture) -to be registered against a MUC domain (normally they're only registered against -your own chat server's domain). - -The goal here is to also enable push notifications also for MUCs. - -In contrast to mod_cloud_notify, this module does NOT integrate with -mod_smacks, because a MUC can't access a remote user's XEP-0198 queue. - -Configuration -============= - - Option Default Description - ------------------------------------ ----------------- ------------------------------------------------------------------------------------------------------------------- - `push_notification_with_body` `false` Whether or not to send the message body to remote pubsub node. - `push_notification_with_sender` `false` Whether or not to send the message sender to remote pubsub node. - `push_max_errors` `16` How much persistent push errors are tolerated before notifications for the identifier in question are disabled - `push_notification_important_body` `New Message!` The body text to use when the stanza is important (see above), no message body is sent if this is empty - `push_max_devices` `5` The number of allowed devices per user (the oldest devices are automatically removed if this threshold is reached) - -There are privacy implications for enabling these options because -plaintext content and metadata will be shared with centralized servers -(the pubsub node) run by arbitrary app developers. - -## To test this module: - -The [Converse](http://conversejs.org/) client has support for registering push -"app servers" against a MUC. - -You specify app servers with the [push_app_servers](https://conversejs.org/docs/html/configuration.html#push-app-servers) -config setting. - -And then you need to set [enable_muc_push](https://conversejs.org/docs/html/configuration.html#enable-muc-push) -to `true` so that these app servers are also registered against MUC domains. - -Additionally you need to set [auto_register_muc_nickname](https://conversejs.org/docs/html/configuration.html#auto-register-muc-nickname) -to true. - -Then, when you enter a MUC, Converse will try to automatically register your nickname -on that MUC. - -Note: Converse currently doesn't let you register separate app servers for -a MUC domain. The same app servers are registered for the MUC domain and your -own domain. - -## To be done: - -We currently don't handle "ghost connections", users who are currently offline -but the XMPP server is not yet aware of this and shows considers them online in -the MUC. - -Prosody already checks for error bounces from undelivered groupchat messages -and then kicks the particular user from the room. - -So these ghost connection users eventually get kicked from the room. - -We now need a module that fires an event when a groupchat messages can't be -delivered to an occupant. The module can look up the undelivered message in MAM -and include it in the event. - -In mod_muc_cloud_notify we can then listen for this event and send out a push -notification.
--- a/mod_muc_config_restrict/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Restrict MUC configuration options to server admins -... - -Introduction -============ - -Sometimes, especially on public services, you may want to allow people -to create their own rooms, but prevent some options from being modified -by normal users. - -For example, using this module you can prevent users from making rooms -persistent, or making rooms publicly visible. - -Details -======= - -You need to supply a list of options that will be restricted to admins. -Available options can vary, but the following table lists Prosody's -built-in options (as defined in XEP-0045): - - Name Description - --------------------------------- ------------------------------------------- - muc\#roomconfig\_roomname The title/name of the room - muc\#roomconfig\_roomdesc The description of the room - muc\#roomconfig\_persistentroom Whether the room should remain when empty - muc\#roomconfig\_publicroom Whether the room is publicly visible - muc\#roomconfig\_changesubject Whether occupants can change the subject - muc\#roomconfig\_whois Control who can see occupant's real JIDs - muc\#roomconfig\_roomsecret The room password - muc\#roomconfig\_moderatedroom Whether the room is moderated - muc\#roomconfig\_membersonly Whether the room is members-only - muc\#roomconfig\_historylength The length of the room history - -Some plugins may add other options to the room config (in Prosody -0.10+), for which you will need to consult their documentation for the -full option name. - -Configuration -============= - -Enable the plugin on a MUC host (do not put it in your global -modules\_enabled list): - -``` {.lua} -Component "conference.example.com" "muc" -modules_enabled = { "muc_config_restrict" } -muc_config_restricted = { - "muc#roomconfig_persistentroom"; -- Prevent non-admins from changing a room's persistence setting - "muc#roomconfig_membersonly"; -- Prevent non-admins from changing whether rooms are members-only -} -``` - -Compatibility -============= - - ------- -------------- - trunk Doesn't work (uses is_admin) - 0.12 Works? - ------- --------------
--- a/mod_muc_defaults/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -# mod_muc_defaults - -Creates MUCs with default configuration settings upon Prosody startup. - -## Configuration - -Under your MUC component, add a `default_mucs` option with the relevant settings. - -``` -Component "conference.example.org" "muc" - modules_enabled = { - "muc_defaults"; - } - - default_mucs = { - { - jid_node = "trollbox", - affiliations = { - admin = { "admin@example.org", "superuser@example.org" }, - owner = { "owner@example.org" }, - visitors = { "visitor@example.org" } - }, - config = { - name = "General Chat", - description = "Public chatroom with no particular topic", - allow_member_invites = false, - change_subject = false, - history_length = 40, - lang = "en", - logging = true, - members_only = false, - moderated = false, - persistent = true, - public = true, - public_jids = true - } - } - }; -```
--- a/mod_muc_eventsource/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ ---- -labels: -- Stage-Beta -summary: Subscribe to MUC rooms using the HTML5 EventSource API -... - -Introduction ------------- - -This module and its docs shamelessly forked from mod_pubsub_eventsource. - -[Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) -is a simple HTTP/line-based protocol supported in HTML5, making it easy -to receive a stream of "events" in realtime using the Javascript -[EventSource -API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). - -EventSource is supported in [most modern -browsers](http://caniuse.com/#feat=eventsource), and for the remainder -there are 'polyfill' compatibility layers such as -[EventSource.js](https://github.com/remy/polyfills/blob/master/EventSource.js) -and [jquery.eventsource](https://github.com/rwldrn/jquery.eventsource). - -Details -------- - -Subscribing to a node from Javascript is easy: - - var source = new EventSource('http://muc.example.org:5280/eventsource/myroom'); - source.onmessage = function (event) { - console.log(event.data); // Do whatever you want with the data here - }; - -### Access control - -Be warned that this module currently performs no access control. It will expose -the messages of ALL rooms on the host it is loaded on. This may be changed in -future revisions. - -### Cross-domain issues - -The same cross-domain restrictions apply to EventSource that apply to -BOSH, and support for CORS is not clearly standardized yet. You may want -to proxy connections through your web server for this reason. See [BOSH: -Cross-domain -issues](https://prosody.im/doc/setting_up_bosh#proxying_requests) for -more information. - -Configuration -------------- - -There is no special configuration for this module. Simply load it onto a -MUC component like so: - - Component "muc.example.org" "muc" - modules_enabled = { "muc_eventsource" } - -As it uses HTTP to serve the event streams, you can use Prosody's -standard [HTTP configuration options](https://prosody.im/doc/http) to -control how/where the streams are served. - -**Note about URLs:** It is important to get the event streams from the -correct hostname (that of the MUC host). An example stream URL is -`http://muc.example.org:5280/eventsource/myroom`. If you need to -access the streams using another hostname (e.g. `example.org`) you can -use the `http_host` option under the Component, e.g. -`http_host = "example.org"`. For more information see the ['Virtual -Hosts'](https://prosody.im/doc/http#virtual_hosts) section of our HTTP -documentation. - -Compatibility -------------- - - ------- -------------- - 0.10 ? - 0.9 ? - 0.8 Doesn't work - Trunk Works - ------- --------------
--- a/mod_muc_gc10/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -# Groupchat 1.0 usage statistics gathering - -Groupchat 1.0 was probably the protocol that predated -[XEP-0045: Multi-User Chat] and there is still some compatibility that -lives on, in the XEP and in implementations. - -This module tries to detect clients still using the GC 1.0 protocol and -what software they run, to determine if support can be removed. - -Since joins in the GC 1.0 protocol are highly ambiguous, some hits -reported will be because of desynchronized MUC clients - -# Compatibility - -Should work with Prosody 0.10.x and earlier. - -It will not work with current trunk, since the MUC code has had major -changes.
--- a/mod_muc_hats_adhoc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -summary: Ad-hoc commands for managing MUC hats ---- - -# Introduction - -This module provides an internal API (i.e. to other modules) to manage -'hats' for users in MUC rooms. - -Hats (first defined in [XEP-0317], currently deferred) are additional identifiers -that can be attached to users in a group chat. For example in an educational -context, you may have a 'Teacher' hat that allows students to identify their -teachers. - -Hats consist of a machine-readable unique identifier (a URI), and optionally -a human-readable label. - -This module provides ad-hoc commands for MUC service admins to add/remove hats -to/from users in MUC rooms. It depends (automatically) on mod_muc_hats_api. - -## Configuration - -``` -Component "conference.example.com" "muc" - modules_enabled = { "muc_hats_adhoc" } -``` - -## Usage - -To successfully use the module you will need to use an XMPP client that is -capable of sending commands to a specific host (e.g. via the service discovery -browser in Gajim, Psi/Psi+ and other clients), and you'll find the commands -on the MUC host. - -Also note that the display of hats in clients is currently non-existent, but -will hopefully improve after [XEP-0317] is resurrected or replaced. -
--- a/mod_muc_hats_api/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ ---- -summary: API for managing MUC hats ---- - -# Introduction - -This module provides an internal API (i.e. to other modules) to manage -'hats' for users in MUC rooms. - -Hats (first defined in [XEP-0317], currently deferred) are additional identifiers -that can be attached to users in a group chat. For example in an educational -context, you may have a 'Teacher' hat that allows students to identify their -teachers. - -Hats consist of a machine-readable unique identifier (a URI), and optionally -a human-readable label. - -[XEP-0317] suggests a protocol for users to manage their own hats, but though the -API in this module allows for both user-managed and system-managed hats, there is -currently no protocol implemented for users to manage their own hats, which is -rarely desired in real-world implementations. - -The rest of this documentation is designed for developers who use this module. - -## Data model - -### User - -``` -{ - "hats": { - "urn:uuid:164c41a2-7461-4cff-bdae-3f93078a6607": { - "active": false - }, - "http://example.com/hats/foo": { - "active": true, - "required": true, - "title": "Awesome" - } -} -``` - -| Field | Type | Description | -|-------|--------|--------------------------------------------------------------| -| hats | object | An object where mapping hat ids (key) to attachments (value) | - -Hat IDs must be a URI that uniquely identifies the hat. - -### Attachment - -``` -{ - "active": true, - "required": true, - "title": "My Awesome Hat" -} -``` - -| Field | Type | Description | -|----------|---------|-------------------------------------------------------------| -| active | boolean | If true, indicates the user is currently displaying the hat | -| required | boolean | If true, indicates the user is not able to remove the hat | -| title | string | A human-readable display name or label for the hat | - -All fields are optional, omitted boolean values are equivalent to false. - -## API - -All methods return 'nil, err' on failure as standard throughout the Prosody codebase. - -Example of using this module from another module: - -``` -local muc_hats = module:depends("muc_hats_api"); - -muc_hats.add_user_hat("user@localhost", "room@conference.localhost", "urn:uuid:164c41a2-7461-4cff-bdae-3f93078a6607", { active = true }); -``` - -Note that the module only works when loaded on a MUC host, which generally means any -module that uses it must also be loaded on the MUC host that it is managing. - -### add_user_hat - -`add_user_hat(user_jid, room_jid, hat_id, attachment)` - -Adds the identified hat to a user's... wardrobe? The user must already -have an affiliation with the room (i.e. member, admin or owner). - -If `attachment` is omitted, it defaults to `{}`. - -#### Error cases - -item-not-found -: Supplied room JID was not found on the current host - -item-not-found -: Supplied user JID was not affiliated with the room - -### remove_user_hat - -`remove_user_hat(user_jid, room_jid, hat_id)` - -If the identified hat is currently available to the user, it is removed. - -#### Error cases - -item-not-found -: Supplied room JID was not found on the current host - -item-not-found -: Supplied user JID was not affiliated with the room - -### set_user_hats - -`set_user_hats(user_jid, room_jid, hats)` - -Ensures the listed hats are the hats available to a user, automatically -adding/removing as necessary. - -The `hats` parameter should be an object mapping hat ids (keys) to attachment -objects (values). - -#### Error cases - -item-not-found -: Supplied room JID was not found on the current host - -item-not-found -: Supplied user JID was not affiliated with the room
--- a/mod_muc_hide_media/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -# Introduction - -This module adds a room configuration option to hide inline media from MUCs and -display them as links instead. - -This can be useful in public channels where content posted by users should not -be shown by default. - -**Note:** You could consider the more useful [mod_muc_restrict_media] instead, -which allows affiliated users (e.g. members, admins, owners) to still send -inline media. - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_hide_media"; -} -``` - -## Settings - -A default setting can be provided in the config file: - -``` {.lua} -muc_room_default_hide_media = true -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_hide_media/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,30 @@ +# Introduction + +This module adds a room configuration option to hide inline media from MUCs and +display them as links instead. + +This can be useful in public channels where content posted by users should not +be shown by default. + +**Note:** You could consider the more useful [mod_muc_restrict_media] instead, +which allows affiliated users (e.g. members, admins, owners) to still send +inline media. + +# Configuring + +## Enabling + +``` {.lua} +Component "rooms.example.net" "muc" +modules_enabled = { + "muc_hide_media"; +} +``` + +## Settings + +A default setting can be provided in the config file: + +``` {.lua} +muc_room_default_hide_media = true +```
--- a/mod_muc_http_defaults/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,191 +0,0 @@ ---- -summary: Seed MUC configuration from JSON REST API ---- - -# Introduction - -This module fetches configuration for MUC rooms from an API when rooms -are created. - -# Requirements - -Should work with Prosody 0.11. - -# Configuration - -`muc_create_api_url` -: URL template for the API endpoint to get settings. `{room.jid}` is - replaced by the address of the room in question. - -`muc_create_api_auth` -: The value of the Authorization header to authenticate against the - API. E.g. `"Bearer /rXU4tkQTYQMgdHfMLH6"`{.lua} - -In the URL template variable, the room JID is available as `{room.jid}`, -which would be turned into `room@muc.host`. To only get the room -localpart, `{room.jid|jid_node}` can be used, and `{room.jid|jid_host}` -splits out the `muc.host` part. - -## Example - -``` {.lua} -Component "channels.example.net" "muc" -modules_enabled = { "muc_http_defaults" } -muc_create_api_url = "https://api.example.net/muc/config?jid={room.jid}" -``` - -# API - -A RESTful JSON API is used. Any error causes the room to be destroyed. - -The returned JSON consists of two main parts, the room configuration and -the affiliations (member list). - -## Room Configuration - -The top level `config` field contains a map of properties corresponding -to the fields in the room configuration dialog, named similarly to the -[room configuration default][doc:modules:mod_muc#room-configuration-defaults] in -Prosodys config file. - -| Property | Type | Description | -|------------------------|---------|---------------------------------------------------------------------------| -| `name` | string | Name of the chat | -| `description` | string | Longer description of the chat | -| `language` | string | Language code | -| `persistent` | boolean | Whether the room should keep existing if it becomes empty | -| `public` | boolean | `true` to include in public listing | -| `members_only` | boolean | Membership or open | -| `allow_member_invites` | boolean | If members can invite others into members-only rooms | -| `public_jids` | boolean | If everyone or only moderators should see real identities | -| `subject` | string | In-room subject or topic message | -| `changesubject` | boolean | If `true` then everyone can change the subject, otherwise only moderators | -| `historylength` | integer | Number of messages to keep in memory (legacy method) | -| `moderated` | boolean | New participants start without voice privileges if set to `true` | -| `archiving` | boolean | Whether [archiving][doc:modules:mod_muc_mam] is enabled | - -## Affiliations - -The list of members go in `affiliations` which is either an object -mapping addresses to affiliations (e.g. `{"user@host":"admin"}`{.json}), -or it can be an array of address, affiliation and optionally a reserved -nickname (e.g. -`[{"jid":"user@host","affiliation":"member","nick":"joe"}]`{.json}). - -## Schema - -Here's a JSON Schema in YAML format describing the expected JSON -response data: - -``` {.yaml} ---- -type: object -properties: - config: - type: object - properties: - name: - type: string - description: - type: string - language: - type: string - persistent: - type: boolean - public: - type: boolean - members_only: - type: boolean - allow_member_invites: - type: boolean - public_jids: - type: boolean - subject: - type: string - changesubject: - type: boolean - historylength: - type: integer - moderated: - type: boolean - archiving: - type: boolean - affiliations: - oneOf: - - type: array - items: - type: object - required: - - jid - - affiliation - properties: - jid: - type: string - pattern: ^[^@/]+@[^/]+$ - affiliation: - $ref: '#/definitions/affiliation' - nick: - type: string - - type: object - additionalProperties: - $ref: '#/definitions/affiliation' -definitions: - affiliation: - type: string - enum: - - owner - - admin - - member - - none - - outcast -... -``` - -## Example - -A basic example with some config settings and a few affiliations: - -``` {.json} -GET /muc/config?jid=place@channels.example.net -Accept: application/json - -HTTP/1.1 200 OK -Content-Type: application/json - -{ - "affiliations" : [ - { - "affiliation" : "owner", - "jid" : "bosmang@example.net", - "nick" : "bosmang" - }, - { - "affiliation" : "admin", - "jid" : "xo@example.net", - "nick" : "xo" - }, - { - "affiliation" : "member", - "jid" : "john@example.net" - } - ], - "config" : { - "archiving" : true, - "description" : "This is the place", - "members_only" : true, - "moderated" : false, - "name" : "The Place", - "persistent" : true, - "public" : false, - "subject" : "Discussions regarding The Place" - } -} -``` - -To allow the creation without making any changes, letting whoever -created it be the owner, just return an empty JSON object: - - HTTP/1.1 200 OK - Content-Type: application/json - - {}
--- a/mod_muc_inject_mentions/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -# Introduction - -This module intercepts messages sent to a MUC, looks in the message's body if a user was mentioned and injects a mention type reference to that user implementing [XEP-0372](https://xmpp.org/extensions/xep-0372.html#usecase_mention) - -## Features - -1. Multiple mentions in the same message using affixes, including multiple mentions to the same user. - Examples: - `Hello nickname` - `@nickname hey!` - `nickname, hi :)` - `Are you sure @nickname?` - -2. Mentions are only injected if no mention was found in a message, avoiding this way, injecting mentions in messages sent from clients with mentions support. - -3. Configuration settings for customizing affixes and enabling/disabling the module for specific rooms. - - -# Configuring - -## Enabling - -```{.lua} - -Component "rooms.example.net" "muc" - -modules_enabled = { - "muc_inject_mentions"; -} - -``` - -## Settings - -Apart from just writing the nick of an occupant to trigger this module, -common affixes used when mentioning someone can be configured in Prosody's config file. -Recommended affixes: - -``` -muc_inject_mentions_prefixes = {"@"} -- Example: @bob hello! -muc_inject_mentions_suffixes = {":", ",", "!", ".", "?"} -- Example: bob! How are you doing? -``` - -This module can be enabled/disabled for specific rooms. -Only one of the following settings must be set. - -``` --- muc_inject_mentions_enabled_rooms = {"room@conferences.server.com"} --- muc_inject_mentions_disabled_rooms = {"room@conferences.server.com"} -``` - -If none of these is set, all rooms in the muc component will have mentions enabled. - - -By default, if a message contains at least one mention, -the module does not do anything, as it believes all mentions were already sent by the client. -In cases where it is desired the module to inspect the message and try to find extra mentions -that could be missing, the following setting can be added: - -``` -muc_inject_mentions_append_mentions = true -``` - - -Prefixes can be removed using: -``` -muc_inject_mentions_strip_out_prefixes = true -``` -Turning `Hey @someone` into `Hey someone`. -Currently, prefixes can only be removed from module added mentions. -If the client sends a mention type reference pointing to a nickname using a prefix (`Hey @someone`), the prefix will not be removed. - - -There are two lists where this module pulls the participants from. -1. Online participants -2. Participants with registered nicknames - -By default, the module will try to find mentions to online participants. -Using: -``` -muc_inject_mentions_reserved_nicks = true -``` -Will try to find mentions to participants with registered nicknames. -This is useful for setups where the nickname is reserved for all participants, -allowing the module to catch mentions to participants that might not be online at the moment of sending the message. - - -It is also possible to modify how this module detects mentions. -In short, the module will detect if a mention is actually a mention -if the nickname (with or without affixes) is between spaces, new lines, or at the beginning/end of the message. -This can be changed using: - -``` --- muc_inject_mentions_mention_delimiters = {" ", "", "\n", "\t"} -``` -Generally speaking and unless the use-case is very specific, there should be no need to modify the defaults of this setting. - -When triggering a mention must only happen if that mention includes a prefix, this can be configured with: -``` --- muc_inject_mentions_prefix_mandatory = true -``` - -By default, mentions use the bare jid of the participant as the URI attribute. -If the MUC jid of the participant (eg. room@chat.example.org/Romeo) is preferred, this can be set using: -``` --- muc_inject_mentions_use_real_jid = false -``` - - -# Example stanzas - -Alice sends the following message - -``` -<message id="af6ca" to="room@conference.localhost" type="groupchat"> - <body>@bob hey! Are you there?</body> -</message> -``` - -Then, the module detects `@bob` is a mention to `bob` and injects a mention type reference to him - -``` -<message from="room@conference.localhost/alice" id="af6ca" to="alice@localhost/ThinkPad" type="groupchat"> - <body>@bob hey! Are you there?</body> - <reference xmlns="urn:xmpp:reference:0" - begin="1" - end="3" - uri="xmpp:bob@localhost" - type="mention" - /> -</message> -```
--- a/mod_muc_intercom/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -This module allows sending a message to another MUC room. - - @other-room: hello - -The message will appear in the other room as as - - <first-room/You> hello
--- a/mod_muc_lang/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# Introduction - -This module adds support for advertising the language used in a room. - -# Configuring - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_lang"; -} -``` - -The room language is specified in a new field in the room configuration -dialog, accessible through compatible clients. - -Use [language codes](https://en.wikipedia.org/wiki/ISO_639) like `en`, -`fr`, `de` etc. - -# Compatibility - -Meant for use with Prosody 0.10.x - -Native support was [added in Prosody -trunk/0.11](https://hg.prosody.im/trunk/rev/9c90cd2fc4c3), so there is -no need for this module.
--- a/mod_muc_limits/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Impose rate-limits on a MUC' -... - -Introduction -============ - -This module allows you to control the maximum rate of 'events' in a MUC -room. This makes it useful to prevent room floods (whether malicious or -accidental). - -Details -======= - -This module limits the following events: - -- Room joins -- Nick changes -- Status changes -- Messages (including private messages) - -The limit is for the room as a whole, not individual occupants in the -room. Users with an affiliation (members, admins and owners) are not -limited. - -Configuration -============= - -Add the module to the MUC host (not the global modules\_enabled): - -```lua -Component "conference.example.com" "muc" - modules_enabled = { "muc_limits" } -``` - -You can define (globally or per-MUC component) the following options: - - Name Default value Description - --------------------------- --------------- ---------------------------------------------------------- - muc_event_rate 0.5 The maximum number of events per second. - muc_burst_factor 6 Allow temporary bursts of this multiple. - muc_max_nick_length 23 The maximum allowed length of user nicknames - muc_max_char_count 5664 The maximum allowed number of bytes in a message - muc_max_line_count 23 The maximum allowed number of lines in a message - muc_limit_base_cost 1 Base cost of sending a stanza - muc_line_count_multiplier 0.1 Additional cost of each newline in the body of a message - -For more understanding of how these values are used, see the algorithm -section below. - -Algorithm -========= - -A certain number of events are allowed per second, given by -muc\_event\_rate. An event rate of 1 allows one event per second, and -event rate of 3 allows three events per second, and 0.5 allows one event -every two seconds, and so on. - -Obviously MUC conversations are not exactly steady streams of events. -Sometimes multiple people will talk at once. This is handled by the -muc\_burst\_factor option. - -A burst factor of 2 will allow 2 times as many events at once, for 2 -seconds, before throttling will be triggered. A factor of 5, 5 times as -many events for 5 seconds. - -When the limit is reached, an error response will be generated telling -the user the MUC is overactive, and asking them to try again. - -Compatibility -============= - - ------- ------- - trunk Works - 0.11 Works - ------- -------
--- a/mod_muc_local_only/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -# Introduction - -This module allows you to make one or more MUCs as accessible to local users only. - -# Details - -Local users (anyone on the same server as the MUC) are granted automatic -membership when they first join the room. Users from other servers are -denied access (even if the room is otherwise configured to be open). - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_local_only"; -} -``` - -## Settings - -Specify a list of MUCs in your config like so: - -``` -muc_local_only = { "my-local-chat@conference.example.com" } -``` - -# Compatibility - -Requires Prosody 0.11.0 or later. - -# Future - -It would be good to add a room configuration option.
--- a/mod_muc_log/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Log chatroom messages to disk -... - -Introduction -============ - -This module logs the conversation of chatrooms running on the server to -Prosody's data store. To view them you will need a module such as -[mod\_muc\_log\_http](mod_muc_log_http.html). - -Details -======= - -mod\_muc\_log must be loaded individually for the components that need -it. Assuming you have a MUC component already running on -conference.example.org then you can add muc\_log to it like so: - - Component "conference.example.org" "muc" - modules_enabled = { - "muc_log"; - } - -Logging is not enabled by default. In 0.9+ logging can be enabled per -room in the room config form. - -To enable logging in older versions, or to enable logging by default for -all rooms, set - - muc_log_by_default = true -- Log all rooms by default - -Compatibility -============= - - ------ --------------- - 0.6 Works - 0.7 Works - 0.8 Works - 0.9 Works - 0.10 Works - 0.11 Does not work - ------ --------------- - -**Note** that per-room configuration only works in 0.9+.
--- a/mod_muc_log/mod_muc_log.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ --- Copyright (C) 2009 Thilo Cestonaro --- Copyright (C) 2009 Waqas Hussain --- Copyright (C) 2009-2013 Matthew Wild --- Copyright (C) 2013 Kim Alvefur --- Copyright (C) 2013 Marco Cirillo - -local hosts = prosody.hosts; -local tostring = tostring; -local split_jid = require "util.jid".split; -local datamanager = require"core.storagemanager".olddm; -local data_load, data_store = datamanager.load, datamanager.store; -local datastore = "muc_log"; -local muc_form_config_option = "muc#roomconfig_enablelogging" - -local log_by_default = module:get_option_boolean("muc_log_by_default", false); -local log_presences = module:get_option_boolean("muc_log_presences", true); - --- Module Definitions - -local function get_room_from_jid(jid) - local node, host = split_jid(jid); - local component = hosts[host]; - if component then - local muc = component.modules.muc - if muc and rawget(muc,"rooms") then - -- We're running 0.9.x or 0.10 (old MUC API) - return muc.rooms[jid]; - elseif muc and rawget(muc,"get_room_from_jid") then - -- We're running >0.10 (new MUC API) - return muc.get_room_from_jid(jid); - else - return - end - end -end - -local function logging_enabled(room) - local enabled = room._data.logging; - if enabled == nil then - return log_by_default; - end - return enabled; -end - -function log_if_needed(event) - local stanza = event.stanza; - - if (stanza.name == "presence") or - (stanza.name == "iq") or - (stanza.name == "message" and tostring(stanza.attr.type) == "groupchat") - then - local node, host = split_jid(stanza.attr.to); - if node and host then - local bare = node .. "@" .. host; - if get_room_from_jid(bare) then - local room = get_room_from_jid(bare) - - local today = os.date("!%y%m%d"); - local now = os.date("!%H:%M:%S"); - - local muc_to = nil - local muc_from = nil; - local already_joined = false; - - if room._data.hidden then -- do not log any data of private rooms - return; - end - if not logging_enabled(room) then -- do not log where logging is not enabled - return; - end - - if stanza.name == "presence" and stanza.attr.type == nil then - muc_from = stanza.attr.to; - if room._occupants and room._occupants[stanza.attr.to] then - already_joined = true; - stanza:tag("alreadyJoined"):text("true"); - end - elseif stanza.name == "iq" and stanza.attr.type == "set" then -- kick, to is the room, from is the admin, nick who is kicked is attr of iq->query->item - if stanza.tags[1] and stanza.tags[1].name == "query" then - local tmp = stanza.tags[1]; - if tmp.tags[1] ~= nil and tmp.tags[1].name == "item" and tmp.tags[1].attr.nick then - tmp = tmp.tags[1]; - for jid, nick in pairs(room._jid_nick) do - if nick == stanza.attr.to .. "/" .. tmp.attr.nick then - muc_to = nick; - break; - end - end - end - end - else - for jid, nick in pairs(room._jid_nick) do - if jid == stanza.attr.from then - muc_from = nick; - break; - end - end - end - - if (muc_from or muc_to) then - local data = data_load(node, host, datastore .. "/" .. today); - local realFrom = stanza.attr.from; - local realTo = stanza.attr.to; - - if data == nil then - data = {}; - end - - stanza.attr.from = muc_from; - stanza.attr.to = muc_to; - data[#data + 1] = "<stanza time=\"".. now .. "\">" .. tostring(stanza) .. "</stanza>\n"; - stanza.attr.from = realFrom; - stanza.attr.to = realTo; - if already_joined == true then - if stanza[#stanza].name == "alreadyJoined" then -- normaly the faked element should be the last, remove it when it is the last - stanza[#stanza] = nil; - else - for i = 1, #stanza, 1 do - if stanza[i].name == "alreadyJoined" then -- remove the faked element - stanza[i] = nil; - break; - end - end - end - end - datamanager.getpath(node, host, datastore, nil, true); -- create the datastore dir - data_store(node, host, datastore .. "/" .. today, data); - end - end - end - end -end - -module:hook("muc-config-form", function(event) - local room, form = event.room, event.form; - table.insert(form, - { - name = muc_form_config_option, - type = "boolean", - label = "Enable Logging?", - value = logging_enabled(room), - } - ); -end); - -module:hook("muc-config-submitted", function(event) - local room, fields, changed = event.room, event.fields, event.changed; - local new = fields[muc_form_config_option]; - if new ~= room._data.logging then - room._data.logging = new; - if type(changed) == "table" then - changed[muc_form_config_option] = true; - else - event.changed = true; - end - end -end); - -module:hook("message/bare", log_if_needed, 1); -if log_presences then - module:hook("iq/bare", log_if_needed, 1); - module:hook("presence/full", log_if_needed, 1); -end - -module:log("debug", "module mod_muc_log loaded!");
--- a/mod_muc_log_http/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Provides a web interface to stored chatroom logs -... - -Introduction -============ - -This module provides a built-in web interface to view chatroom logs -stored by [mod\_muc\_log](mod_muc_log.html). - -Installation -============ - -Just copy the folder muc\_log\_http as it is, into the modules folder of -your Prosody installation. - -Configuration Details -===================== - -Example configuration: - - Component "conference.example.com" "muc" - modules_enabled = { - ..... - "muc_log"; - "muc_log_http"; - ..... - } - - muc_log_http = { -- These are the defaults - show_join = true; - show_presences = true; - show_status = true; - theme = "prosody"; - url_base = "muc_log"; - } - -**show\_join** sets the default for showing joins or leaves. -**show\_status** sets the default for showing status changes. - -The web interface would then be reachable at the address: - - http://conference.example.com:5280/muc_log/ - -TODO -==== - -- Log bans correctly -- Quota \~ per day ?! -- Testing testing :)
--- a/mod_muc_log_http/muc_log_http/mod_muc_log_http.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,759 +0,0 @@ -module:depends("http"); - -local prosody = prosody; -local hosts = prosody.hosts; -local my_host = module:get_host(); -local strchar = string.char; -local strformat = string.format; -local split_jid = require "util.jid".split; -local config_get = require "core.configmanager".get; -local urldecode = require "net.http".urldecode; -local http_event = require "net.http.server".fire_event; -local datamanager = require"core.storagemanager".olddm; -local data_load, data_getpath = datamanager.load, datamanager.getpath; -local datastore = "muc_log"; -local url_base = "muc_log"; -local config = nil; -local table, tostring, tonumber = table, tostring, tonumber; -local os_date, os_time = os.date, os.time; -local str_format = string.format; -local io_open = io.open; -local themes_parent = (module.path and module.path:gsub("[/\\][^/\\]*$", "") or (prosody.paths.plugins or "./plugins") .. "/muc_log_http") .. "/themes"; - -local lom = require "lxp.lom"; -local lfs = require "lfs"; -local html = {}; -local theme; - --- Helper Functions - -local p_encode = datamanager.path_encode; -local function store_exists(node, host, today) - if lfs.attributes(data_getpath(node, host, datastore .. "/" .. today), "mode") then return true; else return false; end -end - --- Module Definitions - -local function html_escape(t) - if t then - t = t:gsub("<", "<"); - t = t:gsub(">", ">"); - t = t:gsub("(http://[%a%d@%.:/&%?=%-_#%%~]+)", function(h) - h = urlunescape(h) - return "<a href='" .. h .. "'>" .. h .. "</a>"; - end); - t = t:gsub("\n", "<br />"); - t = t:gsub("%%", "%%%%"); - else - t = ""; - end - return t; -end - -function create_doc(body, title) - if not body then return "" end - body = body:gsub("%%", "%%%%"); - return html.doc:gsub("###BODY_STUFF###", body) - :gsub("<title>muc_log</title>", "<title>"..(title and html_escape(title) or "Chatroom logs").."</title>"); -end - -function urlunescape (url) - url = url:gsub("+", " ") - url = url:gsub("%%(%x%x)", function(h) return strchar(tonumber(h,16)) end) - url = url:gsub("\r\n", "\n") - return url -end -local function urlencode(s) - return s and (s:gsub("[^a-zA-Z0-9.~_-]", function (c) return ("%%%02x"):format(c:byte()); end)); -end - -local function get_room_from_jid(jid) - local node, host = split_jid(jid); - local component = hosts[host]; - if component then - local muc = component.modules.muc - if muc and rawget(muc,"rooms") then - -- We're running 0.9.x or 0.10 (old MUC API) - return muc.rooms[jid]; - elseif muc and rawget(muc,"get_room_from_jid") then - -- We're running >0.10 (new MUC API) - return muc.get_room_from_jid(jid); - else - return - end - end -end - -local function get_room_list(host) - local component = hosts[host]; - local list = {}; - if component then - local muc = component.modules.muc - if muc and rawget(muc,"rooms") then - -- We're running 0.9.x or 0.10 (old MUC API) - for _, room in pairs(muc.rooms) do - list[room.jid] = room; - end - return list; - elseif muc and rawget(muc,"each_room") then - -- We're running >0.10 (new MUC API) - for room, _ in muc.each_room() do - list[room.jid] = room; - end - return list; - end - end -end - -local function generate_room_list(host) - local rooms; - - for jid, room in pairs(get_room_list(host)) do - local node = split_jid(jid); - if not room._data.hidden and room._data.logging and node then - rooms = (rooms or "") .. html.rooms.bit:gsub("###ROOM###", urlencode(node)):gsub("###COMPONENT###", host); - end - end - - if rooms then - return html.rooms.body:gsub("###ROOMS_STUFF###", rooms):gsub("###COMPONENT###", host), "Chatroom logs for "..host; - end -end - --- Calendar stuff -local function get_days_for_month(month, year) - if month == 2 then - local is_leap_year = (year % 4 == 0 and year % 100 ~= 0) or year % 400 == 0; - return is_leap_year and 29 or 28; - elseif (month < 8 and month%2 == 1) or (month >= 8 and month%2 == 0) then - return 31; - end - return 30; -end - -local function create_month(month, year, callback) - local html_str = html.month.header; - local days = get_days_for_month(month, year); - local time = os_time{year=year, month=month, day=1}; - local dow = tostring(os_date("%a", time)) - local title = tostring(os_date("%B", time)); - local week_days = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; - local week_day = 0; - local weeks = 1; - local _available_for_one_day = false; - - local week_days_html = ""; - for _, tmp in ipairs(week_days) do - week_days_html = week_days_html .. html.month.weekDay:gsub("###DAY###", tmp) .. "\n"; - end - - html_str = html_str:gsub("###TITLE###", title):gsub("###WEEKDAYS###", week_days_html); - - for i = 1, 31 do - week_day = week_day + 1; - if week_day == 1 then html_str = html_str .. "<tr>\n"; end - if i == 1 then - for _, tmp in ipairs(week_days) do - if dow ~= tmp then - html_str = html_str .. html.month.emptyDay .. "\n"; - week_day = week_day + 1; - else - break; - end - end - end - if i < days + 1 then - local tmp = tostring(i); - if callback and callback.callback then - tmp = callback.callback(callback.path, i, month, year, callback.room, callback.webpath); - end - if tmp == nil then - tmp = tostring(i); - else - _available_for_one_day = true; - end - html_str = html_str .. html.month.day:gsub("###DAY###", tmp) .. "\n"; - end - - if i >= days then - break; - end - - if week_day == 7 then - week_day = 0; - weeks = weeks + 1; - html_str = html_str .. "</tr>\n"; - end - end - - if week_day + 1 < 8 or weeks < 6 then - week_day = week_day + 1; - if week_day > 7 then - week_day = 1; - end - if week_day == 1 then - weeks = weeks + 1; - end - for y = weeks, 6 do - if week_day == 1 then - html_str = html_str .. "<tr>\n"; - end - for i = week_day, 7 do - html_str = html_str .. html.month.emptyDay .. "\n"; - end - week_day = 1 - html_str = html_str .. "</tr>\n"; - end - end - html_str = html_str .. html.month.footer; - if _available_for_one_day then - return html_str; - end -end - -local function create_year(year, callback) - local year = year; - local tmp; - if tonumber(year) <= 99 then - year = year + 2000; - end - local html_str = ""; - for i=1, 12 do - tmp = create_month(i, year, callback); - if tmp then - html_str = html_str .. "<div style='float: left; padding: 5px;'>\n" .. tmp .. "</div>\n"; - end - end - if html_str ~= "" then - return "<div name='yearDiv' style='padding: 40px; text-align: center;'>" .. html.year.title:gsub("###YEAR###", tostring(year)) .. html_str .. "</div><br style='clear:both;'/> \n"; - end - return ""; -end - -local function day_callback(path, day, month, year, room, webpath) - local webpath = webpath or "" - local year = year; - if year > 2000 then - year = year - 2000; - end - local bare_day = str_format("20%.02d-%.02d-%.02d", year, month, day); - room = p_encode(room); - local attributes, err = lfs.attributes(path.."/"..str_format("%.02d%.02d%.02d", year, month, day).."/"..room..".dat"); - if attributes ~= nil and attributes.mode == "file" then - local s = html.days.bit; - s = s:gsub("###BARE_DAY###", webpath .. bare_day); - s = s:gsub("###DAY###", day); - return s; - end - return; -end - -local function generate_day_room_content(bare_room_jid) - local days = ""; - local days_array = {}; - local tmp; - local node, host = split_jid(bare_room_jid); - local path = data_getpath(node, host, datastore); - local room = nil; - local next_room = ""; - local previous_room = ""; - local rooms = ""; - local attributes = nil; - local since = ""; - local to = ""; - local topic = ""; - local component = hosts[host]; - - if not(get_room_from_jid(bare_room_jid)) then - return; - end - - path = path:gsub("/[^/]*$", ""); - attributes = lfs.attributes(path); - do - local found = 0; - module:log("debug", generate_room_list(host)); - for jid, room in pairs(get_room_list(host)) do - local node = split_jid(jid) - if not room._data.hidden and room._data.logging and node then - if found == 0 then - previous_room = node - elseif found == 1 then - next_room = node - found = -1 - end - if jid == bare_room_jid then - found = 1 - end - - rooms = rooms .. html.days.rooms.bit:gsub("###ROOM###", urlencode(node)); - end - end - - room = get_room_from_jid(bare_room_jid); - if room._data.hidden or not room._data.logging then - room = nil; - end - end - if attributes and room then - local already_done_years = {}; - topic = room._data.subject or "(no subject)" - if topic:len() > 135 then - topic = topic:sub(1, topic:find(" ", 120)) .. " ..." - end - local folders = {}; - for folder in lfs.dir(path) do table.insert(folders, folder); end - table.sort(folders); - for _, folder in ipairs(folders) do - local year, month, day = folder:match("^(%d%d)(%d%d)(%d%d)"); - if year then - to = tostring(os_date("%B %Y", os_time({ day=tonumber(day), month=tonumber(month), year=2000+tonumber(year) }))); - if since == "" then since = to; end - if not already_done_years[year] then - module:log("debug", "creating overview for: %s", to); - days = create_year(year, {callback=day_callback, path=path, room=node}) .. days; - already_done_years[year] = true; - end - end - end - end - - tmp = html.days.body:gsub("###DAYS_STUFF###", days); - tmp = tmp:gsub("###PREVIOUS_ROOM###", previous_room == "" and node or previous_room); - tmp = tmp:gsub("###NEXT_ROOM###", next_room == "" and node or next_room); - tmp = tmp:gsub("###ROOMS###", rooms); - tmp = tmp:gsub("###ROOMTOPIC###", topic); - tmp = tmp:gsub("###SINCE###", since); - tmp = tmp:gsub("###TO###", to); - return tmp:gsub("###JID###", bare_room_jid), "Chatroom logs for "..bare_room_jid; -end - -local function parse_iq(stanza, time, nick) - local text = nil; - local victim = nil; - if(stanza.attr.type == "set") then - for _,tag in ipairs(stanza) do - if tag.tag == "query" then - for _,item in ipairs(tag) do - if item.tag == "item" and item.attr.nick ~= nil and item.attr.role == 'none' then - victim = item.attr.nick; - for _,reason in ipairs(item) do - if reason.tag == "reason" then - text = reason[1]; - break; - end - end - break; - end - end - break; - end - end - if victim then - if text then - text = html.day.reason:gsub("###REASON###", html_escape(text)); - else - text = ""; - end - return html.day.kick:gsub("###TIME_STUFF###", time):gsub("###VICTIM###", victim):gsub("###REASON_STUFF###", text); - end - end - return; -end - -local function parse_presence(stanza, time, nick) - local ret = ""; - local show_join = "block" - - if config and not config.show_join then - show_join = "none"; - end - - if stanza.attr.type == nil then - local show_status = "block" - if config and not config.show_status then - show_status = "none"; - end - local show, status = nil, ""; - local already_joined = false; - for _, tag in ipairs(stanza) do - if tag.tag == "alreadyJoined" then - already_joined = true; - elseif tag.tag == "show" then - show = tag[1]; - elseif tag.tag == "status" and tag[1] ~= nil then - status = tag[1]; - end - end - if already_joined == true then - if show == nil then - show = "online"; - end - ret = html.day.presence.statusChange:gsub("###TIME_STUFF###", time); - if status ~= "" then - status = html.day.presence.statusText:gsub("###STATUS###", html_escape(status)); - end - ret = ret:gsub("###SHOW###", show):gsub("###NICK###", nick):gsub("###SHOWHIDE###", show_status):gsub("###STATUS_STUFF###", status); - else - ret = html.day.presence.join:gsub("###TIME_STUFF###", time):gsub("###SHOWHIDE###", show_join):gsub("###NICK###", nick); - end - elseif stanza.attr.type == "unavailable" then - - ret = html.day.presence.leave:gsub("###TIME_STUFF###", time):gsub("###SHOWHIDE###", show_join):gsub("###NICK###", nick); - end - return ret; -end - -local function parse_message(stanza, time, nick) - local body, title, ret = nil, nil, ""; - - for _,tag in ipairs(stanza) do - if tag.tag == "body" then - body = tag[1]; - if nick then - break; - end - elseif tag.tag == "nick" and nick == nil then - nick = html_escape(tag[1]); - if body or title then - break; - end - elseif tag.tag == "subject" then - title = tag[1]; - if nick then - break; - end - end - end - if nick and body then - body = html_escape(body); - local me = body:find("^/me"); - local template = ""; - if not me then - template = html.day.message; - else - template = html.day.messageMe; - body = body:gsub("^/me ", ""); - end - ret = template:gsub("###TIME_STUFF###", time):gsub("###NICK###", nick):gsub("###MSG###", body); - elseif nick and title then - title = html_escape(title); - ret = html.day.titleChange:gsub("###TIME_STUFF###", time):gsub("###NICK###", nick):gsub("###TITLE###", title); - end - return ret; -end - -local function increment_day(bare_day) - local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); - local leapyear = false; - module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year)) - - day = tonumber(day); - month = tonumber(month); - year = tonumber(year); - - if year%4 == 0 and year%100 == 0 then - if year%400 == 0 then - leapyear = true; - else - leapyear = false; -- turn of the century but not a leapyear - end - elseif year%4 == 0 then - leapyear = true; - end - - if (month == 2 and leapyear and day + 1 > 29) or - (month == 2 and not leapyear and day + 1 > 28) or - (month < 8 and month%2 == 1 and day + 1 > 31) or - (month < 8 and month%2 == 0 and day + 1 > 30) or - (month >= 8 and month%2 == 0 and day + 1 > 31) or - (month >= 8 and month%2 == 1 and day + 1 > 30) - then - if month + 1 > 12 then - year = year + 1; - month = 1; - day = 1; - else - month = month + 1; - day = 1; - end - else - day = day + 1; - end - return strformat("20%.02d-%.02d-%.02d", year, month, day); -end - -local function find_next_day(bare_room_jid, bare_day) - local node, host = split_jid(bare_room_jid); - local day = increment_day(bare_day); - local max_trys = 7; - - module:log("debug", day); - while(not store_exists(node, host, day)) do - max_trys = max_trys - 1; - if max_trys == 0 then - break; - end - day = increment_day(day); - end - if max_trys == 0 then - return nil; - else - return day; - end -end - -local function decrement_day(bare_day) - local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); - local leapyear = false; - module:log("debug", tostring(day).."/"..tostring(month).."/"..tostring(year)) - - day = tonumber(day); - month = tonumber(month); - year = tonumber(year); - - if year%4 == 0 and year%100 == 0 then - if year%400 == 0 then - leapyear = true; - else - leapyear = false; -- turn of the century but not a leapyear - end - elseif year%4 == 0 then - leapyear = true; - end - - if day - 1 == 0 then - if month - 1 == 0 then - year = year - 1; - month = 12; - day = 31; - else - month = month - 1; - if (month == 2 and leapyear) then day = 29 - elseif (month == 2 and not leapyear) then day = 28 - elseif (month < 8 and month%2 == 1) or (month >= 8 and month%2 == 0) then day = 31 - else day = 30 - end - end - else - day = day - 1; - end - return strformat("20%.02d-%.02d-%.02d", year, month, day); -end - -local function find_previous_day(bare_room_jid, bare_day) - local node, host = split_jid(bare_room_jid); - local day = decrement_day(bare_day); - local max_trys = 7; - module:log("debug", day); - while(not store_exists(node, host, day)) do - max_trys = max_trys - 1; - if max_trys == 0 then - break; - end - day = decrement_day(day); - end - if max_trys == 0 then - return nil; - else - return day; - end -end - -local function parse_day(bare_room_jid, room_subject, bare_day) - local ret = ""; - local year; - local month; - local day; - local tmp; - local node, host = split_jid(bare_room_jid); - local year, month, day = bare_day:match("^20(%d%d)-(%d%d)-(%d%d)$"); - local previous_day = find_previous_day(bare_room_jid, bare_day); - local next_day = find_next_day(bare_room_jid, bare_day); - local temptime = {day=0, month=0, year=0}; - local path = data_getpath(node, host, datastore); - path = path:gsub("/[^/]*$", ""); - local calendar = "" - - if tonumber(year) <= 99 then - year = year + 2000; - end - - temptime.day = tonumber(day) - temptime.month = tonumber(month) - temptime.year = tonumber(year) - calendar = create_month(temptime.month, temptime.year, {callback=day_callback, path=path, room=node, webpath="../"}) or "" - - if bare_day then - local data = data_load(node, host, datastore .. "/" .. bare_day:match("^20(.*)"):gsub("-", "")); - if data then - for i=1, #data, 1 do - local stanza = lom.parse(data[i]); - if stanza and stanza.attr and stanza.attr.time then - local timeStuff = html.day.time:gsub("###TIME###", stanza.attr.time):gsub("###UTC###", stanza.attr.utc or stanza.attr.time); - if stanza[1] ~= nil then - local nick; - local tmp; - - -- grep nick from "from" resource - if stanza[1].attr.from then -- presence and messages - nick = html_escape(stanza[1].attr.from:match("/(.+)$")); - elseif stanza[1].attr.to then -- iq - nick = html_escape(stanza[1].attr.to:match("/(.+)$")); - end - - if stanza[1].tag == "presence" and nick then - tmp = parse_presence(stanza[1], timeStuff, nick); - elseif stanza[1].tag == "message" then - tmp = parse_message(stanza[1], timeStuff, nick); - elseif stanza[1].tag == "iq" then - tmp = parse_iq(stanza[1], timeStuff, nick); - else - module:log("info", "unknown stanza subtag in log found. room: %s; day: %s", bare_room_jid, year .. "/" .. month .. "/" .. day); - end - if tmp then - ret = ret .. tmp - tmp = nil; - end - end - end - end - end - if ret ~= "" then - if next_day then - next_day = html.day.dayLink:gsub("###DAY###", next_day):gsub("###TEXT###", ">") - end - if previous_day then - previous_day = html.day.dayLink:gsub("###DAY###", previous_day):gsub("###TEXT###", "<"); - end - ret = ret:gsub("%%", "%%%%"); - if config.show_presences then - tmp = html.day.body:gsub("###DAY_STUFF###", ret):gsub("###JID###", bare_room_jid); - else - tmp = html.day.bodynp:gsub("###DAY_STUFF###", ret):gsub("###JID###", bare_room_jid); - end - tmp = tmp:gsub("###CALENDAR###", calendar); - tmp = tmp:gsub("###DATE###", tostring(os_date("%A, %B %d, %Y", os_time(temptime)))); - tmp = tmp:gsub("###TITLE_STUFF###", html.day.title:gsub("###TITLE###", room_subject)); - tmp = tmp:gsub("###STATUS_CHECKED###", config.show_status and "checked='checked'" or ""); - tmp = tmp:gsub("###JOIN_CHECKED###", config.show_join and "checked='checked'" or ""); - tmp = tmp:gsub("###NEXT_LINK###", next_day or ""); - tmp = tmp:gsub("###PREVIOUS_LINK###", previous_day or ""); - - return tmp, "Chatroom logs for "..bare_room_jid.." ("..tostring(os_date("%A, %B %d, %Y", os_time(temptime)))..")"; - end - end -end - -local function handle_error(code, err) return http_event("http-error", { code = code, message = err }); end -function handle_request(event) - local response = event.response; - local request = event.request; - local room; - - local node, day, more = request.url.path:match("^/"..url_base.."/+([^/]*)/*([^/]*)/*(.*)$"); - - if more ~= "" then - response.status_code = 404; - return response:send(handle_error(response.status_code, "Unknown URL.")); - end - if node == "" then node = nil; end - if day == "" then day = nil; end - - node = urldecode(node); - - if not html.doc then - response.status_code = 500; - return response:send(handle_error(response.status_code, "Muc Theme is not loaded.")); - end - - if node then room = get_room_from_jid(node.."@"..my_host); end - if node and not room then - response.status_code = 404; - return response:send(handle_error(response.status_code, "Room doesn't exist.")); - end - if room and (room._data.hidden or not room._data.logging) then - response.status_code = 404; - return response:send(handle_error(response.status_code, "There're no logs for this room.")); - end - - if not node then -- room list for component - return response:send(create_doc(generate_room_list(my_host))); - elseif not day then -- room's listing - return response:send(create_doc(generate_day_room_content(node.."@"..my_host))); - else - if not day:match("^20(%d%d)-(%d%d)-(%d%d)$") then - local y,m,d = day:match("^(%d%d)(%d%d)(%d%d)$"); - if not y then - response.status_code = 404; - return response:send(handle_error(response.status_code, "No entries for that year.")); - end - response.status_code = 301; - response.headers = { ["Location"] = request.url.path:match("^/"..url_base.."/+[^/]*").."/20"..y.."-"..m.."-"..d.."/" }; - return response:send(); - end - - local body = create_doc(parse_day(node.."@"..my_host, room._data.subject or "", day)); - if body == "" then - response.status_code = 404; - return response:send(handle_error(response.status_code, "Day entry doesn't exist.")); - end - return response:send(body); - end -end - -local function read_file(filepath) - local f,err = io_open(filepath, "r"); - if not f then return f,err; end - local t = f:read("*all"); - f:close() - return t; -end - -local function load_theme(path) - for file in lfs.dir(path) do - if file:match("%.html$") then - module:log("debug", "opening theme file: " .. file); - local content,err = read_file(path .. "/" .. file); - if not content then return content,err; end - - -- html.a.b.c = content of a_b_c.html - local tmp = html; - for idx in file:gmatch("([^_]*)_") do - tmp[idx] = tmp[idx] or {}; - tmp = tmp[idx]; - end - tmp[file:match("([^_]*)%.html$")] = content; - end - end - return true; -end - -function module.load() - config = module:get_option("muc_log_http", {}); - if module:get_option_boolean("muc_log_presences", true) then config.show_presences = true end - if config.show_status == nil then config.show_status = true; end - if config.show_join == nil then config.show_join = true; end - if config.url_base and type(config.url_base) == "string" then url_base = config.url_base; end - - theme = config.theme or "prosody"; - local theme_path = themes_parent .. "/" .. tostring(theme); - local attributes, err = lfs.attributes(theme_path); - if attributes == nil or attributes.mode ~= "directory" then - module:log("error", "Theme folder of theme \"".. tostring(theme) .. "\" isn't existing. expected Path: " .. theme_path); - return false; - end - - local themeLoaded,err = load_theme(theme_path); - if not themeLoaded then - module:log("error", "Theme \"%s\" is missing something: %s", tostring(theme), err); - return false; - end - - module:provides("http", { - default_path = url_base, - route = { - ["GET /*"] = handle_request; - } - }); -end
--- a/mod_muc_log_http/muc_log_http/themes/prosody/components_bit.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a href="###COMPONENT###/">###COMPONENT###</a><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/components_body.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -<h2>Available Multi-User Chats:</h2><hr /><p> -###COMPONENTS_STUFF### -</p><hr /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_bann.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TIME_STUFF###<span class="muc_bann"> *** ###VICTIM### got banned###REASON_STUFF###</span><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_body.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -<div id="title"> - <div id="date">###DATE###</div> - <div id="roomjid">###JID###</div> - <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a>)</div> -</div> -<div id="calendar"> - <div id="navigation"> - ###PREVIOUS_LINK### <a class="nav" href="../">^</a>###NEXT_LINK### - </div> - ###CALENDAR### -</div> -<div id="topic">###TITLE_STUFF###</div> -<br /> -<input id="toggleJoinLeave" type="checkbox" onclick="showHide('muc_joinLeave_container')" ###JOIN_CHECKED###/><label for="toggleJoinLeave">show/hide joins and leaves</label> -<input id="toggleStatus" type="checkbox" onclick="showHide('muc_statusChange_container')" ###STATUS_CHECKED###/><label for="toggleStatus">show/hide status changes</label> -<input id="toggleUTC" type="checkbox" onclick="changeTimeDisplay()" /><label for="toggleUTC">show time in local time, rather than in UTC</label> -<div id="main">###DAY_STUFF###</div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_bodynp.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -<div id="title"> - <div id="date">###DATE###</div> - <div id="roomjid">###JID###</div> - <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a>)</div> -</div> -<div id="calendar"> - <div id="navigation"> - ###PREVIOUS_LINK### <a class="nav" href="../">^</a>###NEXT_LINK### - </div> - ###CALENDAR### -</div> -<div id="topic">###TITLE_STUFF###</div> -<br /> -<input id="toggleUTC" type="checkbox" onclick="changeTimeDisplay()" /><label for="toggleUTC">show time in local time, rather than in UTC</label> -<div id="main">###DAY_STUFF###</div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_dayLink.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a class="nav" href="../###DAY###/">###TEXT###</a>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_kick.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TIME_STUFF###<span class="muc_kick"> *** ###VICTIM### got kicked###REASON_STUFF###</span><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_message.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TIME_STUFF###<span class="muc_msg_nick"><###NICK###></span> ###MSG###<br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_messageMe.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TIME_STUFF###<span class="muc_msg_me">*###NICK### ###MSG###</span><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_presence_join.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<div class="muc_joinLeave_container">###TIME_STUFF###<span class="muc_join"> *** ###NICK### has joined the room</span><br /></div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_presence_leave.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<div class="muc_joinLeave_container">###TIME_STUFF###<span class="muc_leave"> *** ###NICK### has left the room</span><br /></div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_presence_statusChange.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<div class="muc_statusChange_container">###TIME_STUFF###<span class="muc_statusChange"> *** ###NICK### shows as "###SHOW###"###STATUS_STUFF###</span><br /></div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_presence_statusText.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - and his status message is "###STATUS###" \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_reason.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -, the reason was "###REASON###" \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_time.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a name="###TIME###" href="####TIME###" class="timestuff">[<span name="time" id="###UTC###">###TIME###</span>]</a>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_title.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TITLE###
--- a/mod_muc_log_http/muc_log_http/themes/prosody/day_titleChange.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -###TIME_STUFF###<span class="muc_titleChange"> *** ###NICK### changed the title to "###TITLE###"</span><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/days_bit.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a href="###BARE_DAY###/">###DAY###</a><br /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/days_body.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -<div id="title"> - <div id="date">###SINCE### - ###TO###</div> - <div id="roomjid">###JID###</div> - <div id="links">(join via <a class="component" href="xmpp:###JID###?join">client</a>)</div> -</div> -<div id="calendar"> - <div id="navigation"> - <a class="nav" href="../###PREVIOUS_ROOM###/"><</a> - <a class="nav" href="../">^</a> - <a class="nav" href="../###NEXT_ROOM###/">></a> - </div> - <div class="weekday">List of rooms for this component</div> - <div id="roomList">###ROOMS###</div> -</div> -<div class="topic">###ROOMTOPIC###</div> -<div id="main"> -###DAYS_STUFF### -</div>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/days_rooms_bit.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a href="../###ROOM###/">###ROOM###</a><br />
--- a/mod_muc_log_http/muc_log_http/themes/prosody/doc.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -<html> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" > -<head> - <title>muc_log</title> -</head> -<script type="text/javascript"><!-- -var utc = true; -var timezoneOffset = (parseInt((new Date()).getTimezoneOffset()) * 60) * (-1); -function changeTimeDisplay() { - var eles = document.getElementsByName("time"); - utc = !utc; - for (var i = 0; i < eles.length; i++) { - var tmp = eles[i].id.split(":"); - var d; - if(tmp.length == 3) { - if(!utc) - tmp[2] = Number(tmp[2]) + timezoneOffset; - d = new Date(0, 0, 0, Number(tmp[0]), Number(tmp[1]), Number(tmp[2])); - } - else { - if(!utc) - tmp[0] = Number(tmp[0]) + timezoneOffset; - d = new Date(0, 0, 0, 0, 0, Number(tmp[0])); - } - - eles[i].innerHTML = (d.getHours() <= 9 ? "0" + d.getHours() : d.getHours()) + ":"; - eles[i].innerHTML += (d.getMinutes() <= 9 ? "0" + d.getMinutes() : d.getMinutes()) + ":"; - eles[i].innerHTML += (d.getSeconds() <= 9 ? "0" + d.getSeconds() : d.getSeconds()); - } -} - -function showHide(name) { - var eles = document.getElementsByClassName(name); - for (var i = 0; i < eles.length; i++) { - eles[i].style.display = eles[i].style.display != "none" ? "none" : ""; - } -} - -/* IE compat: */ -onload=function(){ - if (document.getElementsByClassName == undefined) { - document.getElementsByClassName = function(className) - { - var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)"); - var allElements = document.getElementsByTagName("*"); - var results = []; - - var element; - for (var i = 0; (element = allElements[i]) != null; i++) { - var elementClass = element.className; - if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass)) - results.push(element); - } - - return results; - } - } -} ---></script> -<style type="text/css"> -#title { - border-bottom: #f29b00 solid 3pt; padding-bottom: 3px; width: 100%; - color: #000000; font-size: 24px; font-weight: bold; font-family: sans-serif; letter-spacing: 3px; text-decoration: none; margin-bottom: 3pt; -} -#date { - float: right; -} -#links { - font-size: 9px; font-family: Verdana; letter-spacing: 1px; -} -#topic { - color: #000000; font-size: 18px; font-family: sans-serif; -} -#calendar {float: right; margin-left: 10pt;} -#navigation {text-align: center} -#roomList {padding-left: 5px; padding-right: 5px; border: 1px solid black;} - -#main {overflow: auto; border: 2px solid gray; padding: 3px; margin-top: 1em} - -a {color: #6197df; text-decoration: none;} - -a.nav {color: #6197df; font-family: monospace; letter-spacing: 7px; font-size: 24px; text-decoration: none;} -a.log {color: #6197df; text-decoration: none;} -a.log_today {color: #f29b00; text-decoration: none;} - -.day { font: 12px Verdana; height: 17px; color: #BBBBBB} -.weekday { font: 10px Verdana; height: 17px; color: #FFFFFF; background-color: #000000; margin-top: 50px; - padding-left: 5px; padding-right: 5px; padding-top: 3px; text-align: center;} -.timestuff {color: #AAAAAA; text-decoration: none;} -.muc_join {color: #009900; font-style: italic;} -.muc_leave {color: #009900; font-style: italic;} -.muc_statusChange {color: #009900; font-style: italic;} -.muc_title {color: #BBBBBB; font-size: 32px;} -.muc_titleChange {color: #009900; font-style: italic;} -.muc_kick {color: #009900; font-style: italic;} -.muc_bann {color: #009900; font-style: italic;} -.muc_msg_nick {color: #0000AA;} -.muc_msg_me {color: #0000AA;} -.month_title {font: 10pt Verdana;} -.year_title {font: bold 16px Verdana;} -.footer {margin-top: 20pt; text-align: center;} - -label {margin-right: 2em} -thead {font: 25pt bold; text-align: center} -tbody {border: solid black 1px;} -img {border: 0px} -body {margin-left: 20pt; margin-right: 20pt;} -</style> -<body> -###BODY_STUFF### -<div class="footer"><a href="http://prosody.im"><img alt="Powered by Prosody" src="http://prosody.im/files/powered_by_prosody_80x30.png"/></a></div> -</body> -</html>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/month_day.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - <td class="day" valign="middle" align="center">###DAY###</td> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/month_emptyDay.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - <td class="day"> </td> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/month_footer.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -</tbody></table> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/month_header.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -<table rules="groups" cellpadding="5"> -<thead><tr><td colspan="7">###TITLE###</td></tr></thead> -<tbody> -<tr>###WEEKDAYS###</tr>
--- a/mod_muc_log_http/muc_log_http/themes/prosody/month_weekDay.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ - <th class="weekday" valign="middle" align="center">###DAY###</th> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/rooms_bit.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a href="###ROOM###/">###ROOM###</a><br />
--- a/mod_muc_log_http/muc_log_http/themes/prosody/rooms_body.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3 +0,0 @@ -<h2>Available rooms on ###COMPONENT###:</h2><hr /><p> -###ROOMS_STUFF### -</p><hr /> \ No newline at end of file
--- a/mod_muc_log_http/muc_log_http/themes/prosody/year_title.html Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<div class=year_title"><a name="###YEAR###">###YEAR###</a></div>
--- a/mod_muc_mam_hints/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Support XEP-0334: Message Processing Hints for MUC messages' -... - -Introduction -============ - -This module will check for MUC messages with XEP-0334 Message -Processing Hints tags to qualify those messages as "historic" -for later MAM archiving or not. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "muc\_mam\_hints" to your modules\_enabled list in your MUC -component: - -``` {.lua} -Component "conference.example.org" "muc" -modules_enabled = { - "muc_mam", - "muc_mam_hints", -} -```
--- a/mod_muc_mam_markers/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ ---- -labels: -- 'Stage-alpha' -summary: Save received chat markers into MUC archives' -... - -Introduction -============ - -Chat markers (XEP-0333) specification states that markers _SHOULD_ be -archived. This is already happening in one to one conversations in -the personal archives but not in Group Chats. This module hooks the -_muc-message-is-historic_ event to customize the `mod_muc_mam` -behavior and have the chat markers archived. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "muc\_mam\_markers" to your `modules\_enabled` list in your -MUC component's definition. - -No configuration options are available.
--- a/mod_muc_markers/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ -# Introduction - -This module adds an internal Prosody API to retrieve the last displayed message by MUC occupants. - -## Requirements - -The clients must support XEP-0333, and the users to be tracked must be affiliated with the room. - -Currently due to lack of clarity about which id to use in acknowledgements in XEP-0333, this module -rewrites the id attribute of stanzas to match the stanza (archive) id assigned by the MUC server. - -Oh yeah, and mod_muc_mam is required (or another module that adds a stanza-id), otherwise this module -won't do anything. - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_markers"; - "muc_mam"; -} -``` - -## Settings - -| Name | Description | Default | -|----------------------------|--------------------------------------------------------------------------------------|-------------| -| muc_marker_summary_on_join | Whether a summary of all the latest markers should be sent to someone entering a MUC | true | -| muc_marker_type | The type of marker to track (displayed/received/acknowledged) | "displayed" | - - -# Developers - -## Example usage - -``` -local muc_markers = module:depends("muc_markers"); - -function something() - local last_displayed_id = muc_markers.get_user_read_marker("user@localhost", "room@conference.localhost"); -end -```
--- a/mod_muc_media_metadata/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Experimental module to add metadata to media in MUCs' -... - -# Introduction - -This module adds additional metadata to media shared in a MUC. This can help clients -make decisions and provide better UI and enhanced privacy, by knowing things like file -size without needing to make external network requests. - -::: {.alert .alert-danger} -**NOTE:** This is an experimental module. It is not supported by any -clients, and therefore is mainly of interest to client developers who -wish to explore the idea. -::: - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_media_metadata"; -} -``` - -## Settings - -There are no configuration options for this module. - -# Developers - -## Example stanzas - -A normal message in a chatroom containing an image: - -``` -<message from="test@rooms.example.com/matthew" id="9f45a784-5e5b-4db5-a9b3-8ea1d7c1162d" type="groupchat"> - <body>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</body> - <x xmlns="jabber:x:oob"> - <url>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</url> - </x> -</message> -``` - -The same stanza with this module loaded now contains additional metadata added by the server: - -``` -<message from="test@rooms.example.com/matthew" id="9f45a784-5e5b-4db5-a9b3-8ea1d7c1162d" type="groupchat"> - <body>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</body> - <x xmlns="jabber:x:oob"> - <url>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</url> - <metadata xmlns="xmpp:prosody.im/protocol/media-metadata#0"> - <bytes>15690</bytes> - <type>image/png</type> - <blurhash>LEHV6nWB2yk8pyo0adR*.7kCMdnj</blurhash> - </metadata> - </x> -</message> -```
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_media_metadata/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,63 @@ +--- +labels: +- 'Stage-Alpha' +summary: 'Experimental module to add metadata to media in MUCs' +... + +# Introduction + +This module adds additional metadata to media shared in a MUC. This can help clients +make decisions and provide better UI and enhanced privacy, by knowing things like file +size without needing to make external network requests. + +::: {.alert .alert-danger} +**NOTE:** This is an experimental module. It is not supported by any +clients, and therefore is mainly of interest to client developers who +wish to explore the idea. +::: + +# Configuring + +## Enabling + +``` {.lua} +Component "rooms.example.net" "muc" +modules_enabled = { + "muc_media_metadata"; +} +``` + +## Settings + +There are no configuration options for this module. + +# Developers + +## Example stanzas + +A normal message in a chatroom containing an image: + +``` +<message from="test@rooms.example.com/matthew" id="9f45a784-5e5b-4db5-a9b3-8ea1d7c1162d" type="groupchat"> + <body>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</body> + <x xmlns="jabber:x:oob"> + <url>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</url> + </x> +</message> +``` + +The same stanza with this module loaded now contains additional metadata added by the server: + +``` +<message from="test@rooms.example.com/matthew" id="9f45a784-5e5b-4db5-a9b3-8ea1d7c1162d" type="groupchat"> + <body>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</body> + <x xmlns="jabber:x:oob"> + <url>https://matthewwild.co.uk/share.php/70334772-ff74-439b-8173-a71e40ca28db/mam-flow.png</url> + <metadata xmlns="xmpp:prosody.im/protocol/media-metadata#0"> + <bytes>15690</bytes> + <type>image/png</type> + <blurhash>LEHV6nWB2yk8pyo0adR*.7kCMdnj</blurhash> + </metadata> + </x> +</message> +```
--- a/mod_muc_mention_notifications/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -# Configuring - -This module lets Prosody notify users when they're mentioned in a MUC, even if they're not currently present in it. - -Users need to be explicitly mentioned via XEP-0372 references. - -In anonymous and semi-anonymous rooms, the mentioned user needs to have their nickname registered in the MUC so that Prosody can get the real JID from the referenced nickname. - -NOTE: this module is not compatible with mod_block_strangers because the latter will block the notification messages from the MUC (since they're not "groupchat" messages). - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_mention_notifications"; -} -``` - -## Settings - -|Name |Description |Default | -|-----|------------|--------| -|muc_mmn_notify_unaffiliated_users| Notify mentioned users even if they are not members of the room they were mentioned in | false |
--- a/mod_muc_moderation/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -summary: Let moderators remove spam and abuse messages ---- - -# Introduction - -This module implements [XEP-0425: Message Moderation]. - -# Usage - -Moderation is done via a supporting client and requires a `moderator` -role in the channel / group chat. - -# Configuration - -Example [MUC component][doc:chatrooms] configuration: - -``` {.lua} -Component "channels.example.com" "muc" -modules_enabled = { - "muc_mam", - "muc_moderation", -} -``` - -# Compatibility - -- Basic functionality with Prosody 0.11.x and later -- Full functionality with Prosody 0.12.x and `internal` or `sql` - storage^[Replacing moderated messages with tombstones requires new storage API methods.] -- Works with [mod_storage_xmlarchive] - -## Clients - -- [Converse.js](https://conversejs.org/) -- [Gajim](https://dev.gajim.org/gajim/gajim/-/issues/10107) -- [clix](https://code.zash.se/clix/rev/6c1953fbe0fa) - -### Feature requests - -- [Conversations](https://codeberg.org/iNPUTmice/Conversations/issues/20) -- [Dino](https://github.com/dino/dino/issues/1133) -- [Profanity](https://github.com/profanity-im/profanity/issues/1336)
--- a/mod_muc_notifications/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ ---- -labels: -- 'Stage-alpha' -summary: 'Notify of MUC messages to not present members' -... - -Introduction -============ - -This module listens to MUC messages and sends a notification to the -MUC members not present in the MUC at that moment. - -By default, the notification will be a message with a simple text as body. - -By sending this "out-of-MUC" notification, not-joined members will be able to -know that new messages are available. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "muc\_notifications" to your modules\_enabled list in your -MUC component: - -```{.lua} -Component "conference.example.org" "muc" -modules_enabled = { - "muc_notifications", -} -``` - -You may also want to enable "offline\_hints" module so the notification messages -sent by this module are not added to the offline storage for later delivery. - -Configuration -============= - - Option Description - --------------------------- ---------------------------------------------------------------------------------------------- - muc\_notification\_invite If set to `true`, the notification sent will take the form of a MUC invite. (default: `false`)
--- a/mod_muc_occupant_id/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -summary: 'Anonymous unique occupant identifiers for MUCs' -... - -Introduction -============ - -This module implements [XEP-0421: Anonymous unique occupant identifiers for -MUCs](https://xmpp.org/extensions/xep-0421.html). - -TODO -==== - -- Subject messages - -Compatibility -============= - - ------- ------------------ - 0.12 Built-in, not needed - 0.11 Works; except in MUC-PMs - ------- ------------------
--- a/mod_muc_occupant_id/mod_muc_occupant_id.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ - --- Implementation of https://xmpp.org/extensions/inbox/occupant-id.html --- XEP-0421: Anonymous unique occupant identifiers for MUCs - -module:depends("muc"); - -local uuid = require "util.uuid"; -local hmac_sha256 = require "util.hashes".hmac_sha256; -local b64encode = require "util.encodings".base64.encode; - -local xmlns_occupant_id = "urn:xmpp:occupant-id:0"; - -local function generate_id(occupant, room) - local bare = occupant.bare_jid; - - if room._data.occupant_id_salt == nil then - room._data.occupant_id_salt = uuid.generate(); - end - - -- XXX: Temporary not-so-important migration measure. Remove this next time - -- somebody looks at it. This module used to store every participant's - -- occupant-id all the time forever. - room._data.occupant_ids = nil; - - return b64encode(hmac_sha256(bare, room._data.occupant_id_salt)); -end - -local function update_occupant(event) - local stanza, room, occupant, dest_occupant = event.stanza, event.room, event.occupant, event.dest_occupant; - - -- "muc-occupant-pre-change" provides "dest_occupant" but not "occupant". - if dest_occupant ~= nil then - occupant = dest_occupant; - end - - -- strip any existing <occupant-id/> tags to avoid forgery - stanza:remove_children("occupant-id", xmlns_occupant_id); - - local unique_id = generate_id(occupant, room); - stanza:tag("occupant-id", { xmlns = xmlns_occupant_id, id = unique_id }):up(); -end - -local function muc_private(event) - local stanza, room = event.stanza, event.room; - local occupant = room._occupants[stanza.attr.from]; - - update_occupant({ - stanza = stanza, - room = room, - occupant = occupant, - }); -end - -module:add_feature(xmlns_occupant_id); -module:hook("muc-disco#info", function (event) - event.reply:tag("feature", { var = xmlns_occupant_id }):up(); -end); - -module:hook("muc-broadcast-presence", update_occupant); -module:hook("muc-occupant-pre-join", update_occupant); -module:hook("muc-occupant-pre-change", update_occupant); -module:hook("muc-occupant-groupchat", update_occupant); -module:hook("muc-private-message", muc_private);
--- a/mod_muc_ping/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -superseded_by: mod_muc -summary: XEP-0410 Server Optimization (now supported by Prosody mod_muc) ---- - -This module implements the [Server -Optimization](https://xmpp.org/extensions/xep-0410.html#serveroptimization) -part of [XEP-0410: MUC Self-Ping] - -# Usage - -The module is loaded on MUC components: - -```lua -Component "muc.example.com" "muc" -modules_enabled = { - "muc_ping"; -} -``` - -# Configuration - -No options. - -# Compatibility - -It should work with Prosody up until 0.10.x. - -Prosody 0.11.x and trunk natively supports XEP-0410 so this module is **not** needed.
--- a/mod_muc_rai/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -# Introduction - -This module implements [XEP-xxxx: Room Activity Indicators](https://xmpp.org/extensions/inbox/room-activity-indicators.html). - -## Requirements - -This module currently depends on mod_muc_markers, so review the requirements for that module. - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_rai"; - "muc_markers"; - "muc_mam"; -} -``` - -## Settings - -|Name |Description |Default | -|-----|------------|--------| -|muc_rai_max_subscribers| Maximum number of active subscriptions allowed | 1024 | - -# Compatibility - -Requires Prosody trunk (2020-04-15+). \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_muc_rai/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,30 @@ +# Introduction + +This module implements [XEP-xxxx: Room Activity Indicators](https://xmpp.org/extensions/inbox/room-activity-indicators.html). + +## Requirements + +This module currently depends on mod_muc_markers, so review the requirements for that module. + +# Configuring + +## Enabling + +``` {.lua} +Component "rooms.example.net" "muc" +modules_enabled = { + "muc_rai"; + "muc_markers"; + "muc_mam"; +} +``` + +## Settings + +|Name |Description |Default | +|-----|------------|--------| +|muc_rai_max_subscribers| Maximum number of active subscriptions allowed | 1024 | + +# Compatibility + +Requires Prosody trunk (2020-04-15+). \ No newline at end of file
--- a/mod_muc_require_tos/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Require visitors to accept something before being allowed in a room -... - -# Introduction - -This module sends a message to visitors of a room, prompting them to accept or reject it. - -They get kicked if they reject it, and become members if they accept it. - -# Setup - -```lua -Component "rooms.example.org" "muc" - modules_enabled = { - "muc_require_tos"; - } - tos_welcome_message = "Please read and accept the TOS of this service: https://lurk.org/TOS.txt" - tos_yes_message = "Thanks, and welcome here!" - tos_no_message = "Too bad." -``` - -Compatibility -============= - - ----- ----- - trunk Works - ----- ----- -
--- a/mod_muc_reserve_nick_pattern/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Require MUC occupant nicknames to no match some patterns' ---- - -Introduction -============ - -This checks the nickname of a joining user against a configurable list of -[Lua patterns](https://www.lua.org/manual/5.2/manual.html#6.4.1), and prevents -them from joining if it matches any of them. - -Configuration -============= - -There is a single configuration option, `muc_reserve_nick_patterns` and the -default is `{}` - i.e. allow everything. - -Compatibility -============= - -Requires Prosody 0.11 or higher.
--- a/mod_muc_restrict_media/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -# Introduction - -This module adds a room configuration option to hide inline media from -unaffiliated users in MUCs and display them as links instead. - -This can be useful in public channels where content posted by users should not -be shown by default. - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_restrict_media"; -} -``` - -## Settings - -A default setting can be provided in the config file: - -``` {.lua} -muc_room_default_restrict_media = true -```
--- a/mod_muc_restrict_nick/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Require MUC occupant nicknames to match a specific pattern' ---- - -Introduction -============ - -This checks the nickname of a joining user against a configurable -[Lua pattern](https://www.lua.org/manual/5.2/manual.html#6.4.1), and prevents -them from joining if it does not match. - -Configuration -============= - -There is a single configuration option, `muc_restrict_nick_pattern` and the -default is `"^%w+$"` - i.e. allow only alphanumeric characters in nicknames. - -Compatibility -============= - -Requires Prosody 0.11 or higher.
--- a/mod_muc_restrict_pm/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Limit who may send and recieve MUC PMs -... - -# Introduction - -This module adds configurable MUC options that restrict and limit who may send MUC PMs to other users. - -If a user does not have permissions to send a MUC PM, the MUC will send a policy violation stanza. - -# Setup - -```lua -Component "conference.example.org" "muc" - -modules_enabled = { - "muc_restrict_pm"; -} -``` - -Compatibility -============= - - ----- ----- - 0.12 Works - 0.11 Probably does not work - ----- ----- -
--- a/mod_muc_restrict_rooms/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ ---- -summary: Regexp based room restriction module -... - -Introduction -============ - -This module allows disabling room creation based on regexp patterns -defined in configuration. - -Dependencies -============ - -This module depends on **muc/rooms** module. If **muc/rooms** is not -loaded, this module won't work. - -How to load the module -====================== - -Copy the module to the prosody modules/plugins directory. - -In Prosody's configuration file, under the desired MUC component -definition, add: - - modules_enabled = { - ... - "mod_muc_restrict_rooms"; - ... - } - -**Note**: This module *shouldn't* be loaded in the global -**modules\_enabled**, otherwise it won't work. - -Configuration -============= - -**mod\_muc\_restrict\_rooms** has several variables which let you -configure the patterns for room names you want to ban, establish -exceptions for those patterns and even deciding whether admins can or -not bypass the prohibition. - - Name Description Example Default value - ------------------------------ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------- --------------- - muc\_restrict\_matching Table in the key/value format (keys for patterns and values for reasons) that determines which rooms shouldn't be created. The key is a regexp and must be specified between quotation marks (see example). Room names will be evaluated always lowercase, so define your patterns taking this into consideration. Users that try to join any room that matches one of those rules will get an error telling them they cannot join. muc\_restrict\_matching = { ["\^admin"] = "Rooms that start with 'admin' are reserved for staff use only" } {} - muc\_restrict\_exceptions String format table that contains exceptions to the above defined rules. Room names specified here will bypass the muc\_restrict\_matching restrictions and will be available for anyone muc\_restrict\_exceptions = { "admins\_are\_good", "admins\_rocks" } {} - muc\_restrict\_allow\_admins Boolean that determines whether users in the **admin** table are able to bypass any room restriction. If ser to *true*, they will be able to bypass those rules. muc\_restrict\_allow\_admins = true false - -Compatibility -============= - - ----- ------------- - trunk Doesn't work (uses is_admin) - 0.9 Works - 0.8 Should work - ----- -------------
--- a/mod_muc_webchat_url/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -# Introduction - -Many projects have a support room accessible via a web chat. This module -allows making the URL to such a web chat discoverable via the XMPP -service discovery protocol, enabling e.g. [search -engines](https://search.jabbercat.org/) to index and present these. - -# Configuring - -## Enabling - -``` {.lua} -Component "rooms.example.net" "muc" -modules_enabled = { - "muc_webchat_url"; -} -``` - -## Settings - -The URL is configured using the in-band MUC room configuration protocol. - -The module can optionally be configured to give all public (not -members-only, hidden or password protected) rooms gain a default value -based on a template: - -``` {.lua} -muc_webchat_baseurl = "https://chat.example.com/join?room={node}" -``` - -The following variables will be subsituted with room address details: - -`{jid}` -: The complete room address, eg `room@muc.example.com`· - -`{node}` -: The local part (before the `@`) of the room JID. - -`{host}` -: The domain name part of the room JID.
--- a/mod_munin/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Statistics' -summary: Implementation of the Munin node protocol -... - -Summary -======= - -This module implements the Munin reporting protocol, allowing you to -collect statistics directly from Prosody into Munin. - -Configuration -============= - -There is only one recommended option, `munin_node_name`, which specifies -the name that Prosody will identify itself by to the Munin server. You -may want to set this to the same hostname as in the [SRV record][doc:dns] -for the machine. - -```lua -modules_enabled = { - -- your other modules - "munin", -} - -munin_node_name = "xmpp.example.com" -``` - -You will also want to enable statistics collection by setting: - -```lua -statistics_interval = 300 -- every 5 minutes, same as munin -``` - -## Summary - -All these must be in [the global section][doc:configure#overview]. - - Option Type Default - ----------------------- -------- --------------------------- - munin\_node\_name string `"localhost"` - munin\_ignored\_stats set `{ }` - munin\_ports set `{ 4949 }` - munin\_interfaces set `{ "0.0.0.0", "::" }`[^1] - -[^1]: Varies depending on availability of IPv4 and IPv6 - -## Ports and interfaces - - -`mod_munin` listens on port `4949` on all local interfaces by default. -This can be changed with the standard [port and network configuration][doc:ports]: - - -``` lua --- defaults: -munin_ports = { 4949 } -munin_interfaces = { "::", "0.0.0.0" } -``` - -If you already have a `munin-node` instance running, you can set a -different port to avoid the conflict. - -## Configuring Munin - -Simply add `munin_node_name` surrounded by brackets to `/etc/munin/munin.conf`: - -``` ini -[xmpp.example.com] -address xmpp.example.com -port 4949 -``` - -You can leave out `address` if it equal to the name in brackets, and -leave out the `port` if it is the default (`4949`). - -Setting `address` to an IP address may sometimes be useful as the Munin -collection server is not delayed by DNS lookups in case of network -issues. - -If you set a different port, or if the hostname to connect to is -different from this hostname, make sure to add `port` and/or `address` -options. - -See [Munin documentation][muninconf] for more information. - -Compatibility -============= - -**Requires** Prosody 0.10 or above - -[muninconf]: http://guide.munin-monitoring.org/en/stable-2.0/reference/munin.conf.html
--- a/mod_net_dovecotauth/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ ---- -labels: -... - -Introduction -============ - -mod\_net\_dovecotauth is a server implementation of the Dovecot -authentication protocol. It allows you to authenticate e.g. Postfix -against your Prosody installation. - -Due to missing support for virtal hosts in this protocol, only one host -can be supported. - -Configuration -============= - -Install and add to modules\_enabled like any other module. - - dovecotauth_host = "example.com" -- Must be a defined VirtualHost - -Compatibility -============= - -Works with 0.9
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_net_dovecotauth/README.md Tue Mar 18 00:16:25 2025 +0700 @@ -0,0 +1,25 @@ +--- +labels: +... + +Introduction +============ + +mod\_net\_dovecotauth is a server implementation of the Dovecot +authentication protocol. It allows you to authenticate e.g. Postfix +against your Prosody installation. + +Due to missing support for virtal hosts in this protocol, only one host +can be supported. + +Configuration +============= + +Install and add to modules\_enabled like any other module. + + dovecotauth_host = "example.com" -- Must be a defined VirtualHost + +Compatibility +============= + +Works with 0.9
--- a/mod_net_proxy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,179 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Implementation of PROXY protocol versions 1 and 2' -... - -Introduction -============ - -This module implements the PROXY protocol in versions 1 and 2, which fulfills -the following usecase as described within the official protocol specifications: - -> Relaying TCP connections through proxies generally involves a loss of the -> original TCP connection parameters such as source and destination addresses, -> ports, and so on. -> -> The PROXY protocol's goal is to fill the server's internal structures with the -> information collected by the proxy that the server would have been able to get -> by itself if the client was connecting directly to the server instead of via a -> proxy. - -You can find more information about the PROXY protocol on -[the official website](https://www.haproxy.com/blog/haproxy/proxy-protocol/) -or within -[the official protocol specifications.](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) - - -Usage -===== - -Copy the plugin into your prosody's modules directory. And add it -between your enabled modules into the global section (modules\_enabled). - -As the PROXY protocol specifications do not allow guessing if the PROXY protocol -shall be used or not, you need to configure separate ports for all the services -that should be exposed with PROXY protocol support: - -```lua ---[[ - Maps TCP ports to a specific Prosody network service. Further information about - available service names can be found further down below in the module documentation. -]]-- -proxy_port_mappings = { - [15222] = "c2s", - [15269] = "s2s" -} - ---[[ - Specifies a list of trusted hosts or networks which may use the PROXY protocol - If not specified, it will default to: 127.0.0.1, ::1 (local connections only) - An empty table ({}) can be configured to allow connections from any source. - Please read the module documentation about potential security impact. -]]-- -proxy_trusted_proxies = { - "192.168.10.1", - "172.16.0.0/16" -} - ---[[ - While you can manually override the ports this module is listening on with - the "proxy_ports" directive, it is highly recommended to not set it and instead - only configure the appropriate mappings with "proxy_port_mappings", which will - automatically start listening on all mapped ports. - - Example: proxy_ports = { 15222, 15269 } -]]-- -``` - -The above example configuration, which needs to be placed in the global section, -would listen on both tcp/15222 and tcp/15269. All incoming connections have to -originate from trusted hosts/networks (configured by _proxy_trusted_proxies_) and -must be initiated by a PROXYv1 or PROXYv2 sender. After processing the PROXY -protocol, those connections will get mapped to the configured service name. - -Please note that each port handled by _mod_net_proxy_ must be mapped to another -service name by adding an item to _proxy_port_mappings_, otherwise a warning will -be printed during module initialization and all incoming connections to unmapped ports -will be dropped after processing the PROXY protocol requests. - -The service name can be found by analyzing the source of the module, as it is the -same name as specified within the _name_ attribute when calling -`module:provides("net", ...)` to initialize a network listener. The following table -shows the names for the most commonly used Prosody modules: - - ------------- -------------------------- - **Module** **Service Name** - c2s c2s (Plain/StartTLS) - s2s s2s (Plain/StartTLS) - proxy65 proxy65 (Plain) - http http (Plain) - net_multiplex multiplex (Plain/StartTLS) - ------------- -------------------------- - -This module should work with all services that are providing ports which either -offer plaintext or StartTLS-based encryption. Please note that instead of using -this module for HTTP-based services (BOSH/WebSocket) it might be worth resorting -to use proxy which is able to process HTTP and insert a _X-Forwarded-For_ header -instead. - - -Example -======= - -This example provides you with a Prosody server that accepts regular connections on -tcp/5222 (C2S) and tcp/5269 (S2S) while also offering dedicated PROXY protocol ports -for both modules, configured as tcp/15222 (C2S) and tcp/15269 (S2S): - -```lua -c2s_ports = {5222} -s2s_ports = {5269} -proxy_port_mappings = { - [15222] = "c2s", - [15269] = "s2s" -} -``` - -After adjusting the global configuration of your Prosody server accordingly, you can -configure your desired sender accordingly. Below is an example for a working HAProxy -configuration which will listen on the default XMPP ports (5222+5269) and connect to -your XMPP backend running on 192.168.10.10 using the PROXYv2 protocol: - -``` -defaults d-xmpp - log global - mode tcp - option redispatch - option tcplog - option tcpka - option clitcpka - option srvtcpka - - timeout connect 5s - timeout client 24h - timeout server 60m - -frontend f-xmpp - bind :::5222,:::5269 v4v6 - use_backend b-xmpp-c2s if { dst_port eq 5222 } - use_backend b-xmpp-s2s if { dst_port eq 5269 } - -backend b-xmpp-c2s - balance roundrobin - option independent-streams - server mycoolprosodybox 192.168.10.10:15222 send-proxy-v2 - -backend b-xmpp-s2s - balance roundrobin - option independent-streams - server mycoolprosodybox 192.168.10.10:15269 send-proxy-v2 -``` - - -Limitations -=========== - -It is currently not possible to use this module for offering PROXY protocol support -on SSL/TLS ports, which will automatically initiate a SSL handshake. This might be -possible in the future, but it currently does not look like this could easily be -implemented due to the current handling of such connections. - - -Important Notes -=============== - -Please do not expose any ports offering PROXY protocol to the internet - while regular -clients will be unable to use them anyways, it is outright dangerous and allows anyone -to spoof the actual IP address. It is highly recommended to only allow PROXY -connections from trusted sources, e.g. your loadbalancer. - - -Compatibility -============= - - ----- ----- - trunk Works - 0.12 Works - 0.11 Works - 0.10 Works - ----- -----
--- a/mod_nodeinfo2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ ---- -labels: -- Stage-Alpha ---- - -Introduction -============ - -This module exposes a [nodeinfo2](https://git.feneas.org/jaywink/nodeinfo2) -.well-known URL for use e.g. from -[the-federation.info](https://the-federation.info). - -Configuration -============= - -Enable the `nodeinfo` module in your global `modules_enabled` section: -``` -modules_enabled = { - ... - "nodeinfo2" - ... -} -``` - -Set the `nodeinfo2_expose_users` option to false if you don’t want to expose -statistics about the amount of users you host: -``` -nodeinfo2_expose_users = false -``` - -Set the `nodeinfo2_expose_posts` option to false if you don’t want to expose -statistics about the amount of messages being exchanged by your users: -``` -nodeinfo2_expose_posts = false -``` - -This module depends on -[mod\_lastlog](https://modules.prosody.im/mod_lastlog.html) to calculate user -activity, and [mod\_http](https://prosody.im/doc/http). Most of its -configuration actually happens in this dependency. - -Compatibility -============= - - ------- -------------------- - trunk Does not work [^1] - 0.11 Should work - ------- -------------------- - -[^1]: not after - [5f15ab7c6ae5](https://hg.prosody.im/trunk/rev/5f15ab7c6ae5)
--- a/mod_nooffline_noerror/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Discard offline stanzas instead of generating stanza errors if mod_offline is not loaded -... - -Introduction -============ - -By default without mod_offline stanzas that would go to offline storage -trigger error stanzas sent back to the sender to inform him of undeliverable stanzas. - -But if you use MAM on your server and are certain, all of your clients are using it, -you can use this module to disable the error stanzas. -If mod_offline is loaded, this module will do nothing. - -Warning -======= - -You most certainly *should not* use this module if you cannot be certain -that *all* your clients support and use MAM! - -Compatibility -============= - - ----- ------------------------------------------------------------------- - trunk Works - 0.10 Works - 0.9 Untested but should work - ----- -------------------------------------------------------------------
--- a/mod_offline_email/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Forward offline messages via email -... - -Introduction -============ - -Quite often when I am out and about, I'm not able to connect to Jabber. -It is usually much more likely I can access my email though (whether via -the web, or a mobile client). - -For this reason I decided it would be extremely useful to have Jabber -messages sent to me while I was offline forwarded to my email inbox. - -Usage -===== - -Simply add "offline\_email" to your modules\_enabled list. When any user -receives a message while they are offline, it will automatically be -forwarded via mail to the **same** address as their Jabber ID. e.g. -user1@example.com's offline messages will be forwarded to -user1@example.com's email inbox. - -Configuration -============= - - Option Description - ------------------------ ---------------------------------------------------------------------------------------------------------------------------------------------------- - queue\_offline\_emails The number of seconds to buffer messages for, before they are sent as an email. The default is to send each message as it arrives. - smtp\_server Address of the SMTP server to send through. Default 'localhost' (recommended, see caveats below) - smtp\_username If set, Prosody will authenticate with the SMTP server before sending (default is no authentication) - smtp\_password The password for the above user (default is none) - smtp\_from Address from which it will appear the emails came. Default is smtp\_username@smtp\_server, where smtp\_username is replaced with 'xmpp' if not set - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- ------- - -Caveats/Todos/Bugs -================== - -- Currently SMTP sending blocks the whole server. This should not be - noticeable if your mail server is on the same machine as Prosody. -- There is not (yet) any way to configure forwarding to an email - address other than your JID (idea... use email address in vcard?) -- Enable/disable this feature per user?
--- a/mod_offline_hints/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -labels: -- 'Stage-alpha' -summary: Do not store in offline storage messages hinted with no-store' -... - -Introduction -============ - -`mod_offline` does not take into account XEP-334 tags. This module -will not add to the offline storage those messages tagged with -`<no-store />`. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "offline\_hints" to your modules\_enabled list in your -configuration. - -No configuration options are available.
--- a/mod_ogp/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -# mod_ogp - -This module adds [Open Graph Protocol](https://ogp.me) metadata to URLs sent inside a MUC. - -With mod_ogp enabled, when a user sends a URL in a MUC (where the message has its `id` equal to its `origin-id`), the module calls the URL and parses the result for `<meta>` html tags that have any `og:...` properties. -If it finds any, it sends a [XEP-0422 fastening](https://xmpp.org/extensions/xep-0422.html) applied to the original message that looks like: - -```xml -<message id="example" from="chatroom@muc.example.org" to="user@chat.example.org/resource"> -<apply-to xmlns="urn:xmpp:fasten:0" id="origin-id-X"> -<meta xmlns="http://www.w3.org/1999/xhtml" property="og:title" content="The Rock"/> -<meta xmlns="http://www.w3.org/1999/xhtml" property="og:url" content="https://www.imdb.com/title/tt0117500/"/> -<meta xmlns="http://www.w3.org/1999/xhtml" property="og:image" content="https://ia.media-imdb.com/images/rock.jpg"/> -</apply-to> -</message> -``` - -The module is intentionally simple in the sense that it is basically a transport for https://ogp.me/ - -Configuration -------------- - -You can present an allowlist or denylist of domains for which OGP metadata will be fetched -via the `ogp_domain_allowlist` and `ogp_domain_denylist` settings repectively. - -For example: - -```lua -Component "muc.example.org" "muc" - modules_enabled = { "ogp" } - ogp_domain_allowlist = { "prosody.im" } -```
--- a/mod_omemo_all_access/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Disable access control for all OMEMO related PEP nodes' ---- - -Introduction -============ - -Traditionally OMEMO encrypted messages could only be exchanged after gaining mutual presence subscription due to the OMEMO key material being stored in PEP. - -XEP-0060 defines a method of changing the access model of a PEP node from `presence` to `open`. However Prosody does not yet support access models on PEP nodes. - -This module disables access control for all OMEMO PEP nodes (=all nodes in the namespace of `eu.siacs.conversations.axolotl.*`), giving everyone access to the OMEMO key material and allowing them to start OMEMO sessions with users on this server. - -Disco feature -============= - -This modules annouces a disco feature on the account to allow external tools such as the [Compliance Tester](https://conversations.im/compliance/) to check if this module has been installed. - - -Compatibility -============= - - ----- ----------------------------------------------------------------------------- - trunk Not needed, mod\_pep provides this feature already - 0.11 Not needed, mod\_pep provides this feature already - 0.10 Works - ----- -----------------------------------------------------------------------------
--- a/mod_onhold/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -labels: -summary: 'Module enabling "on-hold" functionality' -... - -Introduction -============ - -Enable mod\_onhold to allow temporarily placing messages from particular -JIDs "on hold" -- i.e. store them, but do not deliver them until the -hold status is taken away. - -Details -======= - -Right now, it is configured through adding JIDs to a list in -prosody.cfg.lua. Eventually, more dynamically configurable support will -be added (i.e. with ad-hoc commands or some such thing). - -Simply enable mod\_onhold in your list of modules, and then add a line: - -onhold\_jids = { "someone@address.com", "someoneelse@address2.com" } - -Until those JIDs are removed, messages from those JIDs will not be -delivered. Once they are removed and prosody is restarted, they will be -delivered the next time the user to which they are directed logs on.
--- a/mod_onions/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: s2s to Tor hidden services -... - -Introduction -============ - -This plugin allows Prosody to connect to other servers that are running -as a Tor hidden service. Running Prosody on a hidden service works -without this module, this module is only necessary to allow Prosody to -federate to hidden XMPP servers. - -For general info about creating a hidden service, see -[https://community.torproject.org/onion-services/setup/](https://community.torproject.org/onion-services/setup/). - -Usage -===== - -This module depends on the bit32 Lua library. - -To create a hidden service that can federate with other hidden XMPP -servers, first add a hidden serivce to Tor. It should listen on port -5269 and optionally also on 5222 (if c2s connections to the hidden -service should be allowed). - -Use the hostname that Tor gives with a virtualhost: - - VirtualHost "555abcdefhijklmn.onion" - modules_enabled = { "onions" }; - -Configuration -============= - - Name Description Type Default value - ---------------------- ----------------------------------------------------- --------- --------------- - onions\_socks5\_host the host to connect to for Tor's SOCKS5 proxy string "127.0.0.1" - onions\_socks5\_port the port to connect to for Tor's SOCKS5 proxy integer 9050 - onions\_only forbid all connection attempts to non-onion servers boolean false - onions\_tor\_all pass all s2s connections through Tor boolean false - onions\_map override the address for a host table {} - -By setting `onions_map`, it is possible to override the address used to -connect to a given host with the address of a hidden service. The -configuration of `onions_map` works as follows: - - onions_map = { - ["jabber.calyxinstitute.org"] = "ijeeynrc6x2uy5ob.onion"; - } - -or, to also specify a port: - - onions_map = { - ["jabber.calyxinstitute.org"] = { host = "ijeeynrc6x2uy5ob.onion", port = 5269 }; - } - -Compatibility -============= - - ----- -------------- - 0.8 Doesn't work - 0.9 Works - ----- -------------- - -Notes -===== - -- `onions_tor_all` does not look up SRV records first. Therefore it - will fail for many servers. -- mod\_onions currently does not support connecting to `.onion` - entries in SRV records. - -Security considerations -======================= - -- Running a hidden service on a server together with a normal server - might expose the hidden service. -- A hidden service that wants to remain hidden should either disallow - s2s to non-hidden servers or pass all s2s traffic through Tor - (setting either `onions_only` or `onions_tor_all`).
--- a/mod_openid/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,129 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Enables Prosody to act as an OpenID provider -... - -Introduction -============ - -[OpenID](http://openid.net/) is an decentralized authentication -mechanism for the Web. mod\_openid turns Prosody into an OpenID -*provider*, allowing users to use their Prosody credentials to -authenticate with various third party websites. - -Caveats -======= - -mod\_openid can best be described as a **proof-of-concept**, it has -known deficiencies and should **not** be used in the wild as a -legitimate OpenID provider. mod\_openid was developed using the Prosody -0.4.x series, it has not been tested with the 0.5.x or later series. - -Details -======= - -OpenID works on the basis of a user proving to a third-party they wish -to authenticate with, an OpenID *relaying party*, that they have claim -or ownership over a URL, known as an OpenID *identifier*. mod\_openid -uses Prosody's built in HTTP server to provide every user with an OpenID -identifier of the form `http://host.domain.tld[:port]/openid/user`, -which would be the OpenID identifier of the user with a Jabber ID of -`user@host.domain.tld`. - -Usage -===== - -Simply add "mod\_openid" to your modules\_enabled list. You may then use -the OpenID identifier form as described above as your OpenID identifier. -The port Prosody's HTTP server will listen on is currently set as 5280, -meaning the full OpenID identifier of the user `romeo@montague.lit` -would be `http://montague.lit:5280/openid/romeo`. - -Configuration -============= - -mod\_openid has no configuration options as of this time. - -TODO -==== - -The following is a list of the pending tasks which would have to be done -to make mod\_openid fully featured. They are generally ranked in order -of most importance with an estimated degree of difficulty. - -1. Support Prosody 0.6.x series (**Medium**) -2. Refactor code (**Medium**) - - The code is pretty messy at the moment, it should be refactored - to be more easily understood. - -3. Disable use of "user@domain" OpenID identifier form (*Easy*) - - This is a vestigial feature from the early design, allowing - explicit specification of the JID. However the JID can be - inferred from the simpler OpenID identifier form. - -4. Use a cryptographically secure Pseudo Random Number Generator (PRNG) - (**Medium**) - - This would likely be accomplished using luacrypto which provides - a Lua binding to the OpenSSL PRNG. - -5. Make sure OpenID key-value pairs get signed in the right order - (***Hard***) - - It is important that the OpenID key-value responses be signed in - the proper order so that the signature can be properly verified - by the receiving party. This may be complicated by the fact that - the iterative ordering of keys in a Lua table is not guaranteed - for non-integer keys. - -6. Do an actual match on the OpenID realm (**Medium**) - - The code currently always returns true for matches against an - OpenID realm, posing a security risk. - -7. Don't use plain text authentication over HTTP (***Hard***) - - This would require some Javascript to perform a digest. - -8. Return meaningful error responses (**Medium**) - - Most error responses are an HTTP 404 File Not Found, obviously - something more meaningful could be returned. - -9. Enable Association (***Hard***) - - Association is a feature of the OpenID specification which - reduces the number of round-trips needed to perform - authentication. - -10. Support HTTPS (**Medium**) - - With option to only allow authentication through HTTPS - -11. Enable OpenID 1.1 compatibility (**Medium**) - - mod\_openid is designed from the OpenID 2.0 specification, which - has an OpenID 1.1 compatibility mode. - -12. Check specification compliance (**Medium**) - - Walk through the code and make sure it complies with the OpenID - specification. Comment code as necessary with the relevant - sections in the specification. - -Once all these steps are done, mod\_openid could be considered to have -reached "beta" status and ready to real world use. The following are -features that would be nice to have in a stable release: - -1. Allow users to always trust realms (***Hard***) -2. Allow users to remain logged in with a cookie (***Hard***) -3. Enable simple registration using a user's vCard (**Medium**) -4. More useful user identity page (***Hard***) - - Allow users to alter what realms they trust and what simple - registration information gets sent to relaying parties by - default. - -5. OpenID Bot (***Hard***) - - Offers all functionality of the user identity page management - -6. Better designed pages (*Easy*) - - Use semantic XHTML and CSS to allow for custom styling. - - Use the Prosody favicon. - -Useful Links -============ - -- [OpenID Specifications](http://openid.net/developers/specs/) -- [OpenID on Wikipedia](http://en.wikipedia.org/wiki/OpenID)
--- a/mod_openid/mod_openid.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,448 +0,0 @@ -local usermanager = require "core.usermanager" -local httpserver = require "net.httpserver" -local jidutil = require "util.jid" -local hmac = require "hmac" - -local base64 = require "util.encodings".base64 - -local humane = require "util.serialization".serialize - --- Configuration -local base = "openid" -local openidns = "http://specs.openid.net/auth/2.0" -- [#4.1.2] -local response_404 = { status = "404 Not Found", body = "<h1>Page Not Found</h1>Sorry, we couldn't find what you were looking for :(" }; - -local associations = {} - -local function genkey(length) - -- FIXME not cryptographically secure - str = {} - - for i = 1,length do - local rand = math.random(33, 126) - table.insert(str, string.char(rand)) - end - - return table.concat(str) -end - -local function tokvstring(dict) - -- key-value encoding for a dictionary [#4.1.3] - local str = "" - - for k,v in pairs(dict) do - str = str..k..":"..v.."\n" - end - - return str -end - -local function newassoc(key, shared) - -- TODO don't use genkey here - local handle = genkey(16) - associations[handle] = {} - associations[handle]["key"] = key - associations[handle]["shared"] = shared - associations[handle]["time"] = os.time() - return handle -end - -local function split(str, sep) - local splits = {} - str:gsub("([^.."..sep.."]*)"..sep, function(c) table.insert(splits, c) end) - return splits -end - -local function sign(response, key) - local fields = {} - - for _,field in pairs(split(response["openid.signed"],",")) do - fields[field] = response["openid."..field] - end - - -- [#10.1] - return base64.encode(hmac.sha256(key, tokvstring(fields))) -end - -local function urlencode(s) - return (string.gsub(s, "%W", - function(str) - return string.format("%%%02X", string.byte(str)) - end)) -end - -local function urldecode(s) - return(string.gsub(string.gsub(s, "+", " "), "%%(%x%x)", - function(str) - return string.char(tonumber(str,16)) - end)) -end - -local function utctime() - local now = os.time() - local diff = os.difftime(now, os.time(os.date("!*t", now))) - return now-diff -end - -local function nonce() - -- generate a response nonce [#10.1] - local random = "" - for i=0,10 do - random = random..string.char(math.random(33,126)) - end - - local timestamp = os.date("%Y-%m-%dT%H:%M:%SZ", utctime()) - - return timestamp..random -end - -local function query_params(query) - if type(query) == "string" and #query > 0 then - if query:match("=") then - local params = {} - for k, v in query:gmatch("&?([^=%?]+)=([^&%?]+)&?") do - if k and v then - params[urldecode(k)] = urldecode(v) - end - end - return params - else - return urldecode(query) - end - end -end - -local function split_host_port(combined) - local host = combined - local port = "" - local cpos = string.find(combined, ":") - if cpos ~= nil then - host = string.sub(combined, 0, cpos-1) - port = string.sub(combined, cpos+1) - end - - return host, port -end - -local function toquerystring(dict) - -- query string encoding for a dictionary [#4.1.3] - local str = "" - - for k,v in pairs(dict) do - str = str..urlencode(k).."="..urlencode(v).."&" - end - - return string.sub(str, 0, -1) -end - -local function match_realm(url, realm) - -- FIXME do actual match [#9.2] - return true -end - -local function handle_endpoint(method, body, request) - module:log("debug", "Request at OpenID provider endpoint") - - local params = nil - - if method == "GET" then - params = query_params(request.url.query) - elseif method == "POST" then - params = query_params(body) - else - -- TODO error - return response_404 - end - - module:log("debug", "Request Parameters:\n"..humane(params)) - - if params["openid.ns"] == openidns then - -- OpenID 2.0 request [#5.1.1] - if params["openid.mode"] == "associate" then - -- Associate mode [#8] - -- TODO implement association - - -- Error response [#8.2.4] - local openidresponse = { - ["ns"] = openidns, - ["session_type"] = params["openid.session_type"], - ["assoc_type"] = params["openid.assoc_type"], - ["error"] = "Association not supported... yet", - ["error_code"] = "unsupported-type", - } - - local kvresponse = tokvstring(openidresponse) - module:log("debug", "OpenID Response:\n"..kvresponse) - return { - headers = { - ["Content-Type"] = "text/plain" - }, - body = kvresponse - } - elseif params["openid.mode"] == "checkid_setup" or params["openid.mode"] == "checkid_immediate" then - -- Requesting authentication [#9] - if not params["openid.realm"] then - -- set realm to default value of return_to [#9.1] - if params["openid.return_to"] then - params["openid.realm"] = params["openid.return_to"] - else - -- neither was sent, error [#9.1] - -- FIXME return proper error - return response_404 - end - end - - if params["openid.return_to"] then - -- Assure that the return_to url matches the realm [#9.2] - if not match_realm(params["openid.return_to"], params["openid.realm"]) then - -- FIXME return proper error - return response_404 - end - - -- Verify the return url [#9.2.1] - -- TODO implement return url verification - end - - if params["openid.claimed_id"] and params["openid.identity"] then - -- asserting an identifier [#9.1] - - if params["openid.identity"] == "http://specs.openid.net/auth/2.0/identifier_select" then - -- automatically select an identity [#9.1] - params["openid.identity"] = params["openid.claimed_id"] - end - - if params["openid.mode"] == "checkid_setup" then - -- Check ID Setup mode - -- TODO implement: NEXT STEP - local head = "<title>Prosody OpenID : Login</title>" - local body = string.format([[ -<p>Open ID Authentication<p> -<p>Identifier: <tt>%s</tt></p> -<p>Realm: <tt>%s</tt></p> -<p>Return: <tt>%s</tt></p> -<form method="POST" action="%s"> - Jabber ID: <input type="text" name="jid"/><br/> - Password: <input type="password" name="password"/><br/> - <input type="hidden" name="openid.return_to" value="%s"/> - <input type="submit" value="Authenticate"/> -</form> - ]], params["openid.claimed_id"], params["openid.realm"], params["openid.return_to"], base, params["openid.return_to"]) - - return string.format([[ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-type" content="text/html;charset=UTF-8" /> -%s -</head> -<body> -%s -</body> -</html> - ]], head, body) - elseif params["openid.mode"] == "checkid_immediate" then - -- Check ID Immediate mode [#9.3] - -- TODO implement check id immediate - end - else - -- not asserting an identifier [#9.1] - -- used for extensions - -- TODO implement common extensions - end - elseif params["openid.mode"] == "check_authentication" then - module:log("debug", "OpenID Check Authentication Mode") - local assoc = associations[params["openid.assoc_handle"]] - module:log("debug", "Checking Association Handle: "..params["openid.assoc_handle"]) - if assoc and not assoc["shared"] then - module:log("debug", "Found valid association") - local sig = sign(params, assoc["key"]) - - local is_valid = "false" - if sig == params["openid.sig"] then - is_valid = "true" - end - - module:log("debug", "Signature is: "..is_valid) - - openidresponse = { - ns = openidns, - is_valid = is_valid, - } - - -- Delete this association - associations[params["openid.assoc_handle"]] = nil - return { - headers = { - ["Content-Type"] = "text/plain" - }, - body = tokvstring(openidresponse), - } - else - module:log("debug", "No valid association") - -- TODO return error - -- Invalidate the handle [#11.4.2.2] - end - else - -- Some other mode - -- TODO error - end - elseif params["password"] then - -- User is authenticating - local user, domain = jidutil.split(params["jid"]) - module:log("debug", "Authenticating "..params["jid"].." ("..user..","..domain..") with password: "..params["password"]) - local valid = usermanager.validate_credentials(domain, user, params["password"], "PLAIN") - if valid then - module:log("debug", "Authentication Succeeded: "..params["jid"]) - if params["openid.return_to"] ~= "" then - -- TODO redirect the user to return_to with the openid response - -- included, need to handle the case if its a GET, that there are - -- existing query parameters on the return_to URL [#10.1] - local host, port = split_host_port(request.headers.host) - local endpointurl = "" - if port == '' then - endpointurl = string.format("http://%s/%s", host, base) - else - endpointurl = string.format("http://%s:%s/%s", host, port, base) - end - - local nonce = nonce() - local key = genkey(32) - local assoc_handle = newassoc(key) - - local openidresponse = { - ["openid.ns"] = openidns, - ["openid.mode"] = "id_res", - ["openid.op_endpoint"] = endpointurl, - ["openid.claimed_id"] = endpointurl.."/"..user, - ["openid.identity"] = endpointurl.."/"..user, - ["openid.return_to"] = params["openid.return_to"], - ["openid.response_nonce"] = nonce, - ["openid.assoc_handle"] = assoc_handle, - ["openid.signed"] = "op_endpoint,identity,claimed_id,return_to,assoc_handle,response_nonce", -- FIXME - ["openid.sig"] = nil, - } - - openidresponse["openid.sig"] = sign(openidresponse, key) - - queryresponse = toquerystring(openidresponse) - - redirecturl = params["openid.return_to"] - -- add the parameters to the return_to - if redirecturl:match("?") then - redirecturl = redirecturl.."&" - else - redirecturl = redirecturl.."?" - end - - redirecturl = redirecturl..queryresponse - - module:log("debug", "Open ID Positive Assertion Response Table:\n"..humane(openidresponse)) - module:log("debug", "Open ID Positive Assertion Response URL:\n"..queryresponse) - module:log("debug", "Redirecting User to:\n"..redirecturl) - return { - status = "303 See Other", - headers = { - Location = redirecturl, - }, - body = "Redirecting to: "..redirecturl -- TODO Include a note with a hyperlink to redirect - } - else - -- TODO Do something useful is there is no return_to - end - else - module:log("debug", "Authentication Failed: "..params["jid"]) - -- TODO let them try again - end - else - -- Not an Open ID request, do something useful - -- TODO - end - - return response_404 -end - -local function handle_identifier(method, body, request, id) - module:log("debug", "Request at OpenID identifier") - local host, port = split_host_port(request.headers.host) - - local user_name = "" - local user_domain = "" - local apos = string.find(id, "@") - if apos == nil then - user_name = id - user_domain = host - else - user_name = string.sub(id, 0, apos-1) - user_domain = string.sub(id, apos+1) - end - - user, domain = jidutil.split(id) - - local exists = usermanager.user_exists(user_name, user_domain) - - if not exists then - return response_404 - end - - local endpointurl = "" - if port == '' then - endpointurl = string.format("http://%s/%s", host, base) - else - endpointurl = string.format("http://%s:%s/%s", host, port, base) - end - - local head = string.format("<title>Prosody OpenID : %s@%s</title>", user_name, user_domain) - -- OpenID HTML discovery [#7.3] - head = head .. string.format('<link rel="openid2.provider" href="%s" />', endpointurl) - - local content = 'request.url.path: ' .. request.url.path .. '<br/>' - content = content .. 'host+port: ' .. request.headers.host .. '<br/>' - content = content .. 'host: ' .. tostring(host) .. '<br/>' - content = content .. 'port: ' .. tostring(port) .. '<br/>' - content = content .. 'user_name: ' .. user_name .. '<br/>' - content = content .. 'user_domain: ' .. user_domain .. '<br/>' - content = content .. 'exists: ' .. tostring(exists) .. '<br/>' - - local body = string.format('<p>%s</p>', content) - - local data = string.format([[ -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" -"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head> -<meta http-equiv="Content-type" content="text/html;charset=UTF-8" /> -%s -</head> -<body> -%s -</body> -</html> - ]], head, body) - return data; -end - -local function handle_request(method, body, request) - module:log("debug", "Received request") - - -- Make sure the host is enabled - local host = split_host_port(request.headers.host) - if not hosts[host] then - return response_404 - end - - if request.url.path == "/"..base then - -- OpenID Provider Endpoint - return handle_endpoint(method, body, request) - else - local id = request.url.path:match("^/"..base.."/(.+)$") - if id then - -- OpenID Identifier - return handle_identifier(method, body, request, id) - else - return response_404 - end - end -end - -httpserver.new{ port = 5280, base = base, handler = handle_request, ssl = false}
--- a/mod_password_reset/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Enables users to reset their password via a link' -rockspec: - build: - copy_directories: - - password_reset -... - -Introduction -============ - -This module allows users to reset their password via a simple link to a web page. - -Reset links may be generated by an admin through their XMPP client using the ad-hoc -command that this module provides. Alternatively other modules may reuse this module -to generate links and e.g. send them via email to the user directly. - -A link is only valid for a single reset, and expires after a duration (24 hours by default). - -This module depends on Prosody's internal webserver. - -Compatibility -============= - - ----- ------- - 0.10 Works - ----- ------- - trunk Works - ----- -------
--- a/mod_pastebin/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ ---- -labels: -- Stage-Stable -summary: Redirect long messages to built-in pastebin ---- - -# Introduction - -Pastebins are used very often in IM, especially in chat rooms. You have -a long log or command output which you need to send to someone over IM, -and don't want to fill their message window with it. Put it on a -pastebin site, and give them the URL instead, simple. - -Not for everyone... no matter how hard you try, people will be unaware, -or not care. They may also be too lazy to visit a pastebin. This is -where mod_pastebin comes in! - -# Details - -When someone posts to a room a "large" (the actual limit is -configurable) message, Prosody will intercept the message and convert it -to a URL pointing to a built-in pastebin server. The URLs are randomly -generated, so they can be considered for most purposes to be private, -and cannot be discovered by people who are not in the room. - -# Usage - -To set up mod_pastebin for MUC rooms it **must** be explicitly loaded, -as in the example below - it won't work when loaded globally, as that -will only load it onto normal virtual hosts. - -For example: - - Component "conference.example.com" "muc" - modules_enabled = { "pastebin" } - -Pastes will be available by default at -`http://<your-prosody>:5280/pastebin/` by default. - -In Prosody 0.9 and later this can be changed with [HTTP -settings](https://prosody.im/doc/http). - -In 0.8 and older this can be changed with `pastebin_ports` (see below), -or you can forward another external URL from your web server to Prosody, -use `pastebin_url` to set that URL. - -# Discovery - -The line and character tresholds are advertised in -[service discovery][xep-0030] like this: - -``` {.xml} -<iq id="791d37e8-86d8-45df-adc2-9bcb17c45cb7" type="result" xml:lang="en" from="prosody@conference.prosody.im"> - <query xmlns="http://jabber.org/protocol/disco#info"> - <identity type="text" name="Prosŏdy IM Chatroom" category="conference"/> - <feature var="http://jabber.org/protocol/muc"/> - <feature var="https://modules.prosody.im/mod_pastebin"/> - <x xmlns="jabber:x:data" type="result"> - <field type="hidden" var="FORM_TYPE"> - <value>http://jabber.org/protocol/muc#roominfo</value> - </field> - <field label="Title" type="text-single" var="muc#roomconfig_roomname"> - <value>Prosŏdy IM Chatroom</value> - </field> - <!-- etc... --> - <field type="text-single" var="{https://modules.prosody.im/mod_pastebin}max_lines"> - <value>12</value> - </field> - <field type="text-single" var="{https://modules.prosody.im/mod_pastebin}max_characters"> - <value>1584</value> - </field> - </x> - </query> -</iq> -``` - -# Configuration - - Option Description - ------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - pastebin_threshold Maximum length (in characters) of a message that is allowed to skip the pastebin. (default 500 characters) - pastebin_line_threshold The maximum number of lines a message may have before it is sent to the pastebin. (default 4 lines) - pastebin_trigger A string of characters (e.g. "!paste ") which if detected at the start of a message, always sends the message to the pastebin, regardless of length. (default: not set) - pastebin_expire_after Number of hours after which to expire (remove) a paste, defaults to 24. Set to 0 to store pastes permanently on disk. - pastebin_ports List of ports to run the HTTP server on, same format as mod_httpserver's http_ports[^1] - pastebin_url Base URL to display for pastebin links, must end with / and redirect to Prosody's built-in HTTP server[^2] - -# Compatibility - - ------ ------- - 0.11 Works - 0.10 Works - 0.9 Works - 0.8 Works - ------ ------- - -# Todo - -- Maximum paste length -- Web interface to submit pastes? - -[^1]: As of Prosody 0.9, `pastebin_ports` is replaced by `http_ports`, - see [Prosody HTTP server documentation](https://prosody.im/doc/http) - -[^2]: See also - [http_external_url](https://prosody.im/doc/http#external_url)
--- a/mod_pep_vcard_avatar/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Sync avatars between vCards and PEP ---- - -## Introduction - -This module pushes the users nickname and avatar from vCards into PEP, -or into vCards from PEP. This allows interop between older clients that -use [XEP-0153: vCard-Based Avatars] to see the avatars of clients that -use [XEP-0084: User Avatar] and vice versa. - -Also see [XEP-0398: User Avatar to vCard-Based Avatars Conversion]. - -## Configuration - -Simply [enable it like most other modules][doc:installing_modules#prosody-modules], -no further configuration needed. - -## Compatibility - - ------- -------------------------------- - trunk Use mod\_vcard\_legacy instead - 0.11 Use mod\_vcard\_legacy instead - 0.10 Works - 0.9 Does not work - 0.8 Does not work - ------- --------------------------------
--- a/mod_pep_vcard_png_avatar/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -Introduction -============ - -Conversations (an XMPP client for Android) is publishing PEP avatars in the webp file format. However Pidgin and other XMPP desktop clients can only show vcard avatars, that are in the PNG file format. This module is the [mod_pep_vcard_avatar](https://modules.prosody.im/mod_pep_vcard_avatar.html) module extended to also change the avatar file format to PNG. - -This module needs `dwebp` from `webp` package as an additional dependency. - -Configuration -============= - -Enable the module as any other: - - modules_enabled = { - "mod_pep_vcard_png_avatar"; - } - -You MUSTN'T load mod\_pep\_vcard\_avatar if this module is loaded. - -Compatibility -============= - - ----- ------------- - trunk Works - 0.10 Should work - 0.9 Should work - ----- ------------- - -
--- a/mod_persisthosts/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Introduction -============ - -This module creates stub configuration files for newly activated hosts. - -Configuration -============= - -A single option exists, `persisthosts_path`, which is the path where new -stub configuration files are created. It defaults to `"conf.d"`, and is -treated as relative to the configuration directiory [^1] unless set to -an absolute path. - -[^1]: usually `/etc/prosody` on \*nix systems
--- a/mod_poke_strangers/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Query the features and version of JIDs sending messages to contacts they are not subscribed to.' -... - -Introduction -============ - -In order to build heuristics for which messages are spam, it is necessary to -log as many details as possible about the spammers. This module sends a -version and disco query whenever a message is received from a JID to a user it -is not subscribed to. The results are printed to Prosody's log file at the -'info' level. Queried full JIDs are not queried again until Prosody restarts. - -The queries are sent regardless of whether the local user exists, so it does -not reveal if an account is active. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_post_msg/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,79 +0,0 @@ ---- -summary: 'Receives HTTP POST request, parses it and relays it into XMPP.' ---- - -Introduction -============ - -Sometimes it's useful to have different interfaces to access XMPP. - -This module allows sending XMPP -[`<message>`](https://xmpp.org/rfcs/rfc6121.html#message) stanzas via a -simple HTTP API. - -Example usage -------------- - - curl http://example.com:5280/msg/user -u me@example.com:mypassword -H "Content-Type: text/plain" -d "Server@host has just crashed!" - -This would send a message to user\@example.com from me\@example.com - -Details -======= - -URL format ----------- - - /msg/ [recipient [@host] ]. - -The base URL defaults to `/msg`. This can be configured via Prosodys -[HTTP path settings][doc:http]. - -Authentication --------------- - -Authentication is done by HTTP Basic. - - Authentication: Basic BASE64( "username@virtualhost:password" ) - -Payload formats ---------------- - -Supported formats are: - -`text/plain` -: The HTTP body is used as plain text message payload, in the `<body>` - element. - -`application/x-www-form-urlencoded` -: Allows more fields to be specified. - -`application/json` -: Similar to form data. - -Which one is selected via the `Content-Type` HTTP header. - -### Data fields - -The form data and JSON formats allow the following fields: - -`to` -: Can be used instead of having the receiver in the URL. - -`type` -: [Message type.](https://xmpp.org/rfcs/rfc6121.html#message-syntax-type) - -`body` -: Plain text message payload which goes in the `<body>` element. - -Acknowledgements -================ - -Some code originally borrowed from mod\_webpresence - -See also -======== - -[mod_rest] is a more advanced way to send messages and more via HTTP, -with a very similar API. -
--- a/mod_presence_cache/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -summary: Cache presence from remote users -... - -Introduction -============ - -This module stores a timestamp of the latest presence received from -users contacts so that the client can see who is online faster when they -sign in, and won't have to wait for remote servers to reply. - -Configuration -============= - -Just enable the module. - - modules_enabled = { - -- more modules - "presence_cache"; - } - -Advanced configuration -====================== - -The size of the cache is tuneable: - - presence_cache_size = 99 - -Compatibility -============= - -Requires 0.10 or later
--- a/mod_presence_dedup/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ ---- -summary: Presence deduplication module -labels: -- 'Stage-Alpha' -... - -This module tries to squash incoming identical presence stanzas to save -some bandwidth at the cost of increased memory use. - -Configuration -============= - - Option Type Default - ------------------------------ -------- --------- - presence\_dedup\_cache\_size number `100` - -The only setting controls how many presence stanzas *per session* are -kept in memory for comparing with incoming presenece. - -Requires Prosody 0.10 or later.
--- a/mod_privacy_lists/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ ---- -labels: -- Stage-Deprecated -summary: Privacy lists (XEP-0016) support ---- - -::: {.alert .alert-warning} -[XEP-0016 Privacy Lists] and this module has been deprecated, instead -use [mod_blocklist][doc:modules:mod_blocklist], included with Prosody. -::: - -Introduction ------------- - -Privacy lists are a flexible method for blocking communications. - -Originally known as mod\_privacy and bundled with Prosody, this module -was phased out in favour of the newer simpler blocking (XEP-0191) -protocol, implemented in [mod\_blocklist][doc:modules:mod_blocklist]. - -Configuration -------------- - -None. Each user can specify their privacy lists using their client (if -it supports XEP-0016). - -Compatibility -------------- - - ------ ------- - 0.9 Works - 0.10 Works - ------ -------
--- a/mod_private_adhoc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ---- -summary: Retrieve private XML data via adhoc command -... - -Introduction ------------- - -This is a very simple module which implements an adhoc commant -toretrieves the users private XML data. - -Details -------- - -Simple module which adds the adhoc command "Query private data" and will -return the users private XML data (typically muc bookmarks,.. ).
--- a/mod_privilege/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'XEP-0356 (Privileged Entity) implementation' -... - -Introduction -============ - -Privileged Entity is an extension which allows entity/component to have -privileged access to server (set/get roster, send message on behalf of server, -send IQ stanza on behalf of user, access presence information). It can be used -to build services independently of server (e.g.: PEP service). - -Details -======= - -You can have all the details by reading the -[XEP-0356](http://xmpp.org/extensions/xep-0356.html). - -Only the latest version of the XEP is implemented (using namespace -`urn:xmpp:privilege:2`), if your component use an older version, please update. - -Note that roster permission is not fully implemented yet, roster pushes are not yet sent -to privileged entity. - -Usage -===== - -To use the module, like usual add **"privilege"** to your -modules\_enabled. Note that if you use it with a local component, you -also need to activate the module in your component section: - - modules_enabled = { - [...] - - "privilege"; - } - - [...] - - Component "pubsub.yourdomain.tld" - component_secret = "yourpassword" - modules_enabled = {"privilege"} - -then specify privileged entities **in your host section** like that: - - VirtualHost "yourdomain.tld" - - privileged_entities = { - ["romeo@montaigu.lit"] = { - roster = "get"; - presence = "managed_entity"; - }, - ["juliet@capulet.lit"] = { - roster = "both"; - message = "outgoing"; - presence = "roster"; - }, - ["pubsub.yourdomain.tld"] = { - roster = "get"; - message = "outgoing"; - presence = "roster"; - iq = { - ["http://jabber.org/protocol/pubsub"] = "set"; - }; - }, - } - -Here *romeo@montaigu.lit* can **get** roster of anybody on the host, and will -**have presence for any user** of the host, while *juliet@capulet.lit* can -**get** and **set** a roster, **send messages** on behalf of the server, and -**access presence of anybody linked to the host** (not only people on the -server, but also people in rosters of users of the server). - -*pubsub.yourdomain.tld* is a Pubsub/PEP component which can **get** roster of -anybody on the host, **send messages** on the behalf of the server, **access -presence of anybody linked to the host**, and **send IQ stanza of type "set" for -the namespace "http://jabber.org/protocol/pubsub"** (this can be used to -implement XEP-0376 "Pubsub Account Management"). - -**/!\\ Be extra careful when you give a permission to an entity/component, it's -a powerful access, only do it if you absolutely trust the component/entity, and -you know where the software is coming from** - -Configuration -============= - -roster ------- - -All the permissions give access to all accounts of the virtual host. - - -------- ------------------------------------------------ ---------------------- - roster none *(default)* No access to rosters - get Allow **read** access to rosters - set Allow **write** access to rosters - both Allow **read** and **write** access to rosters - -------- ------------------------------------------------ ---------------------- - -Note that roster implementation is incomplete at the moment, roster pushes are not yet -send to privileged entity. - -message -------- - - ------------------ ------------------------------------------------------------ - none *(default)* Can't send message from server - outgoing Allow to send message on behalf of server (from bare jids) - ------------------ ------------------------------------------------------------ - -presence --------- - - ------------------ ------------------------------------------------------------------------------------------------ - none *(default)* Do not have extra presence information - managed\_entity Receive presence stanzas (except subscriptions) from host users - roster Receive all presence stanzas (except subsciptions) from host users and people in their rosters - ------------------ ------------------------------------------------------------------------------------------------ - -iq --- - -IQ permission is a table mapping allowed namespaces to allowed stanza type. When -a namespace is specified, IQ stanza of the specified type (see below) can be -sent if and only if the first child element of the IQ stanza has the specified -namespace. See https://xmpp.org/extensions/xep-0356.html#iq for details. - -Allowed stanza type: - - -------- ------------------------------------------- - get Allow IQ stanza of type **get** - set Allow IQ stanza of type **set** - both Allow IQ stanza of type **get** and **set** - -------- ------------------------------------------- - -Compatibility -============= - -If you use it with Prosody 0.9 and with a component, you need to patch -core/mod\_component.lua to fire a new signal. To do it, copy the -following patch in a, for example, /tmp/component.patch file: - -``` {.patch} - diff --git a/plugins/mod_component.lua b/plugins/mod_component.lua - --- a/plugins/mod_component.lua - +++ b/plugins/mod_component.lua - @@ -85,6 +85,7 @@ - session.type = "component"; - module:log("info", "External component successfully authenticated"); - session.send(st.stanza("handshake")); - + module:fire_event("component-authenticated", { session = session }); - - return true; - end -``` - -Then, at the root of prosody, enter: - -`patch -p1 < /tmp/component.patch` - - ----- -------------------------------------------------- - trunk Works - 0.12 Works - 0.11 Works - 0.10 Works - 0.9 Need a patched core/mod\_component.lua (see above) - ----- -------------------------------------------------- - -Note -==== - -This module is often used with mod\_delegation (c.f. XEP for more details)
--- a/mod_proctitle/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ ---- -summary: Set process name to prosody -... - -This module sets the process name to `prosody` so it shows up as such -instead of `lua` in process management tools. - -To use this module, you'll need the proctitle Lua library: -<https://github.com/hoelzro/lua-proctitle>
--- a/mod_profile/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: 'Replacement for mod\_vcard with vcard4 support and PEP integration' -superseded_by: mod_vcard_legacy ---- - -::: {.alert .alert-warning} -[mod\_vcard\_legacy][doc:modules:mod_vcard_legacy] and -[mod\_vcard4][doc:modules:mod_vcard4] included with Prosody 0.11.x -provide equivalent functionality. -::: - -# Introduction - -This module was an experimental replacement for [mod\_vcard]. In addition to -the ageing protocol defined by [XEP-0054], it also supports the [new -vCard 4 based protocol][xep0292] and integrates with [Personal -Eventing Protocol][xep0163]. - -Also supports [XEP-0398: User Avatar to vCard-Based Avatars Conversion]. - -The vCard 4, [User Avatar][xep0084] and [User Nickname][xep0172] -PEP nodes are updated when the vCard is changed.. - -# Configuration - - modules_enabled = { - -- "vcard"; -- This module must be removed - - "profile"; - } - -# Compatibility - -Requires Prosody **trunk** as of 2014-05-29. Won't work in 0.10.x. - -It depends on the trunk version of [mod\_pep][doc:modules:mod_pep] for PEP support, -previously known as [mod\_pep\_plus][doc:modules:mod_pep_plus].
--- a/mod_prometheus/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ ---- -labels: -- Statistics -summary: Implementation of the Prometheus protocol -... - -Description -=========== - -This module implements the Prometheus reporting protocol, allowing you -to collect statistics directly from Prosody into Prometheus. - -See the [Prometheus documentation][prometheusconf] on the format for -more information. - -[prometheusconf]: https://prometheus.io/docs/instrumenting/exposition_formats/ - -::: {.alert .alert-info} -**Note:** For use with Prosody trunk (0.12) we recommend the bundled -[mod_http_openmetrics](https://prosody.im/doc/modules/mod_http_openmetrics) -instead. This module (mod_prometheus) will continue to be available in the -community repository for use with older Prosody versions. -::: - -Configuration -============= - -mod\_prometheus itself doesn’t have any configuration option, but it -requires Prosody’s [internal statistics -provider](https://prosody.im/doc/statistics#built-in_providers) to be -enabled. You may also want to change the default collection interval -to the one your statistics consumer is using. See below for more information. - -```lua -statistics = "internal" -statistics_interval = 15 -- in seconds -``` - -::: {.alert .alert-warning} -**NOTE:** Make sure to put the statistics variables in the global section of -the configuration, **not** in a `VirtualHost` or `Component` section. You can -use `prosodyctl check` if you are unsure and want to check your configuration. -::: - -See also the documentation of Prosody’s [HTTP -server](https://prosody.im/doc/http), since Prometheus is an HTTP -protocol that is how you can customise its URL. The default one being -http://localhost:5280/metrics - -Scrape interval vs statistics_interval --------------------------------------- - -The `statistics_interval` should be set to `"manual"` on trunk if and only -if you have a single Prometheus instance scraping Prosody. This will allow -the internal statistics gathering to run optimally. - -If you have multiple instances scraping Prosody, set `statistics_interval` -to the scrape interval of Prometheus to avoid errors in rate calculations -and similar. - -Future work will allow the use of `"manual"` with multiple Prometheus -instances and varying scrape intervals (stay tuned). - -Compatibility -============= - - ------- ------------- - trunk Works (but replaced by [mod_http_openmetrics](https://prosody.im/doc/modules/mod_http_openmetrics)) - 0.11 Works - 0.10 Works - 0.9 Does not work - ------- -------------
--- a/mod_proxy65_whitelist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: Limit which file transfer users can use -... - -Introduction ------------- - -This module attempts to restrict use of non-whitelisted XEP-0065 -proxies. - -Configuration -------------- - -Without any options, the module will restrict users to local [proxy65 -components](https://prosody.im/doc/modules/mod_proxy65). - - -- additional proxies to allow - allowed_streamhosts = { "proxy.eu.jabber.org" } - -The module will add all local proxies to that list. To prevent it from -doing that, set - - allow_local_streamhosts = false
--- a/mod_pubsub_eventsource/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ ---- -labels: -- Stage-Beta -summary: Subscribe to pubsub nodes using the HTML5 EventSource API -... - -Introduction ------------- - -[Server-Sent Events](https://en.wikipedia.org/wiki/Server-sent_events) -is a simple HTTP/line-based protocol supported in HTML5, making it easy -to receive a stream of "events" in realtime using the Javascript -[EventSource -API](https://developer.mozilla.org/en-US/docs/Web/API/EventSource). - -EventSource is supported in [most modern -browsers](http://caniuse.com/#feat=eventsource), and for the remainder -there are 'polyfill' compatibility layers such as -[EventSource.js](https://github.com/remy/polyfills/blob/master/EventSource.js) -and [jquery.eventsource](https://github.com/rwldrn/jquery.eventsource). - -Details -------- - -Subscribing to a node from Javascript is easy: - - var source = new EventSource('http://pubsub.example.org:5280/eventsource/mynode'); - source.onmessage = function (event) { - console.log(event.data); // Do whatever you want with the data here - }; - -### Cross-domain issues - -The same cross-domain restrictions apply to EventSource that apply to -BOSH, and support for CORS is not clearly standardized yet. You may want -to proxy connections through your web server for this reason. See [BOSH: -Cross-domain -issues](https://prosody.im/doc/setting_up_bosh#proxying_requests) for -more information. - -Configuration -------------- - -There is no special configuration for this module. Simply load it onto a -pubsub component like so: - - Component "pubsub.example.org" "pubsub" - modules_enabled = { "pubsub_eventsource" } - -As it uses HTTP to serve the event streams, you can use Prosody's -standard [HTTP configuration options](https://prosody.im/doc/http) to -control how/where the streams are served. - -**Note about URLs:** It is important to get the event streams from the -correct hostname (that of the pubsub host). An example stream URL is -`http://pubsub.example.org:5280/eventsource/mynode`. If you need to -access the streams using another hostname (e.g. `example.org`) you can -use the `http_host` option under the Component, e.g. -`http_host = "example.org"`. For more information see the ['Virtual -Hosts'](https://prosody.im/doc/http#virtual_hosts) section of our HTTP -documentation. - -Compatibility -------------- - - ------- -------------- - 0.9 Works - 0.8 Doesn't work - Trunk Works - ------- --------------
--- a/mod_pubsub_feeds/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -summary: Subscribe to Atom and RSS feeds over pubsub -rockspec: - build: - modules: - mod_pubsub_feeds.feeds: feeds.lib.lua ---- - -# Introduction - -This module allows Prosody to fetch Atom and RSS feeds for you, and push -new results to subscribers over XMPP. - -# Configuration - -This module needs to be be loaded together with -[mod\_pubsub][doc:modules:mod\_pubsub]. - -For example, this is how you could add it to an existing pubsub -component: - -``` lua -Component "pubsub.example.com" "pubsub" -modules_enabled = { "pubsub_feeds" } - -feeds = { - -- The part before = is used as PubSub node - planet_jabber = "http://planet.jabber.org/atom.xml"; - prosody_blog = "http://blog.prosody.im/feed/atom.xml"; -} -``` - -This example creates two nodes, 'planet\_jabber' and 'prosody\_blog' -that clients can subscribe to using -[XEP-0060](http://xmpp.org/extensions/xep-0060.html). Results are in -[ATOM 1.0 format](http://atomenabled.org/) for easy consumption. - -# WebSub {#pubsubhubbub} - -This module also implements [WebSub](https://www.w3.org/TR/websub/), -formerly known as -[PubSubHubbub](http://pubsubhubbub.googlecode.com/svn/trunk/pubsubhubbub-core-0.3.html). -This allows "feed hubs" to instantly push feed updates to subscribers. - -This may be removed in the future since it does not seem to be oft used -anymore. - -# Option summary - - Option Description - ------------------------------ -------------------------------------------------------------------------- - `feeds` A list of virtual nodes to create and their associated Atom or RSS URL. - `feed_pull_interval_seconds` Number of seconds between polling for new results (default 15 *minutes*) - `use_pubsubhubub` Set to `true` to enable WebSub - -# Compatibility - - ------ ------- - 0.12 Works - 0.11 Works - ------ -------
--- a/mod_pubsub_github/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Publish Github commits over pubsub ---- - -## Introduction - -This module accepts Github web hooks and publishes them to a local -pubsub component for XMPP clients to subscribe to. - -Entries are pushed as Atom payloads. - -It may also work with Gitlab. - -## Configuration - -Load the module on a pubsub component: - -``` {.lua} -Component "pubsub.example.com" "pubsub" - modules_enabled = { "pubsub_github" } - github_secret = "NP7bZooYSLKze96TQMpFW5ov" -``` - -The URL for Github to post to would be either: - -- `http://pubsub.example.com:5280/pubsub_github` -- `https://pubsub.example.com:5281/pubsub_github` - -The module also takes the following config options: - - Name Default Description - ----------------------- ------------------- ------------------------------------------------------------ - `github_node` `"github"`{.lua} The pubsub node to publish commits on. - `github_secret` **Required** Shared secret used to sign HTTP requests. - `github_node_prefix` `"github/"`{.lua} - `github_node_mapping` *not set* Field in repository object to use as node instead of `github_node` - `github_actor` *superuser* Which actor to do the publish as (used for access control) - -More advanced example - -``` {.lua} -Component "pubsub.example.com" "pubsub" - modules_enabled = { "pubsub_github" } - github_actor = "github.com" - github_node_mapping = "name" --> github_node_prefix .. "repo" - -- github_node_mapping = "full_name" --> github_node_prefix .. "owner/repo" - github_secret = "sekr1t" -``` - -If your HTTP host doesn't match the pubsub component's address, you will -need to inform Prosody. For more info see Prosody's [HTTP server -documentation](https://prosody.im/doc/http#virtual_hosts). - -## Compatibility - - ------ ------------- - 0.10 Should work - 0.9 Works - ------ -------------
--- a/mod_pubsub_hub/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -summary: PubSubHubbub hub -... - -Introduction -============ - -This module implements a -[PubSubHubbub](http://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.3.html) -(PuSH) hub, allowing PuSH clients to subscribe to local XMPP -[Publish-Subscribe](http://xmpp.org/extensions/xep-0060.html) nodes -stored by [mod\_pubsub](http://prosody.im/doc/modules/mod_pubsub) and -receive real time updates to feeds. - -Configuration -============= - - Component "pubsub.example.com" "pubsub" - - modules_enabled = { - "pubsub_hub"; - } - -The hub is then available on `http://pubsub.example.com:5280/hub`. - -Compatibility -============= - - ------- -------------- - trunk Works - 0.10 Should work - 0.9 Works - 0.8 Doesn't work - ------- -------------- - -
--- a/mod_pubsub_mqtt/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'MQTT interface to Prosody''s pubsub' -... - -Introduction ------------- - -[MQTT](http://mqtt.org/) is a lightweight binary pubsub protocol suited -to embedded devices. This module provides a way for MQTT clients to -connect to Prosody and publish or subscribe to local pubsub nodes. - -The module currently implements MQTT version 3.1.1. - -Details -------- - -MQTT has the concept of 'topics' (similar to XMPP's pubsub 'nodes'). -mod\_pubsub\_mqtt maps pubsub nodes to MQTT topics of the form -`<HOST>/<TYPE>/<NODE>`, e.g.`pubsub.example.org/json/mynode`. - -The 'TYPE' parameter in the topic allows the client to choose the payload -format it will send/receive. For the supported values of 'TYPE' see the -'Payloads' section below. - -### Limitations - -The current implementation is quite basic, and in particular: - -- Authentication is not supported -- Only QoS level 0 is supported - -### Payloads - -XMPP payloads are always XML, but MQTT does not define a payload format. -Therefore mod\_pubsub\_mqtt has some built-in data format translators. - -Currently supported data types: - -- `json`: See [XEP-0335](http://xmpp.org/extensions/xep-0335.html) for - the format. -- `utf8`: Plain UTF-8 text (wrapped inside - `<data xmlns="https://prosody.im/protocol/mqtt"/>`) -- `atom_title`: Returns the title of an Atom entry as UTF-8 data - -Configuration -------------- - -There is no special configuration for this module. Simply load it on -your pubsub host like so: - - Component "pubsub.example.org" "pubsub" - modules_enabled = { "pubsub_mqtt" } - -You may also configure which port(s) mod\_pubsub\_mqtt listens on using -Prosody's standard config directives, such as `mqtt_ports` and -`mqtt_tls_ports`. Network settings **must** be specified in the global section -of the config file, not under any particular pubsub component. The default -port is 1883 (MQTT's standard port number) and 8883 for TLS connections. - -Compatibility -------------- - - ------- -------------- - trunk Works - 0.12 Works - ------- --------------
--- a/mod_pubsub_post/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,157 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Publish to PubSub nodes from via HTTP POST/WebHooks ---- - -# Introduction - -This module is a fairly generic WebHook receiver that lets you easily -publish data to PubSub using a HTTP POST request. The payload can be -Atom feeds, arbitrary XML, or arbitrary JSON. The type should be -indicated via the `Content-Type` header. - -- JSON data is wrapped in a [XEP-0335] container. -- An Atom feed may have many `<entry>` and each one is published as - its own PubSub item. -- Other XML is simply published to the item with ID `current`. - -## JSON example - -``` {.bash} -curl http://localhost:5280/pubsub_post/princely_musings \ - -H "Content-Type: application/json" \ - --data-binary '{"musing":"To be, or not to be: that is the question"}' -``` - -## Atom example - -``` {.bash} -curl http://localhost:5280/pubsub_post/princely_musings \ - -H "Content-Type: application/xml" \ - --data-binary '<feed xmlns="http://www.w3.org/2005/Atom"> - <entry><title>Hello</title></entry></feed>' -``` - -## Simple form-data - -``` {.bash} -curl http://localhost:5280/pubsub_post/princely_musings \ - --data musing="To be, or not to be: that is the question" -``` - -# Configuration - -All settings are optional. - -## Actor identification - -First we have to figure out who is making the request. -This is configured on a per-node basis like this: - -``` {.lua} --- Per node secrets -pubsub_post_actors = { - princely_musings = "hamlet@denmark.lit" -} -pubsub_post_default_actor = "nobody@nowhere.invalid" -``` - -`pubsub_post_default_actor` is used when trying to publish to a node -that is not listed in `pubsub_post_actors`. Otherwise the IP address -of the connection is used. - -## Authentication - -[WebSub](https://www.w3.org/TR/2018/REC-websub-20180123/) [Authenticated -Content -Distribution](https://www.w3.org/TR/2018/REC-websub-20180123/#authenticated-content-distribution) -authentication is used. - -``` {.lua} -pubsub_post_secrets = { - princely_musings = "shared secret" -} -pubsub_post_default_secret = "default secret" -``` - -`pubsub_post_default_secret` is used when trying to publish to a node -that is not listed in `pubsub_post_secrets`. Otherwise the request -proceeds with the previously identified actor. - -::: {.alert .alert-danger} -If configured without a secret and a default actor that has permission -to create nodes the service becomes wide open. -::: - -## Authorization - -Authorization is handled via pubsub affiliations. Publishing requires an -affiliation with the _publish_ capability, usually `"publisher"`. - -### Setting up affiliations - -Prosodys PubSub module supports [setting affiliations via -XMPP](https://xmpp.org/extensions/xep-0060.html#owner-affiliations), -since 0.11.0, so affiliations can be configured with a capable client. - -It can however be done from another plugin: - -``` {.lua} -local mod_pubsub = module:depends("pubsub"); -local pubsub = mod_pubsub.service; - -pubsub:create("princely_musings", true); -pubsub:set_affiliation("princely_musings", true, "127.0.0.1", "publisher"); -``` - -## Data mappings - -The datamapper library added in 0.12.0 allows posting JSON and having it -converted to XML based on a special JSON Schema. - -``` json -{ - "properties" : { - "content" : { - "type" : "string" - }, - "title" : { - "type" : "string" - } - }, - "type" : "object", - "xml" : { - "name" : "musings", - "namespace" : "urn:example:princely" - } -} -``` - -And in the Prosody config file: - -``` lua -pubsub_post_mappings = { - princely_musings = "musings.json"; -} -``` - -Then, POSTing a JSON payload like - -``` json -{ - "content" : "To be, or not to be: that is the question", - "title" : "Soliloquy" -} -``` - -results in a payload like - -``` xml -<musings xmlns="urn:example:princely"> - <title>Soliloquy</title> - <content>To be, or not to be: that is the question</content> -</musings> -``` - -being published to the node.
--- a/mod_pubsub_serverinfo/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ ---- -labels: -- 'Statistics' -... - -Exposes server information over Pub/Sub per [XEP-0485: PubSub Server Information](https://xmpp.org/extensions/xep-0485.html). - -The module announces support (used to 'opt-in', per the XEP) and publishes the name of the local domain via a Pub/Sub node. The published data -will contain a 'remote-domain' element for inbound and outgoing s2s connections. These elements will be named only when the remote domain announces -support ('opts in') too. - -**Known issues:** - -- [Issue #1841](https://issues.prosody.im/1841): This module conflicts with mod_server_contact_info (both will run, but it may affect the ability of some implementations to read the server/contact information provided). - -Installation -============ - -Enable this module in the global or a virtual host. - -The default configuration requires the existence of a Pub/Sub component that uses the 'pubsub' subdomain of the host in which the module is enabled: - - Component "pubsub.example.org" "pubsub" - -The module will create a node and publish data, using a JID that matches the XMPP domain name of the host. Ensure that this actor is an admin of the -Pub/Sub service: - - admins = { "example.org" } - -Configuration -============= - -The Pub/Sub service on which data is published, by default, is a component addressed as the `pubsub` subdomain of the domain of the virtual host that -the module is loaded under. To change this, apply this configuration setting: - - pubsub_serverinfo_service = "anotherpubsub.example.org" - -The Pub/Sub node on which data is published is, by default, a leaf-node named `serverinfo`. To change this, apply this configuration setting: - - pubsub_serverinfo_node = "foobar" - -To prevent a surplus of event notifications, this module will only publish new data after a certain period of time has expired. The default duration -is 300 seconds (5 minutes). To change this simply put in the config: - - pubsub_serverinfo_publication_interval = 180 -- or any other number of seconds - -To detect if remote domains allow their domain name to be included in the data that this module publishes, this module will perform a service -discovery request to each remote domain. To prevent a continuous flood of disco/info requests, the response to these requests is cached. By default, -a cached value will remain in cache for one hour. This duration can be modified by adding this configuration option: - - pubsub_serverinfo_cache_ttl = 1800 -- or any other number of seconds - -Compatibility -============= - -Incompatible with 0.11 or lower. - -Known Issues / TODOs -==================== - -The reported data does not contain the optional 'connection' child elements. These can be used to describe the direction of a connection. - -More generic server information (eg: user counts, software version) should be included in the data that is published.
--- a/mod_pubsub_stats/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ ---- -labels: -- 'Statistics' -... - -This module publishes data from [Prosody's `internal` statistics][doc:statistics] - into a PubSub node in [XEP-0039] format. - -The node defaults to `stats` but can be changed with the option -`pubsub_stats_node`. - -``` lua -Component "pubsub.example.com" "pubsub" -modules_enabled = { - "pubsub_stats"; -} -pubsub_stats_node = "statistics" -```
--- a/mod_pubsub_subscription/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -# Introduction - -This module lets you programmatically subscribe to updates from a -[pubsub][xep0060] node, even if the pubsub service is remote. - -## Example - -``` {.lua} -module:depends("pubsub_subscription"); -module:add_item("pubsub-subscription", { - service = "pubsub.example.com"; - node = "otter_facts"; - - -- Callbacks: - on_subscribed = function() - module:log("info", "Otter facts incoming!"); - end; - - on_item = function(event) - module:log("info", "Random Otter Fact: %s", event.payload:get_text()); - end; -}); -``` - -## Usage - -Ensure the module is loaded and add your subscription via the -`:add_item` API. The item table MUST have `service` and `node` fields -and SHOULD have one or more `on_<event>` callbacks. - -The JID of the pubsub service is given in `service` (could also be the -JID of an user for advanced PEP usage) and the node is given in, -unsurprisingly, the `node` field. - -The various `on_event` callback functions, if present, gets called when -new events are received. The most interesting would be `on_item`, which -receives incoming items. Available events are: - -`on_subscribed` -: The subscription was successful, events may follow. - -`on_unsubscribed` -: Subscription was removed successfully, this happens if the - subscription is removed, which you would normally never do. - -`on_error` -: If there was an error subscribing to the pubsub service. Receives a - table with `type`, `condition`, `text`, and `extra` fields as - argument. - -`on_item` -: An item publication, the payload itself available in the `payload` - field in the table provided as argument. The ID of the item can be - found in `item.attr.id`. - -`on_retract` -: When an item gets retracted (removed by the publisher). The ID of - the item can be found in `item.attr.id` of the table argument.. - -`on_purge` -: All the items were removed by the publisher. - -`on_delete` -: The entire pubsub node was removed from the pubsub service. No - subscription exists after this. - -``` {.lua} -event_payload = { - -- Common prosody event entries: - stanza = util.stanza; - origin = util.session; - - -- PubSub service details - service = "pubsub.example.com"; - node = "otter_facts"; - - -- The pubsub event itself - item = util.stanza; -- <item/> - payload = util.stanza; -- actual payload, child of <item/> -} -``` - -# Compatibility - -Should work with Prosody \>= 0.11.x
--- a/mod_pubsub_text_interface/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -# Introduction - -This module lets you manage subscriptions to pubsub nodes via simple -chat messages. Subscriptions are always added based on bare JID. The -`include_body` flag is enabled so that a plain text body version of -events can be included, where supported. - -# Configuring - -``` {.lua} -Component "pubsub.example.com" "pubsub" -modules_enabled = { - "pubsub_text_interface", -} -``` - -# Commands - -The following commands are supported. Simply send a normal chat message -to the PubSub component where this module is enabled. When subscribing -or unsubscribing, be sure to replace `node` with the node you want to -subscribe to or unsubscribe from. - -- `help` - a help message, listing these commands -- `list` - list available nodes -- `subscribe node` - subscribe to a node -- `unsubscribe node` - unsubscribe from a node -- `last node` - sends the last published item from the node to you - -# Compatibility - -Should work with Prosody since 0.9, when -[mod\_pubsub][doc:modules:mod_pubsub] was introduced. - -The `last` command is available from Prosody 0.11.
--- a/mod_pubsub_twitter/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Subscribe to Twitter search queries over pubsub -... - -Introduction ------------- - -Twitter has an open 'realtime' search API, but it requires polling -(within their rate limits). This module allows Prosody to poll for you, -and push new results to subscribers over XMPP. - -Configuration -------------- - -This module must be loaded on a Prosody pubsub component. Add it to -`modules_enabled` and configure like so: - - Component "pubsub.example.com" "pubsub" - modules_enabled = { "pubsub_twitter" } - - twitter_searches = { - realtime = "xmpp OR realtime"; - prosody = "prosody xmpp"; - } - -This example creates two nodes, 'realtime' and 'prosody' that clients -can subscribe to using -[XEP-0060](http://xmpp.org/extensions/xep-0060.html). Results are in -[ATOM 1.0 format](http://atomenabled.org/) for easy consumption. - - Option Description - ------------------------- -------------------------------------------------------------------------------- - twitter\_searches A list of virtual nodes to create and their associated Twitter search queries. - twitter\_pull\_interval Number of minutes between polling for new results (default 20) - twitter\_search\_url URL of the JSON search API, default: "http://search.twitter.com/search.json" - -Compatibility -------------- - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_push2/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: 'Push 2.0' ---- - -The way forward for push notifications? You are probably looking for -`mod_cloud_notify` for now though - -See also https://hg.prosody.im/prosody-modules/file/tip/mod_push2/push2.markdown - -Configuration -============= - - Option Default Description - ------------------------------------ ----------------- ------------------------------------------------------------------------------------------------------------------- - `contact_uri` xmpp:server.tld Contact information for the server operator (usually as a `mailto:` URI is preferred) - `push_max_hibernation_timeout` `259200` (72h) Number of seconds to extend the smacks timeout if no push was triggered yet (default: 72 hours) - -Internal design notes -===================== - -App servers are notified about offline messages, messages stored by [mod_mam] -or messages waiting in the smacks queue. - -To cooperate with [mod_smacks] this module consumes some events: -`smacks-ack-delayed`, `smacks-hibernation-start` and `smacks-hibernation-end`. -These events allow this module to send out notifications for messages received -while the session is hibernated by [mod_smacks] or even when smacks -acknowledgements for messages are delayed by a certain amount of seconds -configurable with the [mod_smacks] setting `smacks_max_ack_delay`. - -The `smacks_max_ack_delay` setting allows to send out notifications to clients -which aren't already in smacks hibernation state (because the read timeout or -connection close didn't already happen) but also aren't responding to acknowledgement -request in a timely manner. This setting thus allows conversations to be smoother -under such circumstances. - -Compatibility -============= - -**Note:** This module should be used with Lua 5.3 and higher. - -Requires a slightly patches luaossl right now: https://github.com/wahern/luaossl/pull/214 - ------- ----------------------------------------------------------------------------- - trunk Works ------- -----------------------------------------------------------------------------
--- a/mod_push2/push2.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -# Push 2.0 - -Adapted from notes made at [XMPP Summit 25](https://pad.nixnet.services/oy6MKVbESSycLeMJIOh6zw). - -Requirements: - -- Support for SASL2 inlining -- Extensible stanza matching rules and notification payload rules -- Simpler syntax and concept model than original specification - -## Client registers to receive push notifications - -```xml -<enable xmlns='urn:xmpp:push2:0'> - <service>pusher@push.example.com</service> - <client>https://push.example.com/adlfkjadafdasf</client> - <match profile="urn:xmpp:push2:match:archived-with-body"> - <send xmlns="urn:xmpp:push2:send:notify-only:0"/> - </match> -</enable> -``` - -The `<service/>` element contains a JID which push notifications for this client will be sent to. It may be a host, bare or full JID. - -The `<client/>` element contains an opaque string that will be included in all communication with the push service. It may be used to convey client identifiers used by the push notification service to route notifications. - -The `<match/>` and `<send/>` elements define what profiles to use for matching stanzas and sending notifications. These are described later in this document. - -## Match and send profiles - -Different clients and push services have different requirements for push notifications, often due to the differing capabilities of target platforms. - -A "profile" in the context of this specification is a set of rules for matching the kinds of stanzas that should be pushed, and how to transform them before sending the notification to the push service. - -### Match profiles - -Match profiles define which incoming stanzas will trigger a push notification. More than one match may be specified. - -Some match profiles are defined in this XEP. Other XEPs may define additional profiles with the reserved `urn:xmpp:push2:match:` prefix, following the registrar considerations explained later in this document. Custom profiles not defined in a XEP should use their own appropriate URI. - -#### `urn:xmpp:push2:match:all` - -Using this profile, all stanzas will trigger a push notification to be sent to the push service when the client is unavailable. - -#### `urn:xmpp:push2:match:important` - -Stanzas that are considered to be "important" are pushed. At the time of writing, there is no standard definition of "important", however most servers already contain such logic for traffic optimization when combined with [XEP-0352: Client State Indication](https://xmpp.org/extensions/xep-0352.html). - -#### `urn:xmpp:push2:match:archived` - -Push notifications will be sent for any stanza that is stored in the user's archive. This is a good indication that the stanza is important, and is desired on all of a user's devices. - -#### `urn:xmpp:push2:match:archived-with-body` - -Matches only archived messages that contain a body. This can be used to exclude certain message types, such as typing notifications, receipts and error replies. - -### Send profiles - -When a server has determined that a stanza should trigger a push notification (according to the client's selected 'match' profile), it proceeds to create a notification stanza following the send profiles specified in the match profiles which match this stanza. - -After constructing the notification stanza, it will then be sent to the push service JID selected by the client. - -Some send profiles are defined in this XEP. Other XEPs may define additional profiles with the `urn:xmpp:push2:send:` prefix, following the registrar considerations explained later in this document. Custom profiles not defined in a XEP should use their own appropriate URI. - -#### `urn:xmpp:push2:send:notify-only:0` - -Send an empty notification to the push service. Such notifications are useful if a push notification can trigger the client to "wake up" and connect to the server to receive the message over XMPP. - -Example: - -```xml -<message to="pusher@push.example.net"> - <notification xmlns="urn:xmpp:push2:0"> - <client>https://push.example.com/adlfkjadafdasf</client> - <priority>normal</priority> - </notification> -</message> -``` - -#### `urn:xmpp:push2:send:sce+rfc8291+rfc8292:0` - -Delivers content encrypted according to RFC8291 and with a JWT auth following RFC8292 - -```xml -<send xmlns="urn:xmpp:push2:send:sce+rfc8291+rfc8292:0"> - <ua-public>Base64 encoded P-256 ECDH public key (raw, uncompressed)</ua-public> - <auth-secret>Base64 encoded randomly generated 16 octets</auth-secret> - <jwt-alg>ES256</jwt-alg> - <jwt-key>PKCS#8 PEM encoded ECDSA keypair, without the header or footer lines</jwt-key> - <jwt-claim name="aud">https://push.example.com</jwt-claim> -</send> -``` - -The full stanza is wrapped in XEP-0297 forwarded and then that is wrapped in XEP-0420 envelope/content with optional rpad. The raw bytes of the resulting XML are encrypted according to RFC8291 using the provided `ua-public` and `auth-secret`. - -If `jwt-alg` is specified, then a JWT is computed over any provided claims plus a suitable `exp` and `sub` claim and signed using the provided key. - -Then a notification is sent: - -```xml -<message to="pusher@push.example.net"> - <notification xmlns="urn:xmpp:push2:0"> - <client>https://push.example.com/adlfkjadafdasf</client> - <priority>normal</priority> - <encrypted xmlns="urn:xmpp:sce:rfc8291:0"> - <payload>Base64 encoded ciphertext</payload> - </encrypted> - <jwt key="base64 encoded raw public key">the signed JWT, if present</jwt> - </notification> -</message> -``` - -NOTE: if the stanza exceeds the maximum size of 4096 bytes (and some implementations may wish to restrict this even more) the stanza may have some elements removed, body truncated, etc before it is delivered. Servers SHOULD ensure that at least the MAM id (if there is one) is still present after any minimization. - -## Discovering support - -A server that supports this protocol MUST advertise the `urn:xmpp:push2:0` feature in an account's service discovery information, along with the supported match and send profiles. - -```xml -<iq from='juliet@capulet.lit' - to='juliet@capulet.lit/balcony' - id='disco1' - type='result'> - <query xmlns='http://jabber.org/protocol/disco#info'> - <identity category='account' type='registered'/> - <feature var='urn:xmpp:push2:0'/> - <feature var='urn:xmpp:push2:send:'/> - </query> -</iq> -``` - -## Push service interactions - -### Transient delivery errors - -The user's server might receive delivery errors while sending notifications to the user's push service. The error 'type' attribute SHOULD be honoured - errors of type 'wait' SHOULD be retried in an appropriate manner (e.g. using exponential back-off algorithm, up to a limit), discarding the notification after an appropriate length of time or number of attempts. - -Other error types MUST NOT be automatically retried. - -A user's server MAY automatically disable a push configuration for a service that has consistently failed to relay notifications for an extended period of time. This period is a matter of deployment configuration, but a default no less than 72 hours is recommended.
--- a/mod_query_client_ver/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -# Introduction - -This module sends a [version query][xep0092] to clients when they -connect, and logs the response, allowing statistics of client usage to -be recorded. - -Note that since this is per connection, there will be a bias towards -mobile clients on bad connections. - -## Example - - info Running Running Swift version 4.0
--- a/mod_rawdebug/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ ---- -summary: Extra verbose logging of sent and received ---- - -Summary -======= - -Sometimes it is useful to get the raw XML logs from clients for -debugging purposes, but some clients don't expose this. - -This module logs dumps everything sent and received into debug logs, for -debugging purposes.
--- a/mod_readonly/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -This module blocks configured queries that change server state. - -E.g. to make vCard storage read-only: - -``` lua -readonly_stores = { - vcard = { "vcard-temp", "vCard" }; -} -```
--- a/mod_register_apps/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Manage list of compatible client apps' -rockspec: - build: - copy_directories: - - assets -... - -Introduction -============ - -This module provides a way to configure a list of XMPP client apps recommended -by the current server. This list is used by other modules such as mod_invites_page -and mod_invites_register_web. - -It also contains the logos of a number of popular XMPP clients, and serves -them over HTTP for other modules to reference when serving web pages. - -# Configuration - -| Field | Description | -|----------------------|--------------------------------------------------------------------------| -| site_apps | A list of apps and their metadata | -| site_apps_show | A list of app ids to only show | -| site_apps_hide | A list of app ids to never show | - -An "app id" is the lower case app name, with any spaces replaced by `-`. E.g. "My Chat" would be `"my-chat"`. - -The module comes with a preconfigured `site_apps` containing popular clients. Patches are welcome to -add/update this list as needed! - -If you want to limit to just displaying a subset of the apps on your server, use the `site_apps_show` -option, e.g. `site_apps_show = { "conversations", "siskin-im" }`. To never show specific apps, you -can use `site_apps_hide`, e.g. `site_apps_hide = { "pidgin" }`. - -# App metadata format - -The configuration option `site_apps` contains the list -of apps and their metadata. - -``` {.lua} --- Example site_apps config with two clients -site_apps = { - { - name = "Conversations"; - text = [[Conversations is a Jabber/XMPP client for Android 4.0+ smartphones that has been optimized to provide a unique mobile experience.]]; - image = "assets/logos/conversations.svg"; - link = "https://play.google.com/store/apps/details?id=eu.siacs.conversations"; - platforms = { "Android" }; - supports_preauth_uri = true; - magic_link_format = "{app.link!}&referrer={invite.uri}"; - download = { - buttons = { - { - image = "https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"; - url = "https://play.google.com/store/apps/details?id=eu.siacs.conversations"; - }; - }; - }; - }; - { - name = "Gajim"; - text = [[A fully-featured desktop chat client for Windows and Linux.]]; - image = "assets/logos/gajim.svg"; - link = "https://gajim.org/"; - platforms = { "Windows", "Linux" }; - download = { - buttons = { - { - text = "Download Gajim"; - url = "https://gajim.org/download/"; - target = "_blank"; - }; - }; - }; - }; -} -``` -The fields of each client entry are as follows: - -| Field | Description | -|----------------------|--------------------------------------------------------------------------| -| name | The name of the client | -| text | Description of the client | -| image | URL to a logo for the client, may also be a path in the assets/ directory| -| link | URL to the app | -| platforms | A list of platforms the app can be installed on | -| supports_preauth_uri | `true` if the client supports XEP-0401 preauth URIs | -| magic_link_format | A template to generate a magic installation link from an invite | -| download | Download instructions and buttons, described below | - -## Download metadata - -The `download` field supports an optional text prompt and one or more buttons. -Each button must contain either a `text` or `image` field and must contain -a `url` field. It is recommended to set `target = "_blank"` if the link -opens a new page, so that the user doesn't lose the invite page. - -Example download field with instructions and two buttons: - -``` {.lua} -download = { - text = "Some optional instructions about downloading the client..."; - buttons = { - { - text = "Button 1: some text"; - url = "https://example.com/"; - }; - { - image = "https://example.com/button2.png"; - url = "https://example.com/download/"; - }; - }; -} - -```
--- a/mod_register_dnsbl/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -Introduction -============ - -This module checks the IP addresses attempting to register an account -against a DNSBL, blocking the attempt if there is a hit. - -Configuration -============= - - Option Type Default - ------------------- -------- ------------ - registration\_rbl string *Required* - -Compatibility -============= - -Prosody Trunk -[1a0b76b07b7a](https://hg.prosody.im/trunk/rev/1a0b76b07b7a) or later.
--- a/mod_register_dnsbl_warn/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Introduction -============ - -This module checks the IP address of newly registered users against a -DNS block list. If a positive match is found, it gets logged. - -Configuration -============= - - Option Type Default - ------------------- -------- ------------ - registration\_rbl string *Required* - -
--- a/mod_register_json/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,95 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'Token based JSON registration & verification servlet.' -... - -Introduction ------------- - -This module let's you activate a httpserver interface to handle data -from webforms with POST and Base64 encoded JSON. - -Implementation Details ----------------------- - -Example Request format: - - POST /your_register_base_url HTTP/1.1 - Host: yourserveraddress.com:yourchoosenport - Content-Type: application/encoded - Content-Transfer-Encoding: base64 - - eyJ1c2VybmFtZSI6InVzZXJuYW1lb2ZjaG9pY2UiLCJwYXNzd29yZCI6InRoZXVzZXJwYXNzd29yZCIsImlwIjoidGhlcmVtb3RlYWRkcm9mdGhldXNlciIsIm1haWwiOiJ1c2VybWFpbEB1c2VybWFpbGRvbWFpbi50bGQiLCJhdXRoX3Rva2VuIjoieW91cmF1dGh0b2tlbm9mY2hvaWNlIn0= - -Where the encoded content is this (example) JSON Array: - -``` {.json} - { - "username":"john.smith", - "password":"secret-password", - "ip":"192.168.0.0", - "mail":"john.smith@mail.example.net", - "auth_token":"yourauthtokenofchoice" - } -``` - -Your form implementation needs to pass **all** parameters, the -auth\_token is needed to prevent misuses, if the request is successful -the server will answer with status code 200 and with the body of the -response containing the token which your web app can send via e-mail to -the user to complete the registration. - -Else, it will reply with the following http error codes: - -- 400 - if there's an error syntax; -- 401 - whenever an username is already pending registration or the - auth token supplied is invalid; -- 403 - whenever registration is forbidden (blacklist, filtered mail - etc.); -- 406 - if the username supplied fails nodeprepping; -- 409 - if the user already exists, or an user is associated already - with the supplied e-mail; -- 503 - whenever a request is throttled. - -The verification URL path to direct the users to will be: -**/your-base-path-of-choice/verify/** - on your Prosody's http server. - -The module for now stores a hash of the user's mail address to help slow -down duplicated registrations. - -It's strongly encouraged to have the web server communicate with the -servlet via https. - -Usage ------ - -Copy the module folder and all its contents (register\_json) into your -prosody modules' directory.Add the module your vhost of choice -modules\_enabled. - -Hint: pairing with mod\_register\_redirect is helpful, to allow server -registrations only via your webform. - - -Required configuration: - -``` - reg_servlet_auth_token = "your-secret-token" -``` - -Optional configuration directives: - -``` - reg_servlet_base = "/base-path/" -- Base path of the plugin (default is register_account) - reg_servlet_secure = true -- Have the plugin only process requests on https (default is true) - reg_servlet_ttime = seconds -- Specifies the time (in seconds) between each request coming from the same remote address. - reg_servlet_bl = { "1.2.3.4", "4.3.2.1" } -- The ip addresses in this list will be blacklisted and will not be able to submit registrations. - reg_servlet_wl = { "1.2.3.4", "4.3.2.1" } -- The ip addresses in this list will be ignored by the throttling. - reg_servlet_filtered_mails = { ".*banneddomain.tld", ".*deamailprovider.tld" } -- allows filtering of mail addresses via Lua patterns. -``` - -Compatibility -------------- - -0.9
--- a/mod_register_oob_url/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-077 IBR registration URL redirect' ---- - -Introduction -============ - -Registration redirect to out of band URL as described in [XEP-0077: In-Band Registration](http://xmpp.org/extensions/xep-0077.html#redirect). - -Details -======= - -The already existing module `mod_register_redirect` doesn’t add a stream feature advertising its capabilities and thus doesn’t work with clients like Conversations. - -This module tries to take a simpler and more straight forward approach for admins who just want to redirect to an URL and do not need the features provided by `mod_register_redirect`. - -Usage -===== - -Set `allow_registration` to `false` and point `register_oob_url` to the URL that handles your registration. - -Compatibility -============= - - ----- ----------------------------------------------------------------------------- - 0.10 Works - ----- -----------------------------------------------------------------------------
--- a/mod_register_redirect/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'XEP-077 IBR Registration Redirect.' -... - -Introduction ------------- - -Registration Redirect as explained in the [IBR -XEP](http://xmpp.org/extensions/xep-0077.html#redirect). - -Details -------- - -This module shows instructions on how to register to the server, should -it be necessary to perform it through other means Out-Of-Band or not, -and also let's registrations origining from ip addresses in the -whitelist to go through normally. - -Usage ------ - -Copy the module file into your Prosody modules directory. - -The module will work "out of the box" as long as at least an admin entry -is specified (see admins = {} option into prosody's documentation).These -are the optional parameters you can specify into your global -server/hostname configuration: - - registration_whitelist = { "*your whitelisted web server ip address*" } - registration_url = "*your web registration page url*" - registration_text = "Your custom instructions banner here" - registration_oob = true (default) or false, in the case there's no applicable OOB method (e.g. the server admins needs to be contacted by phone) - -To not employ any whitelisting (i.e. registration is handled -externally). - - no_registration_whitelist = true - -Compatibility -------------- - - ------ -------------- - 0.10 Works - 0.9 Works - 0.8 Works - 0.7 Might not work - 0.6 Doesn't work - 0.5 Doesn't work - ------ -------------- -
--- a/mod_register_web/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: A web interface to register user accounts -rockspec: - build: - copy_directories: - - templates -... - -Introduction ------------- - -There are various reasons to prefer web registration instead of -"in-band" account registration over XMPP. For example the lack of -CAPTCHA support in clients and servers. - -Details -------- - -mod\_register\_web has Prosody serve a web page where users can sign up -for an account. It implements reCAPTCHA to prevent automated sign-ups -(from bots, etc.). - -Configuration -------------- - -The module is served on Prosody's default HTTP ports at the path -`/register_web`. More details on configuring HTTP modules in Prosody can -be found in our [HTTP documentation](http://prosody.im/doc/http). - -To configure the CAPTCHA you need to supply a 'captcha\_options' option: - - captcha_options = { - recaptcha_private_key = "12345"; - recaptcha_public_key = "78901"; - } - -The keys for reCAPTCHA are available in your reCAPTCHA account, visit -[reCAPTCHA](https://developers.google.com/recaptcha/) for more info. - -If no reCaptcha options are set, a simple built in captcha is used. - -Customization -------------- - -Copy the files in mod_register_web/templates/ to a new directory. Edit them, -and set `register_web_template = "/path/to/your/custom-templates"` in your -config file. - -Compatibility -------------- - - ----- -------------- - 0.10 Works - 0.9 Works - 0.8 Doesn't work - ----- -------------- - -Todo ----- - -Different CAPTCHA implementation support - -Collection of additional data, such as email address - -The module kept simple!
--- a/mod_reload_components/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -Introduction -============ - -This module allows to load/unload external components after they have -been added/removed to a configuration file. It is necessary to explicitly -initiate a reload on Prosody either via prosodyctl reload or config:reload(). - -Example 1: --------- -If Prosody has started with this configuration file: - -``` {.lua} -VirtualHost "example.com" - authentication = "internal_plain" - -Component "a.example.com" - component_secret = "a" - -Component "b.example.com" - component_secret = "b" -``` - -And the file has changed manually or dynamically to: - -``` {.lua} -VirtualHost "example.com" - authentication = "internal_plain" - -Component "a.example.com" - component_secret = "a" - -Component "c.example.com" - component_secret = "c" -``` - -Then, the following actions will occur if this module is loaded: - -1. The component c.example.com will be loaded and start bouncing for -authentication. -2. The component b.example.com will be unloaded and deactivated. The -connection with it will not be closed, but no further actions will be -executed on Prosody. - -Example 2: --------- - -If Prosody has started with this configuration file: - -``` {.lua} -VirtualHost "example.com" - authentication = "internal_plain" - -Component "a.example.com" - component_secret = "a" -``` - -And the file has changed manually or dynamically to: - -``` {.lua} -VirtualHost "example.com" - authentication = "internal_plain" - -Component "a.example.com" - component_secret = "a" - -VirtualHost "newexample.com" - authentication = "internal_plain" - -Component "a.newexample.com" - component_secret = "a" -``` - -Then, the following actions will occur if this module is loaded: - -1. The component a.newexample.com will be loaded and start bouncing for -authentication. Note that its respective VirtualHost is not loaded. Bad -things may happen. - -Usage -===== - -Copy the module folder into your Prosody modules directory. Place the -module between your enabled modules either into the global or a vhost -section. - -No configuration directives are needed - -Info -==== - -- 0.9, works
--- a/mod_reload_modules/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Automatically reload modules with the config -... - -Introduction ------------- - -By default Prosody does not reload modules at runtime unless instructed -to via one of its admin interfaces. However sometimes you want to easily -reload a module to apply new settings when the config changes. - -mod\_reload\_modules will reload a set list of modules every time -Prosody reloads its config (e.g. on SIGHUP). - -Configuration -------------- - -Add "reload\_modules" to modules\_enabled. Then the list of modules to -reload using the 'reload\_modules' option in your config like so: - - reload_modules = { "groups", "tls" } - -This would reload mod\_groups and mod\_tls whenever the config is -reloaded. Note that on many systems this will be at least daily, due to -logrotate. - -Compatibility -------------- - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_remote_roster/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -summary: Remote Roster Management -... - -Introduction -============ - -This module adds support for [XEP-0321: Remote Roster Management] which -is commonly used to allow components such as transports to modify the -rosters of local users.
--- a/mod_report_forward/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Forward spam/abuse reports to a JID' ---- - -This module forwards spam/abuse reports (e.g. those submitted by users via -XEP-0377 via mod_spam_reporting) to one or more JIDs. - -## Configuration - -Install and enable the module the same as any other: - -```lua -modules_enabled = { - --- - "report_forward"; - --- -} -``` - -There are two main options. You can set `report_forward_to` which accepts a -list of JIDs to send all reports to (default is empty): - -```lua -report_forward_to = { "antispam.example.com" } -``` - -You can also control whether the module sends a report to the server from -which the spam/abuse originated (default is `true`): - -```lua -report_forward_to_origin = false -``` - -The module looks up an abuse report address using XEP-0157 (only XMPP -addresses are accepted). If it fails to find any suitable destination, it will -log a warning and not send the report. - - - -## Protocol - -This section is intended for developers. - -XEP-0377 assumes the report is embedded within another protocol such as -XEP-0191, and doesn't specify a format for communicating "standalone" reports. -This module transmits them inside a `<message>` stanza, and adds a `<jid/>` -element (borrowed from XEP-0268): - -```xml -<message from="prosody.example" to="destination.example"> - <report xmlns="urn:xmpp:reporting:1" reason="urn:xmpp:reporting:spam"> - <jid xmlns="urn:xmpp:jid:0">spammer@bad.example</jid> - <text> - Never came trouble to my house like this. - </text> - </report> -</message> -``` - -It may also include the reported message, if this has been indicated by the -user, wrapped in a XEP-0297 `<forwarded/>` element: - -```xml -<message from="prosody.example" to="destination.example"> - <report reason="urn:xmpp:reporting:spam" xmlns="urn:xmpp:reporting:1"> - <jid xmlns="urn:xmpp:jid:0">spammer@bad.example</jid> - <text>Never came trouble to my house like this.</text> - </report> - <forwarded xmlns="urn:xmpp:forward:0"> - <message from="spammer@bad.example" to="victim@prosody.example" type="chat" xmlns="jabber:client"> - <body>Spam, Spam, Spam, Spam, Spam, Spam, baked beans, Spam, Spam and Spam!</body> - </message> - </forwarded> -</message> -```
--- a/mod_require_otr/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'Enforce a policy for OTR-encrypted messages' -... - -Introduction ------------- - -[OTR, "Off The Record"](https://otr.cypherpunks.ca/), encryption allows -clients to encrypt messages such that the server cannot read/modify -them. - -This module allows the server admin to require that all messages are -OTR-encrypted. - -Configuration -------------- - -Just enable the module by adding it to your global `modules_enabled`, or -if you only want to load it on a single host you can load it only for -one host like this: - - VirtualHost "example.com" - modules_enabled = { "require_otr" } - -#### Compatibility - - ------ ------- - 0.10 Works - 0.9 Works - 0.8 Works - ------ -------
--- a/mod_rest/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,629 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: RESTful XMPP API -rockspec: - build: - modules: - mod_rest.jsonmap: jsonmap.lib.lua - copy_directories: - - example - - res ---- - -# Introduction - -This is yet another RESTful API for sending and receiving stanzas via -Prosody. It can be used to build bots and components implemented as HTTP -services. It is the spiritual successor to [mod_post_msg] and absorbs -use cases from [mod_http_rest] and [mod_component_http] and other such -modules floating around the Internet. - -# Usage - -You make a choice: install via VirtualHosts or as a Component. User authentication can -be used when installed via VirtualHost, and OAuth2 can be used for either. - -## On VirtualHosts - -This enables rest on the VirtualHost domain, enabling user authentication to secure -the endpoint. Make sure that the modules_enabled section is immediately below the -VirtualHost entry so that it's not under any Component sections. EG: - -```lua -VirtualHost "chat.example.com" -modules_enabled = {"rest"} -``` - -## As a Component - -If you install this as a component, you won't be able to use user authentication above, -and must use OAuth2 authentication outlined below. - -``` {.lua} -Component "chat.example.com" "rest" -component_secret = "dmVyeSBzZWNyZXQgdG9rZW4K" -modules_enabled = {"http_oauth2"} -``` - -## User authentication - -To enable user authentication, edit the "admins = { }" section in prosody.cfg.lua, EG: - -```lua -admins = { "admin@chat.example.com" } -``` - -To set up the admin user account: - -```lua -prosodyctl adduser admin@chat.example.com -``` - -and lastly, drop the "@host" from the username in your http queries, EG: - -```lua -curl \ - https://chat.example.com:5281/rest/version/chat.example.com \ - -k \ - --user admin \ - -H 'Accept: application/json' -``` - -## OAuth2 - -[mod_http_oauth2] can be used to grant bearer tokens which are accepted -by mod_rest. Tokens can be passed to `curl` like `--oauth2-bearer -dmVyeSBzZWNyZXQgdG9rZW4K` instead of using `--user`. - -## Sending stanzas - -The API endpoint becomes available at the path `/rest`, so the full URL -will be something like `https://conference.chat.example.com:5281/rest`. - -To try it, simply `curl` an XML stanza payload: - -``` {.sh} -curl https://prosody.example:5281/rest \ - --user username \ - -H 'Content-Type: application/xmpp+xml' \ - --data-binary '<message type="chat" to="user@example.org"> - <body>Hello!</body> - </message>' -``` - -or a JSON payload: - -``` {.sh} -curl https://prosody.example:5281/rest \ - --user username \ - -H 'Content-Type: application/json' \ - --data-binary '{ - "body" : "Hello!", - "kind" : "message", - "to" : "user@example.org", - "type" : "chat" - }' -``` - -The `Content-Type` header is important! - -### Parameters in path - -New alternative format with the parameters `kind`, `type`, and `to` -embedded in the path: - -``` -curl https://prosody.example:5281/rest/message/chat/john@example.com \ - --user username \ - -H 'Content-Type: text/plain' \ - --data-binary 'Hello John!' -``` - -### Replies - -A POST containing an `<iq>` stanza automatically wait for the reply, -long-polling style. - -``` {.sh} -curl https://prosody.example:5281/rest \ - --user username \ - -H 'Content-Type: application/xmpp+xml' \ - --data-binary '<iq type="get" to="example.net"> - <ping xmlns="urn:xmpp:ping"/> - </iq>' -``` - -Replies to other kinds of stanzas that are generated by the same Prosody -instance *MAY* be returned in the HTTP response. Replies from other -entities (connected clients or remote servers) will not be returned, but -can be forwarded via the callback API described in the next section. - -### Simple info queries - -A subset of IQ stanzas can be sent as simple GET requests - -``` -curl https://prosody.example:5281/rest/version/example.com \ - --user username \ - -H 'Accept: application/json' -``` - -The supported queries are - -- `archive` -- `disco` -- `extdisco` -- `items` -- `lastactivity` -- `oob` -- `payload` -- `ping` -- `stats` -- `version` - -## Receiving stanzas - -TL;DR: Set this webhook callback URL, get XML `POST`-ed there. - -``` {.lua} -Component "rest.example.net" "rest" -rest_callback_url = "http://my-api.example:9999/stanzas" -``` - -The callback URL supports a few variables from the stanza being sent, -namely `{kind}` (e.g. message, presence, iq or meta) and ones -corresponding to stanza attributes: `{type}`, `{to}` and `{from}`. - -The preferred format can be indicated via the Accept header in response -to an OPTIONS probe that mod_rest does on startup, or by configuring: - -``` {.lua} -rest_callback_content_type = "application/json" -``` - -Example callback looks like: - -``` {.xml} -POST /stanzas HTTP/1.1 -Content-Type: application/xmpp+xml -Content-Length: 102 - -<message to="bot@rest.example.net" from="user@example.com" type="chat"> -<body>Hello</body> -</message> -``` - -or as JSON: - -``` {.json} -POST /stanzas HTTP/1.1 -Content-Type: application/json -Content-Length: 133 - -{ - "body" : "Hello", - "from" : "user@example.com", - "kind" : "message", - "to" : "bot@rest.example.net", - "type" : "chat" -} -``` - -### Which stanzas - -The set of stanzas routed to the callback is determined by these two -settings: - -`rest_callback_stanzas` -: The stanza kinds to handle, defaults to `{ "message", "presence", "iq" }` - -`rest_callback_events` -: For the selected stanza kinds, which events to handle. When loaded -on a Component, this defaults to `{ "bare", "full", "host" }`, while on -a VirtualHost the default is `{ "host" }`. - -Events correspond to which form of address was used in the `to` -attribute of the stanza. - -bare -: `localpart@hostpart` - -full -: `localpart@hostpart/resourcepart` - -host -: `hostpart` - -The following example would handle only stanzas like `<message -to="anything@hello.example"/>` - -```lua -Component "hello.example" "rest" -rest_callback_url = "http://hello.internal.example:9003/api" -rest_callback_stanzas = { "message" } -rest_callback_events = { "bare" } -``` - -### Replying - -To accept the stanza without returning a reply, respond with HTTP status -code `202` or `204`. - -HTTP status codes in the `4xx` and `5xx` range are mapped to an -appropriate stanza error. - -For full control over the response, set the `Content-Type` header to -`application/xmpp+xml` and return an XMPP stanza as an XML snippet. - -``` {.xml} -HTTP/1.1 200 Ok -Content-Type: application/xmpp+xml - -<message type="chat"> -<body>Yes, this is bot</body> -</message> -``` - -## Payload format - -### JSON - -``` {.json} -{ - "body" : "Hello!", - "kind" : "message", - "type" : "chat" -} -``` - -Further JSON object keys as follows: - -#### Messages - -`kind` -: `"message"` - -`type` -: Commonly `"chat"` for 1-to-1 messages and `"groupchat"` for group - chat messages. Others include `"normal"`, `"headline"` and - `"error"`. - -`body` -: Human-readable message text. - -`subject` -: Message subject or MUC topic. - -`html` -: HTML. - -`oob_url` -: URL of an out-of-band resource, often used for images. - -#### Presence - -`kind` -: `"presence"` - -`type` -: Empty for online or `"unavailable"` for offline. - -`show` -: [Online - status](https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-show), - `away`, `dnd` etc. - -`status` -: Human-readable status message. - -#### Info-Queries - -Only one type of payload can be included in an `iq`. - -`kind` -: `"iq"` - -`type` -: `"get"` or `"set"` for queries, `"response"` or `"error"` for - replies. - -`ping` -: Send a ping. Get a pong. Maybe. - -`disco` -: Retrieve service discovery information about an entity. - -`items` -: Discover list of items (other services, groupchats etc). - -### XML - -``` {.xml} -<message type="" id="" to="" from="" xml:lang=""> -... -</message> -``` - -An XML declaration (`<?xml?>`) **MUST NOT** be included. - -The payload MUST contain one (1) `message`, `presence` or `iq` stanza. - -The stanzas MUST NOT have an `xmlns` attribute, and the default/empty -namespace is treated as `jabber:client`. - -# Examples - -## Python / Flask - -Simple echo bot that responds to messages as XML: - -``` {.python} -from flask import Flask, Response, request -import xml.etree.ElementTree as ET - -app = Flask("echobot") - - -@app.before_request -def parse(): - request.stanza = ET.fromstring(request.data) - - -@app.route("/", methods=["POST"]) -def hello(): - if request.stanza.tag == "message": - return Response( - "<message><body>Yes this is bot</body></message>", - content_type="application/xmpp+xml", - ) - - return Response(status=501) - - -if __name__ == "__main__": - app.run() -``` - -And a JSON variant: - -``` {.python} -from flask import Flask, Response, request, jsonify - -app = Flask("echobot") - - -@app.route("/", methods=["POST"]) -def hello(): - print(request.data) - if request.is_json: - data = request.get_json() - if data["kind"] == "message": - return jsonify({"body": "hello"}) - - return Response(status=501) - - -if __name__ == "__main__": - app.run() -``` - -Remember to set `rest_callback_content_type = "application/json"` for -this to work. - -# JSON mapping - -This section describes the JSON mapping. It can't represent any possible -stanza, for full flexibility use the XML mode. - -## Stanza basics - -`kind` -: String representing the kind of stanza, one of `"message"`, - `"presence"` or `"iq"`. - -`type` -: String with the type of stanza, appropriate values vary depending on - `kind`, see [RFC 6121]. E.g.`"chat"` for *message* stanzas etc. - -`to` -: String containing the XMPP Address of the destination / recipient of - the stanza. - -`from` -: String containing the XMPP Address of the sender the stanza. - -`id` -: String with a reasonably unique identifier for the stanza. - -## Basic Payloads - -### Messages - -`body` -: String, human readable text message. - -`subject` -: String, human readable summary equivalent to an email subject or the - chat room topic in a `type:groupchat` message. - -### Presence - -`show` -: String representing availability, e.g. `"away"`, `"dnd"`. No value - means a normal online status. See [RFC 6121] for the full list. - -`status` -: String with a human readable text message describing availability. - -## More payloads - -### Messages - -`state` -: String with current chat state, e.g. `"active"` (default) and - `"composing"` (typing). - -`html` -: String with HTML allowing rich formatting. **MUST** be contained in a - `<body>` element. - -`oob_url` -: String with an URL of an external resource. - -### Presence - -`muc` -: Object with [MUC][XEP-0045] related properties. - -### IQ - -`ping` -: Boolean, a simple ping query. "Pongs" have only basic fields - presents. - -`version` -: Map with `name`, `version` fields, and optionally an `os` field, to - describe the software. - -#### Service Discovery - -`disco` - -: Boolean `true` in a `kind:iq` `type:get` for a service discovery - query. - - Responses have a map containing an array of available features in - the `features` key and an array of "identities" in the `identities` - key. Each identity has a `category` and `type` field as well as an - optional `name` field. See [XEP-0030] for further details. - -`items` -: Boolean `true` in a `kind:iq` `type:get` for a service discovery - items list query. The response contain an array of items like - `{"jid":"xmpp.address.here","name":"Description of item"}`. - -`extensions` -: Map of extended feature discovery (see [XEP-0128]) data with - `FORM_DATA` fields as the keys pointing at maps with the rest of the - data. - -#### Ad-Hoc Commands - -Used to execute arbitrary commands on supporting entities. - -`command` - -: String representing the command `node` or Map with the following - possible fields: - - `node` - : Required string with node from disco\#items query for the - command to execute. - - `action` - : Optional enum string defaulting to `"execute"`. Multi-step - commands may involve `"next"`, `"prev"`, `"complete"` or - `"cancel"`. - - `actions` - : Set (map of strings to `true`) with available actions to proceed - with in multi-step commands. - - `status` - : String describing the status of the command, normally - `"executing"`. - - `sessionid` - : Random session ID issued by the responder to identify the - session in multi-step commands. - - `note` - : Map with `"type"` and `"text"` fields that carry simple result - information. - - `form` - : Data form with description of expected input and data types in - the next step of multi-step commands. **TODO** document format. - - `data` - : Map with only the data for result dataforms. Fields may be - strings or arrays of strings. - -##### Example - -Discovering commands: - -``` {.json} -{ - "items" : { - "node" : "http://jabber.org/protocol/commands" - }, - "id" : "8iN9hwdAAcfTBchm", - "kind" : "iq", - "to" : "example.com", - "type" : "get" -} -``` - -Response: - -``` {.json} -{ - "from" : "example.com", - "id" : "8iN9hwdAAcfTBchm", - "items" : [ - { - "jid" : "example.com", - "name" : "Get uptime", - "node" : "uptime" - } - ], - "kind" : "iq", - "type" : "result" -} -``` - -Execute the command: - -``` {.json} -{ - "command" : { - "node" : "uptime" - }, - "id" : "Jv-87nRaP6Mnrp8l", - "kind" : "iq", - "to" : "example.com", - "type" : "set" -} -``` - -Executed: - -``` {.json} -{ - "command" : { - "node" : "uptime", - "note" : { - "text" : "This server has been running for 0 days, 20 hours and 54 minutes (since Fri Feb 7 18:05:30 2020)", - "type" : "info" - }, - "sessionid" : "6380880a-93e9-4f13-8ee2-171927a40e67", - "status" : "completed" - }, - "from" : "example.com", - "id" : "Jv-87nRaP6Mnrp8l", - "kind" : "iq", - "type" : "result" -} -``` - -# TODO - -- Describe multi-step commands with dataforms. -- Versioned API, i.e. /v1/stanzas -- Bind resource to webhook/callback - -# Compatibility - -Requires Prosody trunk / 0.12
--- a/mod_restrict_xmpp/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -labels: -- Stage-Alpha -summary: XMPP-layer access control for Prosody ---- - -Introduction -============ - -This module enforces access policies using Prosody's new [roles and -permissions framework](https://prosody.im/doc/developers/permissions). It can -be used to grant restricted access to an XMPP account or services. - -This module is still in its early stages, and prone to change. Feedback from -testers is welcome. At this early stage, it should not be solely relied upon -for account security purposes. - -Configuration -============= - -There is no configuration, apart from Prosody's normal roles and permissions -configuration. - -Permissions -=========== - -`xmpp:federate` -: Communicate with other users and services on other hosts on the XMPP - network - -`xmpp:account:messages:read` -: Read incoming messages - -`xmpp:account:messages:write` -: Send outgoing messages - -`xmpp:account:presence:write` -: Update presence for the account - -`xmpp:account:contacts:read`/`xmpp:account:contacts:write` -: Controls access to the contact list (roster) - -`xmpp:account:bookmarks:read`/`xmpp:account:bookmarks:write` -: Controls access to the bookmarks (group chats list) - -`xmpp:account:profile:read`/`xmpp:account:profile:write` -: Controls access to the user's profile (e.g. vCard/avatar) - -`xmpp:account:omemo:read`/`xmpp:account:omemo:write` -: Controls access to the user's OMEMO data - -`xmpp:account:blocklist:read`/`xmpp:account:blocklist:write` -: Controls access to the user's block list - -`xmpp:account:disco:read` -: Controls access to the user's service discovery information - -Compatibility -============= - -Requires Prosody trunk 72f431b4dc2c (build 1444) or later.
--- a/mod_roster_allinall/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ ---- -labels: -... - -Introduction -============ - -This module is similar in purpouse to mod\_groups, for when you want all -users on the server to be in each others roster. - -Details -======= - -Upon login, this module will add all currently logged in users to the -logging in users roster. - -Configuration -============= - -Just add it to the modules\_enabled, after that there is no further -configuration.
--- a/mod_roster_command/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Manage rosters through prosodyctl -... - -Introduction ------------- - -This module allows you to perform various actions on user rosters via -prosodyctl. - -Details -------- - -After putting this module in your modules directory you can use it via -prosodyctl like this: - - prosodyctl mod_roster_command COMMAND [OPTIONS...] - -**Note:** Do not add mod\_roster\_command to your Prosody config file. -This is unnecessary because it will automatically be loaded by -prosodyctl when you use it. - -### Commands - - subscribe user@host contact@host - -Subscribes the user to the contact's presence. That is, the user will -see when the contact is online (but the contact won't see the user). - - subscribe_both user@host contact@host - -The same as the 'subscribe' command, but performs the subscription in -both directions, so that both the contact and user will always see each -other online. - - unsubscribe user@host contact@host - -Removes a subscription to the contact's presence. - - unsubscribe_both user@host contact@host - -Same as unsubscribe, but also revokes a contact's subscription to the -user's presence. - - rename user@host contact@host [name] [group] - -Sets or updates a name for a contact in the user's roster, and moves the -contact to the given group, if specified. - -Compatibility -------------- - - ----- ------- - 0.9 Works - 0.8 Works - ----- -------
--- a/mod_s2s_auth_compat/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ ---- -labels: -summary: Workaround for servers doing EXTERNAL without proper stream headers -... - -Introduction -============ - -This module is a workaround for servers that try to do s2s -authentication with certificates and SASL EXTERNAL, but do not send -correct stream headers. Notably Openfire versions since 3.7 or 3.8.
--- a/mod_s2s_auth_dane/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ ---- -labels: -- Stage-Broken -- Type-S2SAuth -summary: S2S authentication using DANE -... - -Introduction -============ - -This module implements DANE as described in [Using DNS Security -Extensions (DNSSEC) and DNS-based Authentication of Named Entities -(DANE) as a Prooftype for XMPP Domain Name -Associations](http://tools.ietf.org/html/draft-miller-xmpp-dnssec-prooftype). - -Dependencies -============ - -This module requires a DNSSEC aware DNS resolver. Prosodys internal DNS -module does not support DNSSEC. Therefore, to use this module, a -replacement is needed, such as [this -one](https://www.zash.se/luaunbound.html). - -LuaSec 0.5 or later is also required. - -Configuration -============= - -After [installing the module][doc:installing\_modules], just add it to -`modules_enabled`; - - modules_enabled = { - ... - "s2s_auth_dane"; - } - -DANE Uses ---------- - -By default, only DANE uses are enabled. - - dane_uses = { "DANE-EE", "DANE-TA" } - - Use flag Description - ----------- ------------------------------------------------------------------------------------------------------- - `DANE-EE` Most simple use, usually a fingerprint of the full certificate or public key used the service - `DANE-TA` Fingerprint of a certificate or public key that has been used to issue the service certificate - `PKIX-EE` Like `DANE-EE` but the certificate must also pass normal PKIX trust checks (ie standard certificates) - `PKIX-TA` Like `DANE-TA` but must also pass normal PKIX trust checks (ie standard certificates) - -DNS Setup -========= - -In order for other services to verify your site using using this plugin, -you need to publish TLSA records (and they need to have this plugin). -Here's an example using `DANE-EE Cert SHA2-256` for a host named -`xmpp.example.com` serving the domain `example.com`. - - $ORIGIN example.com. - ; Your standard SRV record - _xmpp-server._tcp.example.com IN SRV 0 0 5269 xmpp.example.com. - ; IPv4 and IPv6 addresses - xmpp.example.com. IN A 192.0.2.68 - xmpp.example.com. IN AAAA 2001:0db8:0000:0000:4441:4e45:544c:5341 - - ; The DANE TLSA records. - _5269._tcp.xmpp.example.com. 300 IN TLSA 3 0 1 E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 - - ; If your zone file tooling does not support TLSA records, you can try the raw binary format: - _5269._tcp.xmpp.example.com. 300 IN TYPE52 \# 35 030001E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 - -[List of DNSSEC and DANE -tools](http://www.internetsociety.org/deploy360/dnssec/tools/) - -Further reading -=============== - -- [DANE Operational Guidance][rfc7671] - -# Compatibility - - version status - --------- ------------ - trunk broken[^1] - 0.12 broken - 0.11 works - 0.10 works - 0.9 works - -**Broken** since [trunk revision 756b8821007a](https://hg.prosody.im/trunk/rev/756b8821007a). - -# Known issues - -- A race condition between the DANE lookup and completion of the TLS - handshake may cause a crash. This does not happen in **trunk** - thanks to better async support. - -[^1]: since [756b8821007a](https://hg.prosody.im/trunk/rev/756b8821007a)
--- a/mod_s2s_auth_fingerprint/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-S2SAuth' -summary: Fingerprint based s2s authentication -... - -Introduction -============ - -This module allows you to manually pin certificate fingerprints of -remote servers. - -Details -======= - -Servers not listed in the configuration are not affected. - -Configuration -============= - -After installing and enabling this module, you can put fingerprints of -remote servers in your config like this: - -``` {.lua} -s2s_auth_fingerprint_digest = "sha1" -- This is the default. Other options are "sha256" and "sha512" -s2s_trusted_fingerprints = { - ["jabber.org"] = "11:C2:3D:87:3F:95:F8:13:F8:CA:81:33:71:36:A7:00:E0:01:95:ED"; - ["matthewwild.co.uk"] = { - "FD:7F:B2:B9:4C:C4:CB:E2:E7:48:FB:0D:98:11:C7:D8:4D:2A:62:AA"; - "CF:F3:EC:43:A9:D5:D1:4D:D4:57:09:55:52:BC:5D:73:06:1A:A1:A0"; - }; -} - --- If you don't want to fall back to dialback, you can list the domains s2s_secure_domains too -s2s_secure_domains = { - "jabber.org"; -} -``` - -Compatibility -============= - - ------- -------------- - trunk Works - 0.9 Works - 0.8 Doesn't work - ------- --------------
--- a/mod_s2s_auth_monkeysphere/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-S2SAuth' -summary: Monkeysphere certificate checking for s2s ---- - -## Introduction - -[Monkeysphere](http://web.monkeysphere.info/) is a project aiming to -introduce PGP's web of trust to protocols such as SSH and TLS (which -XMPP uses). - -## Details - -This module is currently just a prototype, it has numerous issues and is -**not** suitable for production use. - -## Compatibility - - ------- ----------------------------- - trunk Works (not tested recently) - 0.11 Works (not tested) - 0.10 Does not work - 0.9 Does not work - ------- -----------------------------
--- a/mod_s2s_auth_posh/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ ---- -labels: -- 'Type-S2SAuth' ---- - -Introduction -============ - -[PKIX over Secure HTTP (POSH)][rfc7711] describes a method of -securely delegating a domain to a hosting provider, without that hosting -provider needing keys and certificates covering the hosted domain. - -# Validating - -This module performs POSH validation of other servers. It is *not* -needed to delegate your own domain. - -# Delegation - -You can generate the JSON delegation file from a certificate by running -`prosodyctl mod_s2s_auth_posh /path/to/example.crt`. This file needs to -be served at `https://example.com/.well-known/posh/xmpp-server.json`.
--- a/mod_s2s_blacklist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -This module lets you block connections to remote servers at the s2s -level. - -``` {.lua} -modules_enabled = { - -- other modules -- - "s2s_blacklist", - -} -s2s_blacklist = { - "proxy.eu.jabber.org", -} -```
--- a/mod_s2s_idle_timeout/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: 'Close idle server-to-server connections' -... - -Introduction -============ - -Some people find it preferable to close server-to-server connections -after they have been silent for a while. - -Configuration -============= - -...is trivial. The default timeout is 300 seconds (5 minutes). To change -this simply put in the config: - - s2s_idle_timeout = 60 -- or any other number of seconds - -Compatibility -============= - - ----- ------- - 0.6 Works - 0.7 Works - ----- -------
--- a/mod_s2s_keepalive/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ ---- -summary: Keepalive s2s connections -... - -Introduction -============ - -This module periodically sends [XEP-0199] ping requests to remote servers -to keep your connection alive. - -Configuration -============= - -Simply add the module to the `modules_enabled` list like any other -module. By default, all current s2s connections will be pinged -periodically. To ping only a subset of servers, list these in -`keepalive_servers`. The ping interval can be set using -`keepalive_interval`. - -If no response to the ping has been received in about 10 minutes (or -`keepalive_timeout` seconds) the s2s connections are closed. - -``` lua -modules_enabled = { - ... - "s2s_keepalive" -} - -keepalive_servers = { "conference.prosody.im"; "rooms.swift.im" } -keepalive_interval = 90 -- (in seconds, default is 60 ) -keepalive_timeout = 300 -- (in seconds, default is 593 ) -``` - -Compatibility -============= - - ------- ----------------------- - 0.11 Works - 0.10 Works - 0.9 Works - ------- -----------------------
--- a/mod_s2s_keysize_policy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -summary: Distrust servers with too small keys -... - -Introduction -============ - -This module sets the security status of s2s connections to invalid if -their key is too small and their certificate was issued after 2014, per -CA/B Forum guidelines. - -Details -======= - -Certificate Authorities were no longer allowed to issue certificates -with public keys smaller than 2048 bits (for RSA) after December 31 -2013. This module was written to enforce this, as there were some CAs -that were slow to comply. As of 2015, it might not be very relevant -anymore, but still useful for anyone who wants to increase their -security levels. - -When a server is determined to have a "too small" key, this module sets -its chain and identity status to "invalid", so Prosody will treat it as -a self-signed certificate istead. - -"Too small" ------------ - -The definition of "too small" is based on the key type and is taken from -[RFC 4492]. - - Type bits - ------ ------ - RSA 2048 - DSA 2048 - DH 2048 - EC 233 - -Compatibility -============= - -Works with Prosody 0.9 and later. Requires LuaSec with [support for -inspecting public keys](https://github.com/brunoos/luasec/pull/19).
--- a/mod_s2s_log_certs/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ ---- -summary: Log certificate status and fingerprint of remote servers -... - -Introduction -============ - -This module produces info level log messages with the certificate status -and fingerprint every time an s2s connection is established. It can also -optionally store this in persistent storage. - -**info** jabber.org has a trusted valid certificate with SHA1: -11:C2:3D:87:3F:95:F8:13:F8:CA:81:33:71:36:A7:00:E0:01:95:ED - -Fingerprints could then be added to -[mod\_s2s\_auth\_fingerprint](mod_s2s_auth_fingerprint.html). - -Configuration -============= - -Add the module to the `modules_enabled` list. - - modules_enabled = { - ... - "s2s_log_certs"; - } - -If you want to keep track of how many times, and when a certificate is -seen add - -`s2s_log_certs_persist = true` - -Compatibility -============= - - ------- -------------- - trunk Works - 0.10 Works - 0.9 Works - 0.8 Doesn't work - ------- --------------
--- a/mod_s2s_never_encrypt_blacklist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: | - Stops prosody from including starttls into available features for - specified remote servers. -... - -Details -------- - -Let's you stop Prosody from sending \<starttls -xmlns='urn:ietf:params:xml:ns:xmpp-tls'\> feature to choppy/buggy -servers which therefore would fail to re-negotiate and use a secure -stream. (e.g. [OpenFire -3.7.0](http://issues.igniterealtime.org/browse/OF-405)) - -Usage ------ - -Copy the plugin into your prosody's modules directory. - -And add it between your enabled modules into the global section -(modules\_enabled). - -Then list each host as follow: - - tls_s2s_blacklist = { "host1.tld", "host2.tld", "host3.tld" } - -In the unfortunate case of OpenFire... you can add the Server's ip -address directly as it may not send proper rfc6121 requests. - - tls_s2s_blacklist_ip = { "a.a.a.a", "b.b.b.b", "c.c.c.c" } - -Compatibility -------------- - -It's supposed to work with 0.7-0.8.x
--- a/mod_s2s_reload_newcomponent/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -labels: -summary: | - Module to automatically load new components when config:reload is done - in console -... - -Introduction -============ - -Currently, module:reload command in console doesn't load new components. -This module will automatically load the new components (if any) when the -config:reload command is run in the console. - -Details -======= - -In order to use the plugin, simply load the plugin by adding -"s2s\_reload\_newcomponent" to the modules enabled list. The plugin -requires configuration to be reloaded via console plugin's -config:reload() command. - -Now, add a new component in the prosody configuration and then run -config:reload() command in the console plugin. The new component should -become active in prosody at this point and can be used. - -Dependency -========== - -Needs console plugin to reload configuration. - -Compatibility -============= - - ----- ------- - 0.7 works - ----- -------
--- a/mod_s2s_status/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Track the status and health of s2s connections' -... - -Introduction -============ - -Prosody already gives some insight into current s2s connections, e.g. via -the `s2s:show()` command in the console. This will tell you about all current -s2s connections. - -However sometimes this is not enough. For example if an s2s connection fails -to establish, it won't show up - you have to go digging through the log file -looking for the errors instead. - -This module maintains a record of recent connection attempts to a remote -domain. You can use this module to answer questions such as: - -- Why did the last connection attempt to `example.com` fail? -- When did I last have a successful connection with `example.com`? -- Are my s2s connections generally stable? - -**Note:** At the time of writing, this module is not yet finished, and should -be considered a proof-of-concept. - -# Configuration - -Just load the module as normal: - -``` {.lua} -modules_enabled = { - ... - "s2s_status"; - ... -} -``` - -# Compatibility - -trunk (0.12) and later, e.g. due to [60676b607b6d](https://hg.prosody.im/trunk/rev/60676b607b6d).
--- a/mod_s2s_whitelist/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -This module lets you block connections to any remote servers not on a -whitelist. - -``` {.lua} -modules_enabled = { - -- other modules -- - "s2s_whitelist", - -} -s2s_whitelist = { - "example.org", -} -```
--- a/mod_s2soutinjection/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -summary: S2S connection override -... - -# Introduction - -This module is similar to [mod\_srvinjection] but less of an hack. - -# Configuration - -``` lua --- In the global section - -modules_enabled = { - --- your other modules - "s2soutinjection"; -} - --- targets must be IPs, not hostnames -s2s_connect_overrides = { - -- This one will use the default port, 5269 - ["example.com"] = "1.2.3.4"; - - -- To set a different port: - ["another.example"] = { "127.0.0.1", 9999 }; -} -``` - -# Compatibility - -Requires 0.9.x or later. Tested on 0.12.0
--- a/mod_sasl_oauthbearer/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ ---- -labels: -- 'Type-Auth' -summary: SASL OAuthBearer Mechanism -... - -Introduction -============ - -This module adds a new SASL mechanism OAUTHBEARER, as defined in [RFC-7628](https://tools.ietf.org/html/rfc7628). - -It's intended to be used together with the `mod_auth_oauthbearer.lua` module.
--- a/mod_sasl_ssdp/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0474: SASL SCRAM Downgrade Protection' -... - -Introduction -============ - -This module implements the experimental XEP-0474: SASL SCRAM Downgrade -Protection. It provides an alternative downgrade protection mechanism to -client-side pinning which is currently the most common method of downgrade -protection. - -**Note:** This module implements version 0.3.0 of XEP-0474. As of 2023-12-05, -this version is not yet published on xmpp.org. Version 0.3.0 of the XEP is -implemented in Monal 6.0.1 and go-sendxmpp 0.8.0. No other clients are currently -known to implement the XEP at the time of writing. - -# Configuration - -There are no configuration options for this module, just load it as normal. - -# Compatibility - -For SASL2 (XEP-0388) clients, it is compatible with the mod_sasl2 community module. - -For clients using RFC 6120 SASL, it requires Prosody trunk 33e5edbd6a4a or -later. It is not compatible with Prosody 0.12 (it will load, but simply -won't do anything) for "legacy SASL".
--- a/mod_saslname/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ ---- -labels: -- 'Stage-Stable' -- 'Type-Auth' -summary: 'XEP-0233: XMPP Server Registration for use with Kerberos V5' -... - -Introduction -============ - -This module implements a manual method for advertsing the Kerberos -principal name as per [XEP-0233]. It could be used in conjection with -a Kerberos authentication module. - -Configuration -============= - - sasl_hostname = "auth42.us.example.com" - -Compatibility -============= - -Prosody 0.7+
--- a/mod_seclabels/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Security Labels -... - -Introduction -============ - -This module implements [XEP-0258: Security Labels in XMPP], but not -actual policy enforcement. See for example [mod_firewall] for that. - -Configuration -============= - -As with all modules, you enable it by adding it to the modules\_enabled -list. - -These options exist: - - Name Description Default - ------------------------- ----------------------- ------------- - security\_catalog\_name Catalouge name "Default" - security\_catalog\_desc Catalouge description "My labels" - -You can then add your labels in a table called security\_labels. They -can be both orderd and unorderd, but ordered comes first. - -``` {.lua} -security_labels = { - { -- This label will come first - name = "Public", - label = true, -- This is a label, but without the actual label. - default = true -- This is the default label. - }, - { - name = "Private", - label = "PRIVATE", - color = "white", - bgcolor = "blue" - }, - Sensitive = { -- A Sub-selector - SECRET = { -- The index is used as name - label = true - }, - TOPSECRET = { -- The order of this and the above is not guaranteed. - color = "red", - bgcolor = "black", - } - } -} -``` - -Each label can have the following properties: - - Name Description Default - ---------------- --------------------------------------------------------- ---------------------------------------------------------- - name The name of the label. Used for selector. Required. - label The actual label, ie `<esssecuritylabel/>` Required, can be boolean for a empty label, or a string. - display The text shown as display marking. Defaults to the name - color, bgcolor The fore- and background color of the display marking None - default Boolean, true for the default. Only one may be default. false
--- a/mod_secure_interfaces/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Mark some network interfaces (e.g. loopback/LAN) as always secure' -... - -Introduction -============ - -Sometimes you might run clients without encryption on the same machine -or LAN as Prosody - and you want Prosody to treat them as secure (e.g. -allowing plaintext authentication) even though they are not encrypted. - -This module allows you to tell Prosody which of the current server's -interfaces (IP addresses) that you consider to be on secure networks. - -Configuration -============= - -Configuration is simple, just load the module like any other by adding -it to your modules\_enabled list: - - modules_enabled = { - ... - "secure_interfaces"; - ... - } - -Then set the list of secure interfaces (just make sure it is set in the -global section of your config file, and **not** under a VirtualHost or -Component): - - secure_interfaces = { "127.0.0.1", "::1", "192.168.1.54" } - -Compatibility -============= - - ------- --------- - 0.9 Works - 0.8 Unknown - trunk Works - ------- ---------
--- a/mod_server_status/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Server status plugin -... - -Introduction -============ - -This module fetches the current status of configured hosts and/or stanza -statistics from -[mod\_stanza\_counter](http://code.google.com/p/prosody-modules/wiki/mod_stanza_counter#). -And outputs it in either XML or JSON format. - -Usage -===== - -Copy the file into prosody's module directory and place it into your -global's enabled modules. - -Configuration example: - - server_status_basepath = "/server-info/" - server_status_show_hosts = { "iwanttoshowifthishostisonline.com", "iwanttoshowifthishostisonline2.com" } - server_status_show_comps = { "muc.iwanttoshowifthishostisonline.com", "transport.iwanttoshowifthishostisonline.com" } - server_status_json = true - -By default the plugin's output is in XML, setting server\_status\_json -to "true" will turn it into JSON instead. if mod\_stanza\_counter isn't -loaded the plugin will require at least either -server\_status\_show\_hosts or server\_status\_show\_comps to be set. - -Info -==== - -- This is only compatible with 0.9 for older versions please look at - the 0.8-diverge branch.
--- a/mod_service_outage_status/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -This module allows advertising a machine-readable document were outages, -planned or otherwise, may be reported. - -See [XEP-0455: Service Outage Status] for further details, including -the format of the outage status document. - -```lua -modules_enabled = { - -- other modules - "service_outage_status", -} - -outage_status_urls = { - "https://uptime.example.net/status.json", -} -``` - -The outage status document should be hosted on a separate server to -ensure availability even if the XMPP server is unreachable.
--- a/mod_sift/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0273: Stanza Interception and Filtering Technology' -... - -Introduction -============ - -[SIFT][XEP-0273] is a technology to allow clients to filter incoming -traffic on the server. This helps save bandwidth, etc. - -Compatibility -============= - - ----- ------- - 0.7 Works - ----- ------- - -Quirks -====== - -This implementation is a work in progress. - -- Stanzas to full JIDs get sifted correctly -- Stanzas to bare JIDs are currently allowed/disallowed for all - resources as a whole, and not for individual resources -- Presence is only sent to available resources, and probes are not - sent for unavailable reasources -- This module currently does not interact with offline messages - (filtered messages are dropped with an error reply) -- Not tested with privacy lists
--- a/mod_slack_webhooks/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Allow Slack integrations to work with Prosody MUCs' -... - -Introduction -============ - -This module provides a Slack-compatible "web hook" interface to Prosody MUCs. -Both "incoming" web hooks, which allow Slack integrations to post messages -to Prosody MUCs, and "outgoing" web hooks, which copy messages from Prosody -MUCs to Slack-style integrations by HTTP, are supported. This can also be -used, in conjunction with various Slack inter-namespace bridging tools, to -provide a bidirectional bridge between a Prosody-hosted XMPP MUC and a Slack -channel. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "slack\_webhooks" to your modules\_enabled list: - -``` {.lua} -Component "conference.example.org" "muc" -modules_enabled = { - "slack_webhooks", -} -``` - -Configuration -============= - -The normal use for this module is to provide an incoming webhook to allow -integrations to post to prosody MUCs: - -``` {.lua} -incoming_webhook_path = "/msg/DFSDF56587658765NBDSA" -incoming_webhook_default_nick = "Bot" -- Unless otherwise specified, posts as "Bot" -``` - -This allows Slack-style JSON messages posted to http://conference.example.org/msg/DFSDF56587658765NBDSA/chat to appear in the MUC chat@conference.example.org. A username field in the message is honored as the nick attached to the message; if no username is specified, the message will use the value of default_from_nick. -Specifying a string of random gibberish in the URL is important to prevent spam. - -In addition, there is a second operating mode equivalent to Slack's outgoing -webhooks. This allows all messages from a set of specified chat rooms to be -routed to an external server over HTTP in the format used by Slack's -outgoing webhooks. -``` {.lua} -outgoing_webhook_routing = { - -- Send all messages from chat@conference.example.org to - -- a web server. - ["chat"] = "http://example.org/cgi-bin/messagedest", -} -``` - -Known Issues -============ - -The users from whom messages delivered from integrations are apparently -delivered are not, in general, members of the MUC. Other prosody modules -that try to look up information about the users who most messages, mostly -logging modules, may become confused and fail (clients all work fine because -replayed history also can come from non-present users). In at least some cases, -such as with mod_muc_mam, this can be fixed by hiding the JIDs of the -participants in the room configuration. - -There are a few smaller UI issues: - -* If an integration posts with the same username as a room member, there is - no indication (like Slack's [bot] suffix) that the message is not from that - room member. -* It is not currently possible to prevent posting to some MUCs (this is - also true of Slack). -* It should be possible to set the webhook configuration for a room in the - room configuration rather than statically in Prosody's configuration file. - -Compatibility -============= - - ------- ----------------- - trunk Works - 0.10 Works - 0.9 Works - ------- ----------------- -
--- a/mod_smacks/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0198: Reliability and fast reconnects for XMPP' -... - -Introduction -============ - -By default XMPP is as reliable as your network is. Unfortunately in some -cases that is not very reliable - in some network conditions disconnects -can be frequent and message loss can occur. - -To overcome this, XMPP has an optional extension (XEP-0198: Stream -Management) which, when supported by both the client and server, can -allow a client to resume a disconnected session, and prevent message -loss. - -Details -======= - -When using XEP-0198 both the client and the server keep a queue of the -most recently sent stanzas - this is cleared when the other end -acknowledges they have received the stanzas. If the client disconnects, -instead of marking the user offline the server pretends the client is -still online for a short (configurable) period of time. If the client -reconnects within this period, any stanzas in the queue that the client -did not receive are re-sent. - -If the client fails to reconnect before the timeout it will be marked as -offline like prosody does on disconnect without mod_smacks. -If the client is the last one for this jid, all message stanzas are added to -the offline store and all other stanzas stanzas are returned with an -"recipient-unavailable" error. If the client is not the last one with an -open smacks session, *all* stanzas are returned with an "recipient-unavailable" error. - -If you deliberately disabled [mod_offline], all message stanzas of the last client -are also returned with an "recipient-unavailable" error, because the can not be -added to the offline storage. -If you don't want this behaviour you can use [mod_nooffline_noerror] to suppress the error. -This is generally only advisable, if you are sure that all your clients are using MAM! - -This module also provides some events used by [mod_cloud_notify]. -These events are: "smacks-ack-delayed", "smacks-hibernation-start" and -"smacks-hibernation-end". See [mod_cloud_notify] for details on how this -events are used there. - -Use prosody 0.10+ to have per user limits on allowed sessions in hibernation -state and allowed sessions for which the h-value is kept even after the -hibernation timed out. -These are settable using `smacks_max_hibernated_sessions` and `smacks_max_old_sessions`. - -Configuration -============= - - Option Default Description - ---------------------------------- ----------------- ------------------------------------------------------------------------------------------------------------------ - `smacks_hibernation_time` 600 (10 minutes) The number of seconds a disconnected session should stay alive for (to allow reconnect) - `smacks_enabled_s2s` true Enable Stream Management on server connections? *Experimental* - `smacks_s2s_resend` false Attempt to re-send unacked messages on s2s disconnect *Experimental* - `smacks_max_unacked_stanzas` 0 How many stanzas to send before requesting acknowledgement - `smacks_max_ack_delay` 30 (1/2 minute) The number of seconds an ack must be unanswered to trigger an "smacks-ack-delayed" event - `smacks_max_hibernated_sessions` 10 The number of allowed sessions in hibernated state (limited per user) - `smacks_max_old_sessions` 10 The number of allowed sessions with timed out hibernation for which the h-value is still kept (limited per user) - -Compatibility -============= - - ------- ------- - trunk Works - 0.11 Works - ------- ------- - - -Clients -======= - -Clients that support [XEP-0198]: - -- Gajim (Linux, Windows, OS X) -- Conversations (Android) -- ChatSecure (iOS) -- Swift (but not resumption, as of version 2.0 and alphas of 3.0) -- Psi (in an unreleased branch) -- Yaxim (Android) -- Monal (iOS) - -[7693724881b3]: //hg.prosody.im/prosody-modules/raw-file/7693724881b3/mod_smacks/mod_smacks.lua -[mod_offline]: //modules.prosody.im/mod_offline -[mod_nooffline_noerror]: //modules.prosody.im/mod_nooffline_noerror -[mod_cloud_notify]: //modules.prosody.im/mod_cloud_notify
--- a/mod_smacks/mod_smacks.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,690 +0,0 @@ --- XEP-0198: Stream Management for Prosody IM --- --- Copyright (C) 2010-2015 Matthew Wild --- Copyright (C) 2010 Waqas Hussain --- Copyright (C) 2012-2021 Kim Alvefur --- Copyright (C) 2012 Thijs Alkemade --- Copyright (C) 2014 Florian Zeitz --- Copyright (C) 2016-2020 Thilo Molitor --- --- This project is MIT/X11 licensed. Please see the --- COPYING file in the source package for more information. --- - -local st = require "util.stanza"; -local dep = require "util.dependencies"; -local cache = dep.softreq("util.cache"); -- only available in prosody 0.10+ -local uuid_generate = require "util.uuid".generate; -local jid = require "util.jid"; - -local t_remove = table.remove; -local math_min = math.min; -local math_max = math.max; -local os_time = os.time; -local tonumber, tostring = tonumber, tostring; -local add_filter = require "util.filters".add_filter; -local timer = require "util.timer"; -local datetime = require "util.datetime"; - -local xmlns_mam2 = "urn:xmpp:mam:2"; -local xmlns_sm2 = "urn:xmpp:sm:2"; -local xmlns_sm3 = "urn:xmpp:sm:3"; -local xmlns_errors = "urn:ietf:params:xml:ns:xmpp-stanzas"; -local xmlns_delay = "urn:xmpp:delay"; - -local sm2_attr = { xmlns = xmlns_sm2 }; -local sm3_attr = { xmlns = xmlns_sm3 }; - -local resume_timeout = module:get_option_number("smacks_hibernation_time", 600); -local s2s_smacks = module:get_option_boolean("smacks_enabled_s2s", true); -local s2s_resend = module:get_option_boolean("smacks_s2s_resend", false); -local max_unacked_stanzas = module:get_option_number("smacks_max_unacked_stanzas", 0); -local max_inactive_unacked_stanzas = module:get_option_number("smacks_max_inactive_unacked_stanzas", 256); -local delayed_ack_timeout = module:get_option_number("smacks_max_ack_delay", 30); -local max_hibernated_sessions = module:get_option_number("smacks_max_hibernated_sessions", 10); -local max_old_sessions = module:get_option_number("smacks_max_old_sessions", 10); -local core_process_stanza = prosody.core_process_stanza; -local sessionmanager = require"core.sessionmanager"; - -assert(max_hibernated_sessions > 0, "smacks_max_hibernated_sessions must be greater than 0"); -assert(max_old_sessions > 0, "smacks_max_old_sessions must be greater than 0"); - -local c2s_sessions = module:shared("/*/c2s/sessions"); - -local function init_session_cache(max_entries, evict_callback) - -- old prosody version < 0.10 (no limiting at all!) - if not cache then - local store = {}; - return { - get = function(user, key) - if not user then return nil; end - if not key then return nil; end - return store[key]; - end; - set = function(user, key, value) - if not user then return nil; end - if not key then return nil; end - store[key] = value; - end; - }; - end - - -- use per user limited cache for prosody >= 0.10 - local stores = {}; - return { - get = function(user, key) - if not user then return nil; end - if not key then return nil; end - if not stores[user] then - stores[user] = cache.new(max_entries, evict_callback); - end - return stores[user]:get(key); - end; - set = function(user, key, value) - if not user then return nil; end - if not key then return nil; end - if not stores[user] then stores[user] = cache.new(max_entries, evict_callback); end - stores[user]:set(key, value); - -- remove empty caches completely - if not stores[user]:count() then stores[user] = nil; end - end; - }; -end -local old_session_registry = init_session_cache(max_old_sessions, nil); -local session_registry = init_session_cache(max_hibernated_sessions, function(resumption_token, session) - if session.destroyed then return true; end -- destroyed session can always be removed from cache - session.log("warn", "User has too much hibernated sessions, removing oldest session (token: %s)", resumption_token); - -- store old session's h values on force delete - -- save only actual h value and username/host (for security) - old_session_registry.set(session.username, resumption_token, { - h = session.handled_stanza_count, - username = session.username, - host = session.host - }); - return true; -- allow session to be removed from full cache to make room for new one -end); - -local function stoppable_timer(delay, callback) - local stopped = false; - local timer = module:add_timer(delay, function (t) - if stopped then return; end - return callback(t); - end); - if timer and timer.stop then return timer; end -- new prosody api includes stop() function - return { - stop = function(self) stopped = true end; - timer; - }; -end - -local function delayed_ack_function(session, stanza) - -- fire event only if configured to do so and our session is not already hibernated or destroyed - if delayed_ack_timeout > 0 and session.awaiting_ack - and not session.hibernating and not session.destroyed then - session.log("debug", "Firing event 'smacks-ack-delayed', queue = %d", - session.outgoing_stanza_queue and #session.outgoing_stanza_queue or 0); - module:fire_event("smacks-ack-delayed", {origin = session, queue = session.outgoing_stanza_queue, stanza = stanza}); - end - session.delayed_ack_timer = nil; -end - -local function can_do_smacks(session, advertise_only) - if session.smacks then return false, "unexpected-request", "Stream management is already enabled"; end - - local session_type = session.type; - if session.username then - if not(advertise_only) and not(session.resource) then -- Fail unless we're only advertising sm - return false, "unexpected-request", "Client must bind a resource before enabling stream management"; - end - return true; - elseif s2s_smacks and (session_type == "s2sin" or session_type == "s2sout") then - return true; - end - return false, "service-unavailable", "Stream management is not available for this stream"; -end - -module:hook("stream-features", - function (event) - if can_do_smacks(event.origin, true) then - event.features:tag("sm", sm2_attr):tag("optional"):up():up(); - event.features:tag("sm", sm3_attr):tag("optional"):up():up(); - end - end); - -module:hook("s2s-stream-features", - function (event) - if can_do_smacks(event.origin, true) then - event.features:tag("sm", sm2_attr):tag("optional"):up():up(); - event.features:tag("sm", sm3_attr):tag("optional"):up():up(); - end - end); - -local function request_ack_if_needed(session, force, reason, stanza) - local queue = session.outgoing_stanza_queue; - local expected_h = session.last_acknowledged_stanza + #queue; - -- session.log("debug", "*** SMACKS(1) ***: awaiting_ack=%s, hibernating=%s", tostring(session.awaiting_ack), tostring(session.hibernating)); - local max_unacked = max_unacked_stanzas; - if session.state == "inactive" then - max_unacked = max_inactive_unacked_stanzas; - end - if session.awaiting_ack == nil and not session.hibernating then - -- this check of last_requested_h prevents ack-loops if missbehaving clients report wrong - -- stanza counts. it is set when an <r> is really sent (e.g. inside timer), preventing any - -- further requests until a higher h-value would be expected. - -- session.log("debug", "*** SMACKS(2) ***: #queue=%s, max_unacked_stanzas=%s, expected_h=%s, last_requested_h=%s", tostring(#queue), tostring(max_unacked_stanzas), tostring(expected_h), tostring(session.last_requested_h)); - if (#queue > max_unacked and expected_h ~= session.last_requested_h) or force then - session.log("debug", "Queuing <r> (in a moment) from %s - #queue=%d", reason, #queue); - session.awaiting_ack = false; - session.awaiting_ack_timer = stoppable_timer(1e-06, function () - -- session.log("debug", "*** SMACKS(3) ***: awaiting_ack=%s, hibernating=%s", tostring(session.awaiting_ack), tostring(session.hibernating)); - -- only request ack if needed and our session is not already hibernated or destroyed - if not session.awaiting_ack and not session.hibernating and not session.destroyed then - session.log("debug", "Sending <r> (inside timer, before send) from %s - #queue=%d", reason, #queue); - (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) - if session.destroyed then return end -- sending something can trigger destruction - session.awaiting_ack = true; - -- expected_h could be lower than this expression e.g. more stanzas added to the queue meanwhile) - session.last_requested_h = session.last_acknowledged_stanza + #queue; - session.log("debug", "Sending <r> (inside timer, after send) from %s - #queue=%d", reason, #queue); - if not session.delayed_ack_timer then - session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() - delayed_ack_function(session, nil); -- we don't know if this is the only new stanza in the queue - end); - end - end - end); - end - end - - -- Trigger "smacks-ack-delayed"-event if we added new (ackable) stanzas to the outgoing queue - -- and there isn't already a timer for this event running. - -- If we wouldn't do this, stanzas added to the queue after the first "smacks-ack-delayed"-event - -- would not trigger this event (again). - if #queue > max_unacked and session.awaiting_ack and session.delayed_ack_timer == nil then - session.log("debug", "Calling delayed_ack_function directly (still waiting for ack)"); - delayed_ack_function(session, stanza); -- this is the only new stanza in the queue --> provide it to other modules - end -end - -local function outgoing_stanza_filter(stanza, session) - -- XXX: Normally you wouldn't have to check the xmlns for a stanza as it's - -- supposed to be nil. - -- However, when using mod_smacks with mod_websocket, then mod_websocket's - -- stanzas/out filter can get called before this one and adds the xmlns. - local is_stanza = stanza.attr and - (not stanza.attr.xmlns or stanza.attr.xmlns == 'jabber:client') - and not stanza.name:find":"; - - if is_stanza and not stanza._cached then - local queue = session.outgoing_stanza_queue; - local cached_stanza = st.clone(stanza); - cached_stanza._cached = true; - - if cached_stanza and cached_stanza.name ~= "iq" and cached_stanza:get_child("delay", xmlns_delay) == nil then - cached_stanza = cached_stanza:tag("delay", { - xmlns = xmlns_delay, - from = jid.bare(session.full_jid or session.host), - stamp = datetime.datetime() - }); - end - - queue[#queue+1] = cached_stanza; - if session.hibernating then - session.log("debug", "hibernating since %s, stanza queued", datetime.datetime(session.hibernating)); - module:fire_event("smacks-hibernation-stanza-queued", {origin = session, queue = queue, stanza = cached_stanza}); - return nil; - end - request_ack_if_needed(session, false, "outgoing_stanza_filter", stanza); - end - return stanza; -end - -local function count_incoming_stanzas(stanza, session) - if not stanza.attr.xmlns then - session.handled_stanza_count = session.handled_stanza_count + 1; - session.log("debug", "Handled %d incoming stanzas", session.handled_stanza_count); - end - return stanza; -end - -local function wrap_session_out(session, resume) - if not resume then - session.outgoing_stanza_queue = {}; - session.last_acknowledged_stanza = 0; - end - - add_filter(session, "stanzas/out", outgoing_stanza_filter, -999); - - local session_close = session.close; - function session.close(...) - if session.resumption_token then - session_registry.set(session.username, session.resumption_token, nil); - old_session_registry.set(session.username, session.resumption_token, nil); - session.resumption_token = nil; - end - -- send out last ack as per revision 1.5.2 of XEP-0198 - if session.smacks and session.conn and session.handled_stanza_count then - (session.sends2s or session.send)(st.stanza("a", { xmlns = session.smacks, h = string.format("%d", session.handled_stanza_count) })); - end - return session_close(...); - end - return session; -end - -local function wrap_session_in(session, resume) - if not resume then - session.handled_stanza_count = 0; - end - add_filter(session, "stanzas/in", count_incoming_stanzas, 999); - - return session; -end - -local function wrap_session(session, resume) - wrap_session_out(session, resume); - wrap_session_in(session, resume); - return session; -end - -function handle_enable(session, stanza, xmlns_sm) - local ok, err, err_text = can_do_smacks(session); - if not ok then - session.log("warn", "Failed to enable smacks: %s", err_text); -- TODO: XEP doesn't say we can send error text, should it? - (session.sends2s or session.send)(st.stanza("failed", { xmlns = xmlns_sm }):tag(err, { xmlns = xmlns_errors})); - return true; - end - - module:log("debug", "Enabling stream management"); - session.smacks = xmlns_sm; - - wrap_session(session, false); - - local resume_token; - local resume = stanza.attr.resume; - if resume == "true" or resume == "1" then - resume_token = uuid_generate(); - session_registry.set(session.username, resume_token, session); - session.resumption_token = resume_token; - end - (session.sends2s or session.send)(st.stanza("enabled", { xmlns = xmlns_sm, id = resume_token, resume = resume, max = tostring(resume_timeout) })); - return true; -end -module:hook_stanza(xmlns_sm2, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm2); end, 100); -module:hook_stanza(xmlns_sm3, "enable", function (session, stanza) return handle_enable(session, stanza, xmlns_sm3); end, 100); - -module:hook_stanza("http://etherx.jabber.org/streams", "features", - function (session, stanza) - stoppable_timer(1e-6, function () - if can_do_smacks(session) then - if stanza:get_child("sm", xmlns_sm3) then - session.sends2s(st.stanza("enable", sm3_attr)); - session.smacks = xmlns_sm3; - elseif stanza:get_child("sm", xmlns_sm2) then - session.sends2s(st.stanza("enable", sm2_attr)); - session.smacks = xmlns_sm2; - else - return; - end - wrap_session_out(session, false); - end - end); - end); - -function handle_enabled(session, stanza, xmlns_sm) - module:log("debug", "Enabling stream management"); - session.smacks = xmlns_sm; - - wrap_session_in(session, false); - - -- FIXME Resume? - - return true; -end -module:hook_stanza(xmlns_sm2, "enabled", function (session, stanza) return handle_enabled(session, stanza, xmlns_sm2); end, 100); -module:hook_stanza(xmlns_sm3, "enabled", function (session, stanza) return handle_enabled(session, stanza, xmlns_sm3); end, 100); - -function handle_r(origin, stanza, xmlns_sm) - if not origin.smacks then - module:log("debug", "Received ack request from non-smack-enabled session"); - return; - end - module:log("debug", "Received ack request, acking for %d", origin.handled_stanza_count); - -- Reply with <a> - (origin.sends2s or origin.send)(st.stanza("a", { xmlns = xmlns_sm, h = string.format("%d", origin.handled_stanza_count) })); - -- piggyback our own ack request if needed (see request_ack_if_needed() for explanation of last_requested_h) - local expected_h = origin.last_acknowledged_stanza + #origin.outgoing_stanza_queue; - if #origin.outgoing_stanza_queue > 0 and expected_h ~= origin.last_requested_h then - request_ack_if_needed(origin, true, "piggybacked by handle_r", nil); - end - return true; -end -module:hook_stanza(xmlns_sm2, "r", function (origin, stanza) return handle_r(origin, stanza, xmlns_sm2); end); -module:hook_stanza(xmlns_sm3, "r", function (origin, stanza) return handle_r(origin, stanza, xmlns_sm3); end); - -function handle_a(origin, stanza) - if not origin.smacks then return; end - origin.awaiting_ack = nil; - if origin.awaiting_ack_timer then - origin.awaiting_ack_timer:stop(); - end - if origin.delayed_ack_timer then - origin.delayed_ack_timer:stop(); - origin.delayed_ack_timer = nil; - end - -- Remove handled stanzas from outgoing_stanza_queue - -- origin.log("debug", "ACK: h=%s, last=%s", stanza.attr.h or "", origin.last_acknowledged_stanza or ""); - local h = tonumber(stanza.attr.h); - if not h then - origin:close{ condition = "invalid-xml"; text = "Missing or invalid 'h' attribute"; }; - return; - end - local handled_stanza_count = h-origin.last_acknowledged_stanza; - local queue = origin.outgoing_stanza_queue; - if handled_stanza_count > #queue then - origin.log("warn", "The client says it handled %d new stanzas, but we only sent %d :)", - handled_stanza_count, #queue); - origin.log("debug", "Client h: %d, our h: %d", tonumber(stanza.attr.h), origin.last_acknowledged_stanza); - for i=1,#queue do - origin.log("debug", "Q item %d: %s", i, tostring(queue[i])); - end - origin:close{ condition = "undefined-condition"; text = "Client acknowledged more stanzas than sent by server"; }; - return; - end - - for i=1,math_min(handled_stanza_count,#queue) do - local handled_stanza = t_remove(origin.outgoing_stanza_queue, 1); - module:fire_event("delivery/success", { session = origin, stanza = handled_stanza }); - end - - origin.log("debug", "#queue = %d", #queue); - origin.last_acknowledged_stanza = origin.last_acknowledged_stanza + handled_stanza_count; - request_ack_if_needed(origin, false, "handle_a", nil) - return true; -end -module:hook_stanza(xmlns_sm2, "a", handle_a); -module:hook_stanza(xmlns_sm3, "a", handle_a); - ---TODO: Optimise... incoming stanzas should be handled by a per-session --- function that has a counter as an upvalue (no table indexing for increments, --- and won't slow non-198 sessions). We can also then remove the .handled flag --- on stanzas - -local function handle_unacked_stanzas(session) - local queue = session.outgoing_stanza_queue; - local error_attr = { type = "cancel" }; - if #queue > 0 then - session.outgoing_stanza_queue = {}; - for i=1,#queue do - if not module:fire_event("delivery/failure", { session = session, stanza = queue[i] }) then - if queue[i].attr.type ~= "error" then - local reply = st.reply(queue[i]); - if reply.attr.to ~= session.full_jid then - reply.attr.type = "error"; - reply:tag("error", error_attr) - :tag("recipient-unavailable", {xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"}); - core_process_stanza(session, reply); - end - end - end - end - end -end - --- don't send delivery errors for messages which will be delivered by mam later on --- check if stanza was archived --> this will allow us to send back errors for stanzas not archived --- because the user configured the server to do so ("no-archive"-setting for one special contact for example) -local function get_stanza_id(stanza, by_jid) - for tag in stanza:childtags("stanza-id", "urn:xmpp:sid:0") do - if tag.attr.by == by_jid then - return tag.attr.id; - end - end - return nil; -end -module:hook("delivery/failure", function(event) - local session, stanza = event.session, event.stanza; - -- Only deal with authenticated (c2s) sessions - if session.username then - if stanza.name == "message" and stanza.attr.xmlns == nil and - ( stanza.attr.type == "chat" or ( stanza.attr.type or "normal" ) == "normal" ) then - -- don't store messages in offline store if they are mam results - local mam_result = stanza:get_child("result", xmlns_mam2); - if mam_result ~= nil then - return true; -- stanza already "handled", don't send an error and don't add it to offline storage - end - -- do nothing here for normal messages and don't send out "message delivery errors", - -- because messages are already in MAM at this point (no need to frighten users) - local stanza_id = get_stanza_id(stanza, jid.bare(session.full_jid)); - if session.mam_requested and stanza_id ~= nil then - session.log("debug", "mod_smacks delivery/failure returning true for mam-handled stanza: mam-archive-id=%s", tostring(stanza_id)); - return true; -- stanza handled, don't send an error - end - -- store message in offline store, if this client does not use mam *and* was the last client online - local sessions = prosody.hosts[module.host].sessions[session.username] and - prosody.hosts[module.host].sessions[session.username].sessions or nil; - if sessions and next(sessions) == session.resource and next(sessions, session.resource) == nil then - local ok = module:fire_event("message/offline/handle", { origin = session, username = session.username, stanza = stanza }); - session.log("debug", "mod_smacks delivery/failuere returning %s for offline-handled stanza", tostring(ok)); - return ok; -- if stanza was handled, don't send an error - end - end - end -end); - -module:hook("pre-resource-unbind", function (event) - local session, err = event.session, event.error; - if session.smacks then - if not session.resumption_token then - local queue = session.outgoing_stanza_queue; - if #queue > 0 then - session.log("debug", "Destroying session with %d unacked stanzas", #queue); - handle_unacked_stanzas(session); - end - else - session.log("debug", "mod_smacks hibernating session for up to %d seconds", resume_timeout); - local hibernate_time = os_time(); -- Track the time we went into hibernation - session.hibernating = hibernate_time; - local resumption_token = session.resumption_token; - module:fire_event("smacks-hibernation-start", {origin = session, queue = session.outgoing_stanza_queue}); - timer.add_task(resume_timeout, function () - session.log("debug", "mod_smacks hibernation timeout reached..."); - -- We need to check the current resumption token for this resource - -- matches the smacks session this timer is for in case it changed - -- (for example, the client may have bound a new resource and - -- started a new smacks session, or not be using smacks) - local curr_session = full_sessions[session.full_jid]; - if session.destroyed then - session.log("debug", "The session has already been destroyed"); - elseif curr_session and curr_session.resumption_token == resumption_token - -- Check the hibernate time still matches what we think it is, - -- otherwise the session resumed and re-hibernated. - and session.hibernating == hibernate_time then - -- wait longer if the timeout isn't reached because push was enabled for this session - -- session.first_hibernated_push is the starting point for hibernation timeouts of those push enabled clients - -- wait for an additional resume_timeout seconds if no push occurred since hibernation at all - local current_time = os_time(); - local timeout_start = math_max(session.hibernating, session.first_hibernated_push or session.hibernating); - if session.push_identifier ~= nil and not session.first_hibernated_push then - session.log("debug", "No push happened since hibernation started, hibernating session for up to %d extra seconds", resume_timeout); - return resume_timeout; - end - if session.push_identifier ~= nil and current_time-timeout_start < resume_timeout then - session.log("debug", "A push happened since hibernation started, hibernating session for up to %d extra seconds", resume_timeout-(current_time-timeout_start)); - return resume_timeout-(current_time-timeout_start); -- time left to wait - end - session.log("debug", "Destroying session for hibernating too long"); - session_registry.set(session.username, session.resumption_token, nil); - -- save only actual h value and username/host (for security) - old_session_registry.set(session.username, session.resumption_token, { - h = session.handled_stanza_count, - username = session.username, - host = session.host - }); - session.resumption_token = nil; - sessionmanager.destroy_session(session); - else - session.log("debug", "Session resumed before hibernation timeout, all is well") - end - end); - return true; -- Postpone destruction for now - end - end -end); - -local function handle_s2s_destroyed(event) - local session = event.session; - local queue = session.outgoing_stanza_queue; - if queue and #queue > 0 then - session.log("warn", "Destroying session with %d unacked stanzas", #queue); - if s2s_resend then - for i = 1, #queue do - module:send(queue[i]); - end - session.outgoing_stanza_queue = nil; - else - handle_unacked_stanzas(session); - end - end -end - -module:hook("s2sout-destroyed", handle_s2s_destroyed); -module:hook("s2sin-destroyed", handle_s2s_destroyed); - -local function get_session_id(session) - return session.id or (tostring(session):match("[a-f0-9]+$")); -end - -function handle_resume(session, stanza, xmlns_sm) - if session.full_jid then - session.log("warn", "Tried to resume after resource binding"); - session.send(st.stanza("failed", { xmlns = xmlns_sm }) - :tag("unexpected-request", { xmlns = xmlns_errors }) - ); - return true; - end - - local id = stanza.attr.previd; - local original_session = session_registry.get(session.username, id); - if not original_session then - session.log("debug", "Tried to resume non-existent session with id %s", id); - local old_session = old_session_registry.get(session.username, id); - if old_session and session.username == old_session.username - and session.host == old_session.host - and old_session.h then - session.send(st.stanza("failed", { xmlns = xmlns_sm, h = string.format("%d", old_session.h) }) - :tag("item-not-found", { xmlns = xmlns_errors }) - ); - else - session.send(st.stanza("failed", { xmlns = xmlns_sm }) - :tag("item-not-found", { xmlns = xmlns_errors }) - ); - end; - elseif session.username == original_session.username - and session.host == original_session.host then - session.log("debug", "mod_smacks resuming existing session %s...", get_session_id(original_session)); - original_session.log("debug", "mod_smacks session resumed from %s...", get_session_id(session)); - -- TODO: All this should move to sessionmanager (e.g. session:replace(new_session)) - if original_session.conn then - original_session.log("debug", "mod_smacks closing an old connection for this session"); - local conn = original_session.conn; - c2s_sessions[conn] = nil; - conn:close(); - end - local migrated_session_log = session.log; - original_session.ip = session.ip; - original_session.conn = session.conn; - original_session.send = session.send; - original_session.close = session.close; - original_session.filter = session.filter; - original_session.filter.session = original_session; - original_session.filters = session.filters; - original_session.stream = session.stream; - original_session.secure = session.secure; - original_session.hibernating = nil; - session.log = original_session.log; - session.type = original_session.type; - wrap_session(original_session, true); - -- Inform xmppstream of the new session (passed to its callbacks) - original_session.stream:set_session(original_session); - -- Similar for connlisteners - c2s_sessions[session.conn] = original_session; - - original_session.send(st.stanza("resumed", { xmlns = xmlns_sm, - h = string.format("%d", original_session.handled_stanza_count), previd = id })); - - -- Fake an <a> with the h of the <resume/> from the client - original_session:dispatch_stanza(st.stanza("a", { xmlns = xmlns_sm, - h = stanza.attr.h })); - - -- Ok, we need to re-send any stanzas that the client didn't see - -- ...they are what is now left in the outgoing stanza queue - -- We have to use the send of "session" because we don't want to add our resent stanzas - -- to the outgoing queue again - local queue = original_session.outgoing_stanza_queue; - session.log("debug", "resending all unacked stanzas that are still queued after resume, #queue = %d", #queue); - for i=1,#queue do - session.send(queue[i]); - end - session.log("debug", "all stanzas resent, now disabling send() in this migrated session, #queue = %d", #queue); - function session.send(stanza) - migrated_session_log("error", "Tried to send stanza on old session migrated by smacks resume (maybe there is a bug?): %s", tostring(stanza)); - return false; - end - module:fire_event("smacks-hibernation-end", {origin = session, resumed = original_session, queue = queue}); - request_ack_if_needed(original_session, true, "handle_resume", nil); - else - module:log("warn", "Client %s@%s[%s] tried to resume stream for %s@%s[%s]", - session.username or "?", session.host or "?", session.type, - original_session.username or "?", original_session.host or "?", original_session.type); - session.send(st.stanza("failed", { xmlns = xmlns_sm }) - :tag("not-authorized", { xmlns = xmlns_errors })); - end - return true; -end -module:hook_stanza(xmlns_sm2, "resume", function (session, stanza) return handle_resume(session, stanza, xmlns_sm2); end); -module:hook_stanza(xmlns_sm3, "resume", function (session, stanza) return handle_resume(session, stanza, xmlns_sm3); end); - -module:hook("csi-client-active", function (event) - if event.origin.smacks then - request_ack_if_needed(event.origin, true, "csi-active", nil); - end -end); - -module:hook("csi-flushing", function(event) - local session = event.session; - if session.smacks then - if not session.awaiting_ack and not session.hibernating and not session.destroyed then - session.log("debug", "Sending <r> (csi-flushing)"); - session.awaiting_ack = true; -- The send() call may invoke this event again, so set this first - (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })) - end - end -end); - -local function handle_read_timeout(event) - local session = event.session; - if session.smacks then - if session.awaiting_ack then - if session.awaiting_ack_timer then - session.awaiting_ack_timer:stop(); - end - if session.delayed_ack_timer then - session.delayed_ack_timer:stop(); - session.delayed_ack_timer = nil; - end - return false; -- Kick the session - end - session.log("debug", "Sending <r> (read timeout)"); - (session.sends2s or session.send)(st.stanza("r", { xmlns = session.smacks })); - session.awaiting_ack = true; - if not session.delayed_ack_timer then - session.delayed_ack_timer = stoppable_timer(delayed_ack_timeout, function() - delayed_ack_function(session, nil); - end); - end - return true; - end -end - -module:hook("s2s-read-timeout", handle_read_timeout); -module:hook("c2s-read-timeout", handle_read_timeout);
--- a/mod_smacks_noerror/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Module deprecated, just use mod_smacks and mod_nooffline_noerror -... - -Introduction -============ - -This module is deprecated and superseded by mod_smacks. -If you explicitly disabled mod_offline you need the new module -mod_nooffline_noerror to regain all features of this deprecated module.
--- a/mod_smacks_offline/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: Module deprecated, just use mod_smacks -... - -Introduction -============ - -This module is deprecated and superseded by mod_smacks.
--- a/mod_smacks_offline/mod_smacks_offline.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ --- this module is deprecated, log an error and load the superseding module instead -module:depends"smacks" - -module:log("error", "mod_smacks_offline is deprecated! Just use mod_smacks!"); \ No newline at end of file
--- a/mod_sms_clickatell/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: XMPP to SMS gateway using the Clickatell API -... - -Introduction -============ - -This module provides and SMS gateway component which uses the Clickatell -HTTP API to deliver text messages. See clickatell.com for details on -their services. Note that at present, this is entirely one way: replies -will either go nowhere or as sms to the source number you specify. - -Configuration -============= - -In prosody.cfg.lua: - - Component "sms.example.com" "sms_clickatell" - sms_message_prefix = "some text" - -The sms\_message\_prefix is a piece of text you want prefixing to all -messages sent through the gateway. For example, I use the prefix -"`[Via XMPP]` " to indicate to recipients that I've sent the message via -the internet rather than the mobile network. Since my primary use case -for this component is to be able to send messages to people only -reachable via mobile when I myself only have internet access and no -mobile reception, this option allows me to give a hint to my recipients -that any reply they send may not reach me in a timely manner. - -Usage -===== - -Once you've installed and configured, you should be able to use service -discovery in your XMPP client to find the component service. Once found, -you need to register with the service, supplying your Clickatell -username, password, API ID, and a source number for your text messages. - -The source number is the mobile number you want messages to 'originate' -from i.e. where your recipients see messages coming from. The number -should be in international format without leading plus sign, or you can -use some other format if clickatell supports it. - -To send text messages to a target number, you need to add a contact in -the form of `[number]@sms.example.com`, where `[number]` is the mobile -number of the recipient, in international format without leading plus -sign, and sms.example.com is the name for the component you configured -above. For example: - -447999000001@sms.yourdomain.com - -You should then be able to send messages to this contact which get sent -as text messages to the number by the component. - -Compatibility -============= - - ----- ------- - 0.7 Works - ----- ------- - -Todo -==== - -- Refactor to create a framework for multiple sms gateway back ends, - and split Clickatell specific code in to its own back end
--- a/mod_sms_free/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: XMPP to SMS gateway using the HTTP API provided by mobile.free.fr -... - -Introduction -============ - -This module sends an SMS to your phone when you receive a message on XMPP when -your status is xa or disconnected. - -Note that it doesn’t support sending SMS to anyone else than yourself, in that -it is quite different from other gateways. - -Configuration -============= - -In prosody.cfg.lua: - - modules_enabled = { - "sms_free", - } - -Usage -===== - -Every user who wants to use this gateway can issue an ad-hoc command to their -server, then follow the instructions and start receiving messages by SMS when -they are unavailable or xa. - -Compatibility -============= - - ----- ------------------------------------ - trunk Works - 0.11 Does not work (SNI support required) - ----- ------------------------------------
--- a/mod_spam_reporting/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'XEP-0377: Spam Reporting' ---- - -This module is a very basic implementation of [XEP-0377: Spam Reporting]. - -When someone reports spam or abuse, a line about this is logged and an -event is fired so that other modules can act on the report.
--- a/mod_srvinjection/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Manually specify SRV records -... - -Introduction -============ - -This Prosody plugin lets you manually override SRV records used for a -remote host. - -Usage -===== - -Simply add `"srvinjection"` to your `modules_enabled` list to enable. -Then add the `srvinjection` option to the global section. - -Configuration -============= - -The `srvinjection` option can be used as follows: - - srvinjection = { - ["example.com"] = {"localhost", 5000}; - ["jabber.org"] = {"localhost", 5001}; - }; - -The format for individual items is -`["remote-hostname"] = {"srv-hostname", srv-port};`. - -The special remote hostname `"*"` can be used as a wildcard: - - srvinjection = { ["*"] = {"xmpp-server.l.google.com", 5269} } -- Use Google's XMPP server for all hostnames - -Reloading -========= - -The module can be reloaded via the telnet console. Edit the config file -to make any updates. - -You can reload the configuration from disk: - - config:reload() - -And then reload the module to apply the configuration changes: - - module:reload("srvinjection", "*") - -Compatibility -============= - - ----- ------- - 0.8 Works - 0.7 Works - 0.6 Works - ----- ------- - -How it works -============ - -The module replaces the `lookup` function of the `net.adns` module with -its own. The original is set back when the module is unloaded.
--- a/mod_stanza_counter/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Simple incoming and outgoing stanza counter -... - -Introduction -============ - -This module counts incoming and outgoing stanzas from when the instance -started, and makes the data available to other modules by creating a -global prosody. object - -Details -======= - -The counter module is "stanza\_counter", the example output module is -stanza\_counter\_http. - -Usage -===== - -Copy both files into prosody's module directory and place 'em into your -enabled modules (stanza\_counter\_http requires to be loaded into the -global section!) - -Config for stanza\_counter\_http: - -``` {.lua} - -stanza_counter_basepath = "/counter-path-custom/" -``` - -Info -==== - -- As of now to count components stanzas, it needs to be manually - loaded (inserted into modules\_enabled of the components' sections) - on these. -- This version isn't compatible with previous versions of prosody - (looks at 0.8-diverge branch for olders).
--- a/mod_stanzadebug/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ ---- -summary: Extra verbose stanza logging ---- - -Summary -======= - -This module logs the full stanzas that are sent and received into debug -logs, for debugging purposes.
--- a/mod_statistics_statsman/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -A module providing a streaming statistics interface like -[mod_statistics] but based on the new [statistics API][doc:statistics] -introduced in Prosody 0.10. - -# Usage - -To use, enable the built-in statistics like so: - -```lua -statistics = "internal" -``` - -Then, in `modules_enabled`, replace `"statistics"` with -`"statistics_statsman"` and the various `"statistics_<something>"` -with equivalent `"measure_<something>"`. - - -# Compatibility - - ------- -------------------- - trunk Does not work [^1] - 0.11 Should work - 0.10 Should work - ------- -------------------- - -[^1]: not after - [5f15ab7c6ae5](https://hg.prosody.im/trunk/rev/5f15ab7c6ae5)
--- a/mod_stats39/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ ---- -labels: -- 'Statistics' -... - -This module provides **public** access to Prosodys -[internal statistics][doc:statistics] trough the -[XEP-0039: Statistics Gathering] protocol. This is a simple protocol -that returns triplets of name, unit and value for each know statistic -collected by Prosody. The names used are the internal names assigned by -modules or statsmanager, names from the registry are **not** used. - -# Configuration - -Enabled as usual by adding to [`modules_enabled`][doc:modules_enabled]: - -```lua --- Enable Prosodys internal statistics gathering -statistics = "internal" - --- and enable the module -modules_enabled = { - -- other modules - "stats39"; -} -``` - -# Usage - - -## Example - -Statistics can be queried from the XML console of clients that have one: - -```xml -C: -<iq type="get" to="example.com" id="dTMERjt5"> - <query xmlns="http://jabber.org/protocol/stats"/> -</iq> - -S: -<iq type="result" to="example.com" id="dTMERjt5"> - <query xmlns="http://jabber.org/protocol/stats"> - <stat name="cpu.clock:amount" value="0.212131"/> - <stat name="cpu.percent:amount" value="0"/> - <stat name="memory.allocated:amount" value="8.30259e+06"/> - <stat name="memory.allocated_mmap:amount" value="401408"/> - <stat name="memory.lua:amount" value="6.21347e+06"/> - <stat name="memory.returnable:amount" value="13872"/> - <stat name="memory.rss:amount" value="2.03858e+07"/> - <stat name="memory.total:amount" value="6.53885e+07"/> - <stat name="memory.unused:amount" value="14864"/> - <stat name="memory.used:amount" value="8.28773e+06"/> - <stat name="/*/mod_c2s/connections:amount" value="0"/> - <stat name="/*/mod_c2s/ipv6:amount" value="0"/> - <stat name="/*/mod_s2s/connections:amount" value="0"/> - <stat name="/*/mod_s2s/ipv6:amount" value="0"/> - <stat name="stats.collection:duration" unit="seconds" value="0.000125647"/> - <stat name="stats.processing:duration" unit="seconds" value="0"/> - </query> -</iq> -``` - -# Compatibly - - Prosody version Works - ----------------- ------- - 0.9.x No - 0.10.x Yes - 0.11.x Yes - Trunk[^1] No - -[^1]: Does not work with trunk since the [change to - OpenMetrics](https://hg.prosody.im/trunk/rev/5f15ab7c6ae5)
--- a/mod_storage_appendmap/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- 'Type-Storage' -summary: Experimental map store optimized for small incremental changes -... - -This is an experimental storage driver where changed data is appended. -Data is simply written as `key = value` pairs to the end of the file. -This allows changes to individual keys to be written without needing to -write out the entire object again, but reads would grow gradually larger -as it still needs to read old overwritten keys. This may be suitable for -e.g. rosters where individual contacts are changed at a time. In theory, -this could also allow rolling back changes. - -Requires 0.10
--- a/mod_storage_ejabberdsql_readonly/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Type-Storage' -- 'Stage-Alpha' -summary: Ejabberd SQL Read-only Storage Module -... - -Introduction -============ - -This is a storage backend using Ejabberd’s SQL backend. It depends on -[LuaDBI][doc:depends#luadbi] - -This module only works in read-only, and was made to be used by -[mod_migrate] to migrate from Ejabberd’s SQL backend. - -Configuration -============= - -Copy the module to the prosody modules/plugins directory. - -In Prosody's configuration file, set: - - storage = "ejabberdsql_readonly" - -EjabberdSQL options are the same as the [SQL -ones][doc:modules:mod_storage_sql#usage]. - -Compatibility -============= - - ------- --------------------------- - trunk Works - 0.10 Untested, but should work - 0.9 Does not work - ------- ---------------------------
--- a/mod_storage_gdbm/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Type-Storage' -- ArchiveStorage -summary: 'Lua-GDBM storage' -... - -Introduction -============ - -This is a storage module using GNU DBM as backend. It supports archives. - -Dependencies -============ - -[lgdbm](http://webserver2.tecgraf.puc-rio.br/~lhf/ftp/lua/#lgdbm) is -required to access gdbm databases. - -Details -======= - -Refer to [Prosodys data storage -documentation](https://prosody.im/doc/storage). - -Compatibility -============= - - -------- ------------- - \>=0.9 Should work - -------- -------------
--- a/mod_storage_ldap/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ ---- -labels: -- 'Type-Storage' -summary: 'LDAP storage for rosters, groups, and vcards' -... - -Introduction -============ - -See [mod\_lib\_ldap](mod_lib_ldap.html) for more information. - -Installation -============ - -You must install [mod\_lib\_ldap](mod_lib_ldap.html) to use this module. -After that, you need only copy mod\_storage\_ldap.lua and -ldap/vcard.lib.lua to your Prosody installation's plugins directory. -Make sure vcard.lib.lua is installed under plugins/ldap/. - -Configuration -============= - -In addition to the configuration that [mod\_lib\_ldap](mod_lib_ldap.html) -itself requires, this plugin also requires the following fields in the -ldap section: - -- user.namefield -- groups.memberfield -- groups.namefield -- groups.basedn -- vcard\_format (optional) - -See the README.html distributed with [mod\_lib\_ldap](mod_lib_ldap.html) for -details.
--- a/mod_storage_lmdb/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Type-Storage' -summary: 'Lightning Memory-Mapped Database storage' -... - -Introduction -============ - -This is a storage module using OpenLDAP Lightning Memory-Mapped Database -as backend. - -Dependencies -============ - -[lightningdbm](https://github.com/shmul/lightningdbm) is required to -access LMDB databases. - -Details -======= - -Refer to [Prosodys data storage -documentation](https://prosody.im/doc/storage). - -Compatibility -============= - - -------- ------------- - \>=0.9 Should work - -------- -------------
--- a/mod_storage_memory/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ---- -labels: -- 'Stage-Merged' -- 'Type-Storage' -- ArchiveStorage -summary: 'Simple memory-only storage module' -... - -Introduction -============ - -This module acts as a normal storage module for Prosody, but saves all -data in memory only. All data is lost when the server stops. This makes -it useful for testing, or certain specialized applications. - -Details -======= - -Because the accounts store will always begin empty, it is mostly useful -combined with an authentication plugin which doesn't use Prosody's -storage API, or with [mod\_auth\_any](mod_auth_any.html), or you can -create user accounts manually each time the server starts. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_storage_memory/mod_storage_memory.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,178 +0,0 @@ -local serialize = require "util.serialization".serialize; -local envload = require "util.envload".envload; -local st = require "util.stanza"; -local is_stanza = st.is_stanza or function (s) return getmetatable(s) == st.stanza_mt end - -local auto_purge_enabled = module:get_option_boolean("storage_memory_temporary", false); -local auto_purge_stores = module:get_option_set("storage_memory_temporary_stores", {}); - -local memory = setmetatable({}, { - __index = function(t, k) - local store = module:shared(k) - t[k] = store; - return store; - end -}); - -local function NULL() return nil end - -local function _purge_store(self, username) - self.store[username or NULL] = nil; - return true; -end - -local keyval_store = {}; -keyval_store.__index = keyval_store; - -function keyval_store:get(username) - return (self.store[username or NULL] or NULL)(); -end - -function keyval_store:set(username, data) - if data ~= nil then - data = envload("return "..serialize(data), "@data", {}); - end - self.store[username or NULL] = data; - return true; -end - -keyval_store.purge = _purge_store; - -local archive_store = {}; -archive_store.__index = archive_store; - -function archive_store:append(username, key, value, when, with) - if type(when) ~= "number" then - when, with, value = value, when, with; - end - if is_stanza(value) then - value = st.preserialize(value); - value = envload("return xml"..serialize(value), "@stanza", { xml = st.deserialize }) - else - value = envload("return "..serialize(value), "@data", {}); - end - local a = self.store[username or NULL]; - if not a then - a = {}; - self.store[username or NULL] = a; - end - local i = #a+1; - local v = { key = key, when = when, with = with, value = value }; - if not key then - key = tostring(a):match"%x+$"..tostring(v):match"%x+$"; - v.key = key; - end - if a[key] then - table.remove(a, a[key]); - end - a[i] = v; - a[key] = i; - return key; -end - -local function archive_iter (a, start, stop, step, limit, when_start, when_end, match_with) - local item, when, with; - local count = 0; - coroutine.yield(true); -- Ready - for i = start, stop, step do - item = a[i]; - when, with = item.when, item.with; - if when >= when_start and when_end >= when and (not match_with or match_with == with) then - coroutine.yield(item.key, item.value(), when, with); - count = count + 1; - if limit and count >= limit then return end - end - end -end - -function archive_store:find(username, query) - local a = self.store[username or NULL] or {}; - local start, stop, step = 1, #a, 1; - local qstart, qend, qwith = -math.huge, math.huge; - local limit; - if query then - module:log("debug", "query included") - if query.reverse then - start, stop, step = stop, start, -1; - if query.before then - start = a[query.before]; - end - elseif query.after then - start = a[query.after]; - end - limit = query.limit; - qstart = query.start or qstart; - qend = query["end"] or qend; - qwith = query.with; - end - if not start then return nil, "invalid-key"; end - local iter = coroutine.wrap(archive_iter); - iter(a, start, stop, step, limit, qstart, qend, qwith); - return iter; -end - -function archive_store:delete(username, query) - if not query or next(query) == nil then - self.store[username or NULL] = nil; - return true; - end - local old = self.store[username or NULL]; - if not old then return true; end - local qstart = query.start or -math.huge; - local qend = query["end"] or math.huge; - local qwith = query.with; - local new = {}; - self.store[username or NULL] = new; - local t; - for i = 1, #old do - i = old[i]; - t = i.when; - if not(qstart >= t and qend <= t and (not qwith or i.with == qwith)) then - self:append(username, i.key, i.value(), t, i.with); - end - end - if #new == 0 then - self.store[username or NULL] = nil; - end - return true; -end - -archive_store.purge = _purge_store; - -local stores = { - keyval = keyval_store; - archive = archive_store; -} - -local driver = {}; - -function driver:open(store, typ) -- luacheck: ignore 212/self - local store_mt = stores[typ or "keyval"]; - if store_mt then - return setmetatable({ store = memory[store] }, store_mt); - end - return nil, "unsupported-store"; -end - -if auto_purge_enabled then - module:hook("resource-unbind", function (event) - local user_bare_jid = event.session.username.."@"..event.session.host; - if not prosody.bare_sessions[user_bare_jid] then -- User went offline - module:log("debug", "Clearing store for offline user %s", user_bare_jid); - local f, s, v; - if auto_purge_stores:empty() then - f, s, v = pairs(memory); - else - f, s, v = auto_purge_stores:items(); - end - - for store_name in f, s, v do - if memory[store_name] then - memory[store_name][event.session.username] = nil; - end - end - end - end); -end - -module:provides("storage", driver);
--- a/mod_storage_mongodb/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ ---- -labels: -- 'Type-Storage' -- 'Stage-Alpha' -summary: MongoDB Storage Module -... - -Introduction -============ - -This is a storage backend that uses MongoDB. Depends on [luamongo -bindings](https://github.com/mwild1/luamongo) - -This module is not under active development and has a number of issues -related to limitations in MongoDB. It is not suitable for production -use. - -Configuration -============= - -Copy the module to the prosody modules/plugins directory. - -In Prosody's configuration file, set: - - storage = "mongodb" - -MongoDB options are: - - Name Description - ------------ ------------------------------------------------------------------- - server hostname:port - username your username for the given database - password your password for the given database (either raw or pre-digested) - is\_digest whether the password field has been pre-digested - -Compatibility -============= - - ------- --------------------------- - trunk Untested, but should work - ------- ---------------------------
--- a/mod_storage_muc_log/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -- ArchiveStorage -summary: 'Storage module using mod\_muc\_log data with new stanza archive API' ---- - -Introduction -============ - -[mod\_muc\_log] provided logging of chatrooms running on the server to -Prosody's data store. This module gives access to this data using the -0.10+ stanza archive API, allowing legacy log data to be used with -[mod\_mam\_muc] and [mod\_http\_muc\_log]. - -Details -======= - -Replace [mod\_muc\_log] and [mod\_muc\_log\_http] in your config -with - -``` {.lua} -Component "conference.example.org" "muc" -modules_enabled = { - -- "muc_log"; -- functionality replaced by mod_mam_muc + mod_storage_muc_log - "mam_muc"; -- Does logging to storage backend configured below - - -- "muc_log_http"; -- Replaced by the mod_http_muc_log - "http_muc_log"; -} -storage = { - muc_log = "muc_log"; -} -``` - -Compatibility -============= - - version status - --------- --------------- - 0.9 unknown - 0.10 works - 0.11 does not work
--- a/mod_storage_muconference_readonly/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ ---- -labels: -- 'Type-Storage' -- 'Stage-Alpha' -summary: MU-Conference SQL Read-only Storage Module -... - -Introduction -============ - -This is a storage backend using MU-Conference’s SQL storage. It depends -on [LuaDBI][doc:depends#luadbi] - -This module only works in read-only, and was made to be used by -[mod_migrate] to migrate from MU-Conference’s SQL storage. - -You may need to convert your 'rooms' and 'rooms\_lists' tables to -utf8mb4 before running that script, in order not to end up with -mojibake. Note that MySQL doesn’t support having more than -191 characters in the jid field in this case, so you may have to change -the table schema as well. - -Configuration -============= - -Copy the module to the prosody modules/plugins directory. - -In Prosody's configuration file, set: - - storage = "muconference_readonly" - -MUConferenceSQL options are the same as the [SQL -ones][doc:modules:mod_storage_sql#usage]. - -Compatibility -============= - - ------- --------------------------- - trunk Works - 0.10 Untested, but should work - 0.9 Does not work - ------- ---------------------------
--- a/mod_storage_multi/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ ---- -summary: Multi-backend storage module (WIP) -labels: -- NeedDocs -- Stage-Alpha -... - -Introduction -============ - -This module attemtps to provide a storage driver that is really multiple -storage drivers. This could be used for storage error tolerance or -caching of data in a faster storage driver. - -Configuration -============= - -An example: - -``` {.lua} -storage = "multi" -storage_multi_policy = "all" -storage_multi = { - "memory", - "internal", - "sql" -} -``` - -Here data would be first read from or written to [mod\_storage\_memory], -then internal storage, then SQL storage. For reads, the first successful -read will be used. For writes, it depends on the `storage_multi_policy` -option. If set to `"all"`, then all storage backends must report success -for the write to be considered successful. Other options are `"one"` and -`"majority"`.
--- a/mod_storage_xmlarchive/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ ---- -labels: -- 'Stage-Beta' -- 'Type-Storage' -- ArchiveStorage -summary: XML file based archive storage ---- - -Introduction -============ - -This module implements stanza archives using files, similar to the -default "internal" storage. Unlike "internal", it saves messages in two -files per day (and per user), one containing metadata and one containing -the actual messages in XML format (hence the name). - -Splitting data per day improves performance for larger archives as it -does not have to look through data from other days. - -Configuration -============= - -To use this with [mod\_mam] add this to your config: - -``` lua -storage = { - archive = "xmlarchive" -} -``` - -To use it with [mod\_mam\_muc] or [mod\_http\_muc\_log]: - -``` lua -storage = { - muc_log = "xmlarchive" -} -``` - -Refer to [Prosodys data storage documentation][doc:storage] for more -information. - -Note that this module does not implement the "keyval" storage method and -can't be used by anything other than archives. - -Compatibility -============= - - ------ --------------- - trunk Should work - 0.11 Works - 0.10 Should work - 0.9 Does not work - ------ --------------- - -Conversion to or from internal storage --------------------------------------- - -This module stores data in a way that overlaps with the more recent -archive support in `mod_storage_internal`, meaning e.g. [mod_migrate] -will not be able to cleanly convert to or from the `xmlarchive` format. - -To mitigate this, an migration command has been added to -`mod_storage_xmlarchive`: - -``` bash -prosodyctl mod_storage_xmlarchive convert $DIR internal $STORE $JID+ -``` - -Where `$DIR` is `to` or `from`, `$STORE` is e.g. `archive` or `archive2` -for MAM and `muc_log` for MUC logs. Finally, `$JID` is one or more JID -of the users or MUC rooms to be migrated. - -To migrate all users/rooms on a particular host, pass a bare hostname. - -::: {.alert .alert-danger} -Since this is a destructive command, don't forget to backup your data -first. - -Prosody should *not* be running while converting data. -::: - - -Data structure -============== - -Data is split in three kinds of files and messages are grouped by day. -Prosodys `util.datamanager` is used, so all special characters in these -filenames are escaped and reside under `hostname/store` in Prosodys Data -directory, commonly `/var/lib/prosody`. - -`username.list` -: A list of dates in `YYYY-MM-DD` format. - -`username@YYYY-MM-DD.list` -: Index containing metadata for messages stored on that day. - -`username@YYYY-MM-DD.xml` -: Messages in textual XML format, separated by newlines. - -This makes it fairly simple and fast to find messages by timestamp. -Queries that are not time based, but limited to a specific contact may -be expensive as potentially the entire archive will be read. - -Each archive ID is of the form `YYYY-MM-DD-random`, making lookups by -archive id just as simple as time based queries. - -## Limitations - -- Only XML stanzas can be stored. -- The deletion method only supports removing entire days at a time.
--- a/mod_strict_https/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ ---- -summary: HTTP Strict Transport Security ---- - -# Introduction - -This module implements [RFC 6797: HTTP Strict Transport Security] and -responds to all non-HTTPS requests with a `301 Moved Permanently` -redirect to the HTTPS equivalent of the path. - -# Configuration - -Add the module to the `modules_enabled` list and optionally configure -the specific header sent. - -``` lua -modules_enabled = { - ... - "strict_https"; -} -hsts_header = "max-age=31556952" -``` - -If the redirect from `http://` to `https://` causes trouble with -internal use of HTTP APIs it can be disabled: - -``` lua -hsts_redirect = false -``` - -# Compatibility - - ------- ------------- - trunk Should work - 0.12 Should work - 0.11 Should work - ------- -------------
--- a/mod_support_contact/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ ---- -labels: -- 'Stage-Stable' -summary: Add a support contact to new registrations -... - -Introduction -============ - -This Prosody plugin adds a default contact to newly registered accounts. - -Usage -===== - -Simply add "support\_contact" to your modules\_enabled list. When a new -account is created, the new roster would be initialized to include a -support contact. - -Configuration -============= - - ------------------------- -------------------------------------------------------------------------------------------------------------------------------- - support\_contact The bare JID of the support contact. The default is support@hostname, where hostname is the host the new user's account is on. - support\_contact\_nick Nickname of the support contact. The default is "Support". - support\_contact\_group The roster group in the support contact's roster in which to add the new user. - ------------------------- -------------------------------------------------------------------------------------------------------------------------------- - -Compatibility -============= - - ------ ------- - 0.10 Works - 0.9 Works - 0.8 Works - 0.7 Works - 0.6 Works - ------ ------- - -**For 0.8 and older** use [version -999a4b3e699b](http://hg.prosody.im/prosody-modules/file/999a4b3e699b/mod_support_contact/mod_support_contact.lua). - -Caveats/Todos/Bugs -================== - -- This only works for accounts created via in-band registration.
--- a/mod_support_room/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -# Introduction - -This module adds newly registered users as members to a specified MUC -room and sends them an invite. In a way, this is similar in purpose to -[mod_support_contact] and [mod_default_bookmarks]. - -# Example - - VirtualHost"example.com" - modules_enabled = { "support_room" } - support_room = "room@muc.example.com" - support_room_inviter = "support@example.com" - support_room_reason = "Invite new users to the support room" - - Component "muc.example.com" - -# Compatibility - -This module - - Version Works - --------- ------- - 0.11.x Yes - 0.10.x No
--- a/mod_swedishchef/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Silly little module to convert your conversations to "swedish"' -... - -Introduction -============ - -This module does some conversions on message bodys passed through it -causing them to look like our beloved swedish chef had typed them. - -Details -======= - -To load this on a MUC component do - - Component "funconference.example.com" "muc" - modules_enabled = { "swedishchef" } - swedishchef_trigger = "!chef"; -- optional, converts only when the message starts with "!chef" - -In theory this also works for whole servers, but that is untested and -not recommended ;) - -Compatibility -============= - - ----- ------- - 0.6 Works - 0.5 Works - ----- ------- - -Todo -==== - -- Possibly add xhtml-im (XEP-0071) support
--- a/mod_tcpproxy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'TCP-over-XMPP :)' -... - -Introduction -============ - -It happens occasionally that I would like to use the XMPP server as a -generic proxy for connecting to another service. It is especially -awkward in some environments, and impossible in (for example) Javascript -inside a web browser. - -Details -======= - -Using mod\_tcpproxy an XMPP client (including those using BOSH) can -initiate a pipe to a given TCP/IP address and port. This implementation -uses the [In-Band Bytestreams](http://xmpp.org/extensions/xep-0047.html) -XEP, simply extended with 2 new attributes in a new namespace, host and -port. - -An example Javascript client can be found in the web/ directory of -mod\_tcpproxy in the repository. - -Configuration -============= - -Just add tcpproxy as a component, for example: - -`Component "tcp.example.com" "tcpproxy"` - -Protocol -======== - -A new stream is opened like this: - -``` {.xml} -<iq type="set" id="newconn1" to="tcp.example.com"> - <open xmlns='http://jabber.org/protocol/ibb' - sid='connection1' - block-size='4096' - stanza='message' - xmlns:tcp='http://prosody.im/protocol/tcpproxy' - tcp:host='example.com' - tcp:port='80' /> -</iq> -``` - -The stanza attribute (currently) MUST be 'message', and block-size is -(currently) ignored. - -In response to this stanza you will receive a result upon connection -success, or an error if the connection failed. You can then send to the -connection by sending message stanzas as described in the IBB XEP. -Incoming data will likewise be delivered as messages. - -Compatibility -============= - - ----- -------------- - 0.7 Works - 0.6 Doesn't work - ----- -------------- - -Todo -==== - -- ACLs (restrict to certain JIDs, and/or certain target hosts/ports) -- Honour block-size -- Support iq stanzas for data transmission -- Signal to start SSL/TLS on a connection
--- a/mod_telnet_tlsinfo/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ ---- -labels: -- 'Stage-Obsolete' -summary: Telnet command for showing TLS info ---- - -Introduction -============ - -This module adds two commands to the telnet console, `c2s:showtls()` and -`s2s:showtls()`. These commands shows TLS parameters, such as ciphers -and key agreement protocols, of all c2s or s2s connections. - -Configuration -============= - -Just add the module to the `modules_enabled` list. There is no other -configuration. - - modules_enabled = { - ... - "telnet_tlsinfo"; - } - -Usage -===== - -Simply type `c2s:showtls()` to show client connections or -`s2s:showtls()` for server-to-server connections. These commands can -also take a JID for limiting output to matching users or servers. - - s2s:showtls("prosody.im") - | example.com -> prosody.im - | protocol: TLSv1.1 - | cipher: DHE-RSA-AES256-SHA - | encryption: AES(256) - | algbits: 256 - | bits: 256 - | authentication: RSA - | key: DH - | mac: SHA1 - | export: false - - Field Description - ---------------- ------------------------------------------------------------------------------------------------- - protocol The protocol used. **Note**: With older LuaSec, this is the protocol that added the used cipher - cipher The OpenSSL cipher string for the currently used cipher - encryption Encryption algorithm used - bits, algbits Secret bits involved in the cipher - authentication The authentication algorithm used - mac Message authentication algorithm used - key Key exchange mechanism used. - export Whethere an export cipher is used - -Compatibility -============= - - --------------------- ------------------ - 0.9 with LuaSec 0.5 Works - 0.10 Merged into core - --------------------- ------------------
--- a/mod_test_data/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ ---- -description: Generate test data -labels: -- 'Stage-Alpha' ---- - -This module is of use to developers who want to fill their Prosody storage with -test data, e.g. for debugging, testing or performance measurements of Prosody or -client code. - -Currently it only supports filling MAM archives, but patches are welcome for other -functionality.
--- a/mod_throttle_presence/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: Limit presence stanzas to save traffic -... - -Introduction -============ - -For most people 'presence' (status changes) of contacts make up most of -the traffic received by their client. However much of the time it is not -essential to have highly accurate presence information. - -This module automatically cuts down on presence traffic when clients -indicate they are inactive (using the [CSI protocol](mod_csi.html)). - -This is extremely valuable for mobile clients that wish to save battery -power while in the background. - -Configuration -============= - -Just load the module (e.g. in modules\_enabled). There are no -configuration options. - -Compatibility -============= - - ----- ------- - 0.9 Works - ----- -------
--- a/mod_throttle_unsolicited/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ ---- -rockspec: - dependencies: - - mod_track_muc_joins -summary: Limit rate of outgoing unsolicited messages ---- - -Introduction -============ - -This module limits the rate of outgoing unsolicited messages from local -clients. Optionally, unsolicited messages coming in from remote servers -may be limited per s2s conneciton. A message counts as "unsolicited" if -the receiving user hasn't added the sending user to their roster. - -The module depends on [mod\_track\_muc\_joins] in order to allow sent -messages to joined MUC rooms. - -Configuration -============= - -To set a limit on messages from local sessions: - -``` {.lua} -unsolicited_messages_per_minute = 10 -``` - -To enable limits on unsolicited messages from s2s connections: - -``` {.lua} -unsolicited_s2s_messages_per_minute = 100 -```
--- a/mod_tls_policy/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,45 +0,0 @@ ---- -summary: Cipher policy enforcement with application level error reporting -... - -# Introduction - -This module arose from discussions at the XMPP Summit about enforcing -better ciphers in TLS. It may seem attractive to disallow some insecure -ciphers or require forward secrecy, but doing this at the TLS level -would the user with an unhelpful "Encryption failed" message. This -module does this enforcing at the application level, allowing better -error messages. - -# Configuration - -First, download and add the module to `module_enabled`. Then you can -decide on what policy you want to have. - -Requiring ciphers with forward secrecy is the most simple to set up. - -``` lua -tls_policy = "FS" -- allow only ciphers that enable forward secrecy -``` - -A more complicated example: - -``` lua -tls_policy = { - c2s = { - encryption = "AES"; -- Require AES (or AESGCM) encryption - protocol = "TLSv1.2"; -- and TLSv1.2 - bits = 128; -- and at least 128 bits (FIXME: remember what this meant) - } - s2s = { - cipher = "AESGCM"; -- Require AESGCM ciphers - protocol = "TLSv1.[12]"; -- and TLSv1.1 or 1.2 - authentication = "RSA"; -- with RSA authentication - }; -} -``` - -# Compatibility - -Requires LuaSec 0.5 -
--- a/mod_tlsfail/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ ---- -summary: STARTTLS failure test ---- - -This module responds to `<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>` with -`<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>`, in order to test -how clients and server behave. - -See [RFC6120 Section 5.4.2.2](https://xmpp.org/rfcs/rfc6120.html#rfc.section.5.4.2.2) -
--- a/mod_traceback/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -This module writes out a traceback to `traceback.txt` in Prosodys data -directory (see `prosodyctl about`) when the signal `SIGUSR1` is -received. This is useful when debugging seemingly frozen instances in -case it is stuck in Lua code.
--- a/mod_track_muc_joins/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ ---- -summary: Keep track of joined chat rooms -... - -# Introduction - -This module attempts to keep track of what MUC chat rooms users have -joined. It's not very useful on its own, but can be used by other -modules to influence decisions. - -# Usage - -Rooms joined and the associated nickname is kept in a table field -`rooms_joined` on the users session. - -An example: - -``` lua -local jid_bare = require"util.jid".bare; - -module:hook("message/full", function (event) - local stanza = event.stanza; - local session = prosody.full_sessions[stanza.attr.to]; - if not session then - return -- No such session - end - - local joined_rooms = session.joined_rooms; - if not joined_rooms then - return -- This session hasn't joined any rooms at all - end - - -- joined_rooms is a map of room JID -> room nickname - local nickname = joined_rooms[jid_bare(stanza.attr.from)]; - if nickname then - session.log("info", "Got a MUC message from %s", stanza.attr.from); - - local body = stanza:get_child_text("body"); - if body and body:find(nickname, 1, true) then - session.log("info", "The message contains my nickname!"); - end - end -end); -``` - -# Known issues - -[XEP 45 § 7.2.3 Presence Broadcast][enter-pres] has the following text: - -> In particular, if roomnicks are locked down then the service MUST do -> one of the following. -> -> \[...\] -> -> If the user has connected using a MUC client (...), then the service -> MUST allow the client to enter the room, modify the nick in accordance -> with the lockdown policy, and **include a status code of "210"** in -> the presence broadcast that it sends to the new occupant. - -This case is not yet handled. - -[enter-pres]: http://xmpp.org/extensions/xep-0045.html#enter-pres
--- a/mod_turn_external/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ ---- -summary: Advertise an external TURN service -... - -This module advertises STUN/TURN service to clients via XEP-0215 (it utilizes -mod_external_services to do so, which will be automatically loaded by this -module and must be available). - -This module is included with Prosody 0.12 but made available here for older -versions. - -Documentation is available on the [Prosody site][doc:modules:mod_turn_external].
--- a/mod_turn_external/mod_turn_external.lua Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -local set = require "util.set"; - -local secret = module:get_option_string("turn_external_secret"); -local host = module:get_option_string("turn_external_host", module.host); -local user = module:get_option_string("turn_external_user"); -local port = module:get_option_number("turn_external_port", 3478); -local ttl = module:get_option_number("turn_external_ttl", 86400); -local tcp = module:get_option_boolean("turn_external_tcp", false); -local tls_port = module:get_option_number("turn_external_tls_port"); - -if not secret then error("mod_" .. module.name .. " requires that 'turn_external_secret' be set") end - -local services = set.new({ "stun-udp"; "turn-udp" }); -if tcp then - services:add("stun-tcp"); - services:add("turn-tcp"); -end -if tls_port then - services:add("turns-tcp"); -end - -module:depends "external_services"; - -for _, type in ipairs({ "stun"; "turn"; "turns" }) do - for _, transport in ipairs({"udp"; "tcp"}) do - if services:contains(type .. "-" .. transport) then - module:add_item("external_service", { - type = type; - transport = transport; - host = host; - port = type == "turns" and tls_port or port; - - username = type == "turn" and user or nil; - secret = type == "turn" and secret or nil; - ttl = type == "turn" and ttl or nil; - }) - end - end -end
--- a/mod_turncredentials/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -# Introduction - -[XEP-0215] implementation for [time-limited TURN -credentials](https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00). - -# Configuration - - Option Type Default - ------------------------- -------- ------------ - turncredentials\_secret string *required* - turncredentials\_host string *required* - turncredentials\_port number `3478` - turncredentials\_ttl number `86400` - -# Compatible TURN / STUN servers. - -- [coturn](https://github.com/coturn/coturn) - [setup guide][doc:coturn] -- [restund](http://www.creytiv.com/restund.html) -- [eturnal](https://eturnal.net/) - -# Compatibility - -Incompatible with [mod_extdisco](https://modules.prosody.im/mod_extdisco.html)
--- a/mod_twitter/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'Simple example of working component and HTTP polling.' -... - -Introduction -============ - -Twitter has simple API to use, so I tried to deal with it via Prosody. I -didn't manage to finish this module, but it is nice example of component -that accepts registrations, unregistrations, does HTTP polling and so -on. Maybe someone will finnish this idea. - -Details -======= - -It does require some non-prosody Lua libraries: LuaJSON - -Configuration -============= - -At the moment no configuration needed, but you can configure some -variables inside code. - -TODO -==== - -- Send latest tweets to XMPP user -- Reply user's messages to Twitter -- OAuth support -- User configuration (forms) -- discuss about using cjson -- [!!!!] rewrite to be compatible with 0.9+ -- drop? (since it is mod\_twitter in spectrum) - -Compatibility -============= - - ------- --------------------- - trunk Currently Not Works - 0.9 Currently Not Works - 0.8 Works - ------- ---------------------
--- a/mod_uptime_presence/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ ---- -labels: -summary: Report server uptime in presence -... - -Introduction -============ - -This module simply responds to a presence probe sent to the server with -a presence staza containing a timestamp from when the server started. - -This is imagined as an alternative to the XEP-0012 "Last Activity" -protocol that can seem a bit overloaded.
--- a/mod_vcard_muc/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ ---- -summary: Support for MUC vCards and avatars ---- - -Introduction -============ - -This module adds the ability to set vCard for MUC rooms. One of the most common use case is to be able to define an avatar for your own MUC room. - -Usage -===== - -Add "vcard\_muc" to your modules\_enabled list: - -``` {.lua} -Component "conference.example.org" "muc" -modules_enabled = { - "vcard_muc", -} -``` - -Compatibility -============= - - ----- ------------- - trunk Works - 0.10 Should work - 0.9 Should work - ----- ------------- - -
--- a/mod_vjud/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: 'XEP-0055: Jabber Search' -... - -Introduction -============ - -Basic implementation of [XEP-0055: Jabber Search]. - -Details -======= - -This module has two modes. One mode requires users to opt-in to be -searchable, then allows users to search the list of those users. The -second mode allows search across all users. - -Usage -===== - -First copy the module to the prosody plugins directory. - -Then add "vjud" to your modules\_enabled list: - - modules_enabled = { - -- ... - "vjud", - -- ... - } - -Alternatively, you can load it as a component: - - Component "search.example.com" "vjud" - -(Some old clients require this) - -Configuration -============= - - Option Default Description - ------------ ---------- -------------------------------- - vjud\_mode "opt-in" Choose how users are listed in the directory ("opt-in" or "all") - -Compatibility -============= - - ------- --------------------------------- - 0.8 Works, but only the opt-in mode - 0.9 Works - trunk Works - ------- --------------------------------- - -Note that the version for 0.8 and 0.9 are slightly different.
--- a/mod_warn_legacy_tls/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -TLS 1.0 and TLS 1.1 are about to be obsolete. This module warns clients -if they are using those versions, to prepare for disabling them. - -# Configuration - -``` {.lua} -modules_enabled = { - -- other modules etc - "warn_legacy_tls"; -} - --- This is the default, you can leave it out if you don't wish to --- customise or translate the message sent. --- '%s' will be replaced with the TLS version in use. -legacy_tls_warning = [[ -Your connection is encrypted using the %s protocol, which has been demonstrated to be insecure and will be disabled soon. Please upgrade your client. -]] -``` - -## Options - -`legacy_tls_warning` -: A string. The text of the message sent to clients that use outdated - TLS versions. Default as in the above example. - -`legacy_tls_versions` -: Set of TLS versions, defaults to - `{ "SSLv3", "TLSv1", "TLSv1.1" }`{.lua}, i.e. TLS \< 1.2.
--- a/mod_watch_spam_reports/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ ---- -labels: -- 'Stage-Beta' -rockspec: - dependencies: - - mod_spam_reporting -summary: 'Notify admins about incoming XEP-0377 spam reports' ---- - -This module sends a message to the server admins for incoming -[XEP-0377][1] spam reports. It depends on [mod\_spam\_reporting][2] -and doesn't require any configuration. - -Compatibility -============= - - ----- ----------- - trunk Works - 0.11 Works - ----- ----------- - - -[1]:https://xmpp.org/extensions/xep-0377.html -[2]:https://modules.prosody.im/mod_spam_reporting.html
--- a/mod_watchuntrusted/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,42 +0,0 @@ ---- -labels: -- 'Stage-Alpha' -summary: | - Warn admins about outgoing s2s connections that are refused due to - invalid or untrusted certificates -... - -Introduction -============ - -Similar to mod\_watchregistrations, this module warns admins when an s2s -connection fails due for encryption or trust reasons. - -The certificate shows the SHA1 hash, so it can easily be used together -with mod\_s2s\_auth\_fingerprint. - -Configuration -============= - - modules_enabled = { - -- other modules -- - "watchuntrusted", - - } - - untrusted_fail_watchers = { "admin@example.lit" } - untrusted_fail_notification = "Establishing a secure connection from $from_host to $to_host failed. Certificate hash: $sha1. $errors" - - Option Default Description - ------------------------------- --------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------- - untrusted\_fail\_watchers All admins The users to send the message to - untrusted\_fail\_notification "Establishing a secure connection from \$from\_host to \$to\_host failed. Certificate hash: \$sha1. \$errors" The message to send, \$from\_host, \$to\_host, \$sha1 and \$errors are replaced - untrusted\_message\_type `"chat"` Which kind of message to send. `"normal"` or `"headline"` are other sensible options - untrusted\_ignore\_domains Empty The domains that this module should not warn about - -Compatibility -============= - - ------- ------- - trunk Works - ------- -------
--- a/mod_welcome_page/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ ---- -labels: -- 'Stage-Beta' -summary: 'Serve a welcome page to users' -rockspec: - dependencies: - - mod_http_libjs - build: - copy_directories: - - html -... - -Introduction -============ - -This module serves a welcome page to users, and allows them to create an -account invite via the web on invite-only servers. - -The page template and policy of when to allow account creation are both -possible to override. - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- mod_invites -- mod_invites_adhoc -- mod_invites_page -- mod_invites_register -- mod_invites_register_web -- mod_register_apps - -For details and a full overview, start with the mod_invites documentation. - -Configuration -======= - -`welcome_page_template_path` -: The path to a directory containing the page templates and assets. See - the module source for the example template. - -`welcome_page_variables` -: Optional variables to pass to the template, available as `{var.name}` - -`welcome_page_open_registration` -: Whether to allow account creation in the absence of any other plugin - overriding the policy. Defaults to `false` unless `registration_invite_only` - is set to `false`.
--- a/mod_xhtmlim/README.markdown Mon Mar 17 23:42:11 2025 +0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -Introduction -============ - -This module attempts to sanitize XHTML-IM messages. - -It does **not** attempt to sanitize any CSS embedded in `style` -attributes, these are instead stripped by default. - -Configuration -============= - - Option Type Default - ------------------------ --------- --------- - `strip_xhtml_style` boolean `true` - `bounce_invalid_xhtml` boolean `false`